版本信息:2.2.2 版本較老
一、基礎(chǔ)組件
1、文本
1)普通文本
Text(
'文本是視圖系統(tǒng)中的常見控件,用來(lái)顯示一段特定樣式的字符串,就比如Android里的TextView,或是iOS中的UILabel。',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.red), //20號(hào)紅色粗體展示
),
2)富文本
Text.rich(
TextSpan(children: <TextSpan>[
TextSpan(
text: '文本是視圖系統(tǒng)中常見的控件,它用來(lái)顯示一段特定樣式的字符串,類似',
style: redStyle), //第1個(gè)片段,紅色樣式
TextSpan(text: 'Android', style: blackStyle), //第1個(gè)片段,黑色樣式
TextSpan(text: '中的', style: redStyle), //第1個(gè)片段,紅色樣式
TextSpan(text: 'TextView', style: blackStyle) //第1個(gè)片段,黑色樣式
]),
textAlign: TextAlign.center,
),
2、圖片
1)本地圖片
Image.asset(
'assets/images/back_circle.png', // 圖片的本地地址
width: 100,
height: 100,
fit: BoxFit.cover, // 圖片填充方式
),
2)網(wǎng)絡(luò)圖片
Image.network(
'https://upload.jianshu.io/users/upload_avatars/1694376/4b7e25bd43ee.jpg',
width: 200, // 設(shè)置圖片寬度
height: 200, // 設(shè)置圖片高度
fit: BoxFit.cover, // 圖片填充方式
),
FadeInImage(
placeholder: AssetImage('assets/images/loading.gif'), // 占位圖(本地資源)
image: NetworkImage('https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/logo-doubao-overflow.png'), // 目標(biāo)網(wǎng)絡(luò)圖片
width: 200,
height: 200,
fit: BoxFit.cover,
),
3、按鈕
//按鈕
FloatingActionButton(
onPressed: () => print('FloatingActionButton pressed'),
child: Text('Btn'),
),
FlatButton(
onPressed: () => print('FlatButton pressed'),
child: Text('Btn'),
),
RaisedButton(
onPressed: () => print('RaisedButton pressed'),
child: Text('Btn'),
),
FlatButton(
color: Colors.yellow, //設(shè)置背景色為黃色
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(20.0)), //設(shè)置斜角矩形邊框
colorBrightness: Brightness.light, //確保文字按鈕為深色
onPressed: () => print('FlatButton pressed'),
height: 40,
minWidth: 50,
child: Row(
children: <Widget>[Icon(Icons.add), Text("Add")],
)),
二、列表
在 Android 中是由 ListView 或 RecyclerView 實(shí)現(xiàn)的,在 iOS 中是用 UITableView 實(shí)現(xiàn)的;而在 Flutter 中,實(shí)現(xiàn)這種需求的則是列表控件 ListView。
1)豎直滾動(dòng)
ListView(
children: <Widget>[
ListTile(title: Text('第一項(xiàng)')),
ListTile(title: Text('第二項(xiàng)')),
ListTile(title: Text('第三項(xiàng)')),
//設(shè)置ListTile組件的標(biāo)題與圖標(biāo)
ListTile(leading: Icon(Icons.map), title: Text('Map')),
ListTile(leading: Icon(Icons.mail), title: Text('Mail')),
ListTile(leading: Icon(Icons.message), title: Text('Message')),
]
),
2)水平滾動(dòng)
ListView(
scrollDirection: Axis.horizontal,
//item延展尺寸(寬度)
itemExtent: 140,
children: [
Container(color: Colors.black),
Container(color: Colors.red),
Container(color: Colors.blue),
Container(color: Colors.green),
Container(color: Colors.yellow),
Container(color: Colors.orange),
]),
3)build方法構(gòu)造多個(gè)item
ListView.builder(
itemCount: 100, //元素個(gè)數(shù)
itemExtent: 50.0, //列表項(xiàng)高度
itemBuilder: (BuildContext context, int index) => ListTile(
title: Text("title $index"), subtitle: Text("body $index")))
4)分割線類型
ListView.separated(
itemCount: 100,
separatorBuilder: (BuildContext context, int index) => index %2 ==0? Divider(color: Colors.green) : Divider(color: Colors.red),//index為偶數(shù),創(chuàng)建綠色分割線;index為奇數(shù),則創(chuàng)建紅色分割線
itemBuilder: (BuildContext context, int index) => ListTile(title: Text("title $index"), subtitle: Text("body $index"))//創(chuàng)建子Widget
)
5)自定義scrollView
CustomScrollView組件,ListView 實(shí)現(xiàn)了單一視圖下可滾動(dòng) Widget 的交互模型,同時(shí)也包含了 UI 顯示相關(guān)的控制邏輯和布局模型。但是,對(duì)于某些特殊交互場(chǎng)景,比如多個(gè)效果聯(lián)動(dòng)、嵌套滾動(dòng)、精細(xì)滑動(dòng)、視圖跟隨手勢(shì)操作等,還需要嵌套多個(gè) ListView 來(lái)實(shí)現(xiàn)。這時(shí),各自視圖的滾動(dòng)和布局模型就是相互獨(dú)立、分離的,就很難保證整個(gè)頁(yè)面統(tǒng)一一致的滑動(dòng)效果。那么,F(xiàn)lutter 是如何解決多 ListView 嵌套時(shí),頁(yè)面滑動(dòng)效果不一致的問(wèn)題的呢?在 Flutter 中有一個(gè)專門的控件 CustomScrollView,用來(lái)處理多個(gè)需要自定義滾動(dòng)效果的 Widget。
在 CustomScrollView 中,這些彼此獨(dú)立的、可滾動(dòng)的 Widget 被統(tǒng)稱為 Sliver。比如,ListView 的 Sliver 實(shí)現(xiàn)為 SliverList,AppBar 的 Sliver 實(shí)現(xiàn)為 SliverAppBar。這些 Sliver 不再維護(hù)各自的滾動(dòng)狀態(tài),而是交由 CustomScrollView 統(tǒng)一管理,最終實(shí)現(xiàn)滑動(dòng)效果的一致性。
接下來(lái),我通過(guò)一個(gè)滾動(dòng)視差的例子,與你演示 CustomScrollView 的使用方法。視差滾動(dòng)是指讓多層背景以不同的速度移動(dòng),在形成立體滾動(dòng)效果的同時(shí),還能保證良好的視覺體驗(yàn)。 作為移動(dòng)應(yīng)用交互設(shè)計(jì)的熱點(diǎn)趨勢(shì),越來(lái)越多的移動(dòng)應(yīng)用使用了這項(xiàng)技術(shù)。
以一個(gè)有著封面頭圖的列表為例,我們希望封面頭圖和列表這兩層視圖的滾動(dòng)聯(lián)動(dòng)起來(lái),當(dāng)用戶滾動(dòng)列表時(shí),頭圖會(huì)根據(jù)用戶的滾動(dòng)手勢(shì),進(jìn)行縮小和展開。經(jīng)分析得出,要實(shí)現(xiàn)這樣的需求,我們需要兩個(gè) Sliver:作為頭圖的 SliverAppBar,與作為列表的 SliverList。
具體的實(shí)現(xiàn)思路是:在創(chuàng)建 SliverAppBar 時(shí),把 flexibleSpace 參數(shù)設(shè)置為懸浮頭圖背景。flexibleSpace 可以讓背景圖顯示在 AppBar 下方,高度和 SliverAppBar 一樣;而在創(chuàng)建 SliverList 時(shí),通過(guò) SliverChildBuilderDelegate 參數(shù)實(shí)現(xiàn)列表項(xiàng)元素的創(chuàng)建;最后,將它們一并交由 CustomScrollView 的 slivers 參數(shù)統(tǒng)一管理。
//頭圖header一起聯(lián)動(dòng)
CustomScrollView(slivers: <Widget>[
SliverAppBar(
//SliverAppBar作為頭圖控件
title: Text('CustomScrollView Demo'), //標(biāo)題
floating: true, //設(shè)置懸浮樣式
flexibleSpace: Image.network(
"https://upload.jianshu.io/users/upload_avatars/1694376/4b7e25bd43ee.jpg",
fit: BoxFit.cover), //設(shè)置懸浮頭圖背景
expandedHeight: 300, //頭圖控件高度
),
SliverList(
//SliverList作為列表控件
delegate: SliverChildBuilderDelegate(
(context, index) =>
ListTile(title: Text('Item #$index')), //列表項(xiàng)創(chuàng)建方法
childCount: 100, //列表元素個(gè)數(shù)
),
),
]
)
6)監(jiān)聽偏移量,用類ScrollController
class _PageScrollControllerState extends State<PageScrollController> with WidgetsBindingObserver {
late ScrollController _controller;//ListView 控制器
bool isToTop = false;// 標(biāo)示目前是否需要啟用 "Top" 按鈕
@override
void initState() {
_controller = ScrollController();
_controller.addListener(() {// 為控制器注冊(cè)滾動(dòng)監(jiān)聽方法
if(_controller.offset > 1000) {// 如果 ListView 已經(jīng)向下滾動(dòng)了 1000,則啟用 Top 按鈕
setState(() {isToTop = true;});
} else if(_controller.offset < 300) {// 如果 ListView 向下滾動(dòng)距離不足 300,則禁用 Top 按鈕
setState(() {isToTop = false;});
}
});
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Scroll Controller Widget")),
body: Column(
children: <Widget>[
Container(
height: 40.0,
child: RaisedButton(onPressed: (isToTop ? () {
if (isToTop) {
_controller.animateTo(.0,
duration: Duration(milliseconds: 200),
curve: Curves.ease
); // 做一個(gè)滾動(dòng)到頂部的動(dòng)畫
}
} : null), child: Text("Top"),),
),
Expanded(
child: ListView.builder(
controller: _controller, // 初始化傳入控制器
itemCount: 100, // 列表元素總數(shù)
itemBuilder: (context, index) =>
ListTile(title: Text("Index : $index")), // 列表項(xiàng)構(gòu)造方法
),
),
],
),
);
}
@override
void dispose() {
_controller.dispose(); // 銷毀控制器
super.dispose();
}
}
7)監(jiān)聽開始滾動(dòng)結(jié)束等,用widget NotificationListener
body: NotificationListener<ScrollNotification>(// 添加 NotificationListener 作為父容器
onNotification: (scrollNotification) {// 注冊(cè)通知回調(diào)
if (scrollNotification is ScrollStartNotification) {// 滾動(dòng)開始
print('Scroll Start');
} else if (scrollNotification is ScrollUpdateNotification) {// 滾動(dòng)位置更新
print('Scroll Update');
} else if (scrollNotification is ScrollEndNotification) {// 滾動(dòng)結(jié)束
print('Scroll End');
}
return true;
},
child: ListView.builder(
itemCount: 30,// 列表元素個(gè)數(shù)
itemBuilder: (context, index) => ListTile(title: Text("Index : $index")),// 列表項(xiàng)創(chuàng)建方法
),
)