Flutter 基础组件
版本信息:2.2.2 版本较老
一、基础组件
1、文本
1)普通文本
Text(
'文本是视图系统中的常见控件,用来显示一段特定样式的字符串,就比如Android里的TextView,或是iOS中的UILabel。',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.red), //20号红色粗体展示
),
2)富文本
Text.rich(
TextSpan(children: <TextSpan>[
TextSpan(
text: '文本是视图系统中常见的控件,它用来显示一段特定样式的字符串,类似',
style: redStyle), //第1个片段,红色样式
TextSpan(text: 'Android', style: blackStyle), //第1个片段,黑色样式
TextSpan(text: '中的', style: redStyle), //第1个片段,红色样式
TextSpan(text: 'TextView', style: blackStyle) //第1个片段,黑色样式
]),
textAlign: TextAlign.center,
),
2、图片
1)本地图片
Image.asset(
'assets/images/back_circle.png', // 图片的本地地址
width: 100,
height: 100,
fit: BoxFit.cover, // 图片填充方式
),
2)网络图片
Image.network(
'https://upload.jianshu.io/users/upload_avatars/1694376/4b7e25bd43ee.jpg',
width: 200, // 设置图片宽度
height: 200, // 设置图片高度
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'), // 目标网络图片
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, //设置背景色为黄色
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(20.0)), //设置斜角矩形边框
colorBrightness: Brightness.light, //确保文字按钮为深色
onPressed: () => print('FlatButton pressed'),
height: 40,
minWidth: 50,
child: Row(
children: <Widget>[Icon(Icons.add), Text("Add")],
)),
二、列表
在 Android 中是由 ListView 或 RecyclerView 实现的,在 iOS 中是用 UITableView 实现的;而在 Flutter 中,实现这种需求的则是列表控件 ListView。
1)竖直滚动
ListView(
children: <Widget>[
ListTile(title: Text('第一项')),
ListTile(title: Text('第二项')),
ListTile(title: Text('第三项')),
//设置ListTile组件的标题与图标
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)水平滚动
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方法构造多个item
ListView.builder(
itemCount: 100, //元素个数
itemExtent: 50.0, //列表项高度
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为偶数,创建绿色分割线;index为奇数,则创建红色分割线
itemBuilder: (BuildContext context, int index) => ListTile(title: Text("title $index"), subtitle: Text("body $index"))//创建子Widget
)
5)自定义scrollView
CustomScrollView组件,ListView 实现了单一视图下可滚动 Widget 的交互模型,同时也包含了 UI 显示相关的控制逻辑和布局模型。但是,对于某些特殊交互场景,比如多个效果联动、嵌套滚动、精细滑动、视图跟随手势操作等,还需要嵌套多个 ListView 来实现。这时,各自视图的滚动和布局模型就是相互独立、分离的,就很难保证整个页面统一一致的滑动效果。那么,Flutter 是如何解决多 ListView 嵌套时,页面滑动效果不一致的问题的呢?在 Flutter 中有一个专门的控件 CustomScrollView,用来处理多个需要自定义滚动效果的 Widget。
在 CustomScrollView 中,这些彼此独立的、可滚动的 Widget 被统称为 Sliver。比如,ListView 的 Sliver 实现为 SliverList,AppBar 的 Sliver 实现为 SliverAppBar。这些 Sliver 不再维护各自的滚动状态,而是交由 CustomScrollView 统一管理,最终实现滑动效果的一致性。
接下来,我通过一个滚动视差的例子,与你演示 CustomScrollView 的使用方法。视差滚动是指让多层背景以不同的速度移动,在形成立体滚动效果的同时,还能保证良好的视觉体验。 作为移动应用交互设计的热点趋势,越来越多的移动应用使用了这项技术。
以一个有着封面头图的列表为例,我们希望封面头图和列表这两层视图的滚动联动起来,当用户滚动列表时,头图会根据用户的滚动手势,进行缩小和展开。经分析得出,要实现这样的需求,我们需要两个 Sliver:作为头图的 SliverAppBar,与作为列表的 SliverList。
具体的实现思路是:在创建 SliverAppBar 时,把 flexibleSpace 参数设置为悬浮头图背景。flexibleSpace 可以让背景图显示在 AppBar 下方,高度和 SliverAppBar 一样;而在创建 SliverList 时,通过 SliverChildBuilderDelegate 参数实现列表项元素的创建;最后,将它们一并交由 CustomScrollView 的 slivers 参数统一管理。
//头图header一起联动
CustomScrollView(slivers: <Widget>[
SliverAppBar(
//SliverAppBar作为头图控件
title: Text('CustomScrollView Demo'), //标题
floating: true, //设置悬浮样式
flexibleSpace: Image.network(
"https://upload.jianshu.io/users/upload_avatars/1694376/4b7e25bd43ee.jpg",
fit: BoxFit.cover), //设置悬浮头图背景
expandedHeight: 300, //头图控件高度
),
SliverList(
//SliverList作为列表控件
delegate: SliverChildBuilderDelegate(
(context, index) =>
ListTile(title: Text('Item #$index')), //列表项创建方法
childCount: 100, //列表元素个数
),
),
]
)
6)监听偏移量,用类ScrollController
class _PageScrollControllerState extends State<PageScrollController> with WidgetsBindingObserver {
late ScrollController _controller;//ListView 控制器
bool isToTop = false;// 标示目前是否需要启用 "Top" 按钮
@override
void initState() {
_controller = ScrollController();
_controller.addListener(() {// 为控制器注册滚动监听方法
if(_controller.offset > 1000) {// 如果 ListView 已经向下滚动了 1000,则启用 Top 按钮
setState(() {isToTop = true;});
} else if(_controller.offset < 300) {// 如果 ListView 向下滚动距离不足 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
); // 做一个滚动到顶部的动画
}
} : null), child: Text("Top"),),
),
Expanded(
child: ListView.builder(
controller: _controller, // 初始化传入控制器
itemCount: 100, // 列表元素总数
itemBuilder: (context, index) =>
ListTile(title: Text("Index : $index")), // 列表项构造方法
),
),
],
),
);
}
@override
void dispose() {
_controller.dispose(); // 销毁控制器
super.dispose();
}
}
7)监听开始滚动结束等,用widget NotificationListener
body: NotificationListener<ScrollNotification>(// 添加 NotificationListener 作为父容器
onNotification: (scrollNotification) {// 注册通知回调
if (scrollNotification is ScrollStartNotification) {// 滚动开始
print('Scroll Start');
} else if (scrollNotification is ScrollUpdateNotification) {// 滚动位置更新
print('Scroll Update');
} else if (scrollNotification is ScrollEndNotification) {// 滚动结束
print('Scroll End');
}
return true;
},
child: ListView.builder(
itemCount: 30,// 列表元素个数
itemBuilder: (context, index) => ListTile(title: Text("Index : $index")),// 列表项创建方法
),
)