从几个常用的控件,进入Flutter 开发
通过前面几篇文章的学习,了解很多关于 Flutter 框架视图渲染的基础知识和原理,可能有点吃力,认为那些知识点可能在开发中都用不到。其实不然,像视图数据流转机制、底层渲染方案、视图更新策略等知识,都是构成一个 UI 框架的根本,看似枯燥,却往往具有最长久的生命力。新框架每年层出不穷,可是扒下那层炫酷的“外衣”,里面其实还是那些最基础的知识和原理。
只有把这些最基础的知识弄明白了,修炼好了内功,才能触类旁通,由点及面形成自己的知识体系,也能够在框架之上思考应用层构建视图实现的合理性。
在了解了基本原理之后,进一步学习开发就很容易上手了,通过这篇文章,介绍几个常用的UI控件,可以完成简单的页面开发了,提高自己的成就感。
在 APP 中我们经常看到的控件有,文本控件、图片控件、按钮控件、列表控件,通过这些控件的组合,可以开发出丰富多彩的页面,下面就一起来学习吧。
文本控件
在 Flutter 中,文本展示是通过 Text 控件实现的。
Text 支持两种类型的文本展示,一个是默认的展示单一样式的文本 Text,另一个是支持多种混合样式的富文本 Text.rich。
通过之前的学习知道 Text 是 StatelessWidget,是无状态的,只要在构造的时候,传入 样式配置就可以了,而这些配置可以分为两类(应该说Widget 的配置信息都由这两类组成,当然还有点击、触摸事件的处理等):
- 控制整体文本布局的参数,如文本对齐方式 textAlign、文本排版方向 textDirection,文本显示最大行数 maxLines、文本截断规则 overflow 等等,这些都是构造函数中的参数;
- 控制文本展示样式的参数,如字体名称 fontFamily、字体大小 fontSize、文本颜色 color、文本阴影 shadows 等等,这些参数被统一封装到了构造函数中的参数 style 中。
看看单一文本的显示
Text(
'You have pushed the button this many times:',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red),//20号红色粗体展示);
textAlign: TextAlign.center,
)
看起来很简单: 参数1:文本内容; 参数2:文本样式 参数3:文本的对齐样式
可能刚开始接触的时候,不知道构造参数里面不知道可以传入什么,这时候就可以使用到官方文档 可以清楚的知道里面有哪些属性和方法,快速了解控件的使用。
富文本的展示:在开发中,有时候我们也需要使用到富文本,在Flutter 中富文本就是将 一个string 分成几个部分,给每一个部分设置样式:
TextStyle blackStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: 20, color: Colors.black); //黑色样式
TextStyle redStyle = TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red); //红色样式
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,
);
图片
在开发中图片的来源大致分为:
- 本地资源文件
- 本地文件
- 网络请求 图片的构造方法还提供了填充模式 fit、拉伸模式 centerSlice、重复模式 repeat 等属性,可以针对图片与目标区域的宽高比差异制定排版模式。
同样打开 Image官网文档,从中可以了解到一个控件的使用。
当需要使用到占位图功能的时候就使用到 FadeInImage 控件,FadeInImage 控件提供了图片占位的功能,并且支持在图片加载完成时淡入淡出的视觉效果。此外,由于 Image 支持 gif 格式,我们甚至还可以将一些炫酷的加载动画作为占位图。
FadeInImage.assetNetwork(
placeholder: 'assets/loading.gif', //gif占位
image: 'https://xxx/xxx/xxx.jpg',
fit: BoxFit.cover, //图片拉伸模式
width: 200,
height: 200,
)
CachedNetworkImage:支持缓存到文件系统。由于 Image 控件处理缓存的时候,只会缓存到内存中,无法缓存到文件系统中,CachedNetworkImage的出现就解决了这个问题。
CachedNetworkImage(
imageUrl: "http://xxx/xxx/jpg",
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
按钮
Flutter 提供了三个基本的按钮控件,即 FloatingActionButton、FlatButton 和 RaisedButton。
可以看到:
- FloatingActionButton: 一个圆形的悬浮按钮;
- FlatButton: 扁平化的按钮,默认透明背景,被点击后会呈现灰色背景。
- RaisedButton 凸起的按钮。 可以看到里面的两个参数:
- onPressed: 点击事件,不写默认不可以点击;
- child:里面可以放置想要的控件,上面放的是Text。
列表
除了文本、图片、按钮控件,最常用的还有 列表控件。
在Flutter 中提供的列表控件是:ListView。 先看看最简单的使用:
在 ListView 中直接添加 ListTile就完成显示了,当然我们可以通过 scrollDirection: Axis.horizontal
把方向设置为水平。
但是很多时候,我们不需要一开始就创建出所有的item ,而是当需要显示的时候才显示出来,因为一次性创建这么多的item 会非常消耗性能。这时候,ListView 提供了另一个构造方法。ListView.builder
这样是和我们真是项目开发中的一样了,通过点击按钮,模拟网络请求,然后得到数据后,更新页面。
分割线:
在 列表中经常使用到分割线,在Flutter 中有两种方式:
- 把分割线放到item 视图中,成为视图的一部分;
- 通过构造方法,传入分割线。通过 命名构造函数 ListView.separated 第一种就不用过多介绍了,现在来看看第二种是如何实现的:
只需要添加 separatorBuilder
参数即可完成。
CustomScrollView
在开发中常见的还有 多个滚动视图的嵌套,在Flutter 中给我们提供了 CustomScrollView。用来处理多个需要自定义滚动效果的 Widget。在 CustomScrollView 中,这些彼此独立的、可滚动的 Widget 被统称为 Sliver。
看看一个视差效果:让多层背景以不同的速度移动,在形成立体滚动效果的同时,还能保证良好的视觉体验。 以一个有着封面头图的列表为例,我们希望封面头图和列表这两层视图的滚动联动起来,当用户滚动列表时,头图会根据用户的滚动手势,进行缩小和展开。
具体的实现思路是:
- 在创建 SliverAppBar 时,把 flexibleSpace 参数设置为悬浮头图背景。flexibleSpace 可以让背景图显示在 AppBar 下方,高度和 SliverAppBar 一样;
- 而在创建 SliverList 时,通过 SliverChildBuilderDelegate 参数实现列表项元素的创建;
- 最后,将它们一并交由 CustomScrollView 的 slivers 参数统一管理。
CustomScrollView(slivers: [
SliverAppBar(
//SliverAppBar作为头图控件
floating: true,
//设置悬浮样式
flexibleSpace: Image.network("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1301814620,1650274350&fm=26&gp=0.jpg", fit: BoxFit.cover),
//设置悬浮头图背景
expandedHeight: 300, //头图控件高度
),
SliverList(
//SliverList作为列表控件
delegate: SliverChildBuilderDelegate(
(context, index) =>
ListTile(title: Text('Item #$index')), //列表项创建方法
childCount: 100, //列表元素个数
),
),
]),
可以看到实现起来还是挺简单的,至少比使用Android 原生来实现简单多了。
ScrollControler
通过 ScrollControler 来监听滚动的回调。
- 首先,我们在 State 的初始化方法里,创建了 ScrollController,并通过 _controller.addListener 注册了滚动监听方法回调,
- 在视图构建方法 build 中,我们将 ScrollController 对象与 ListView 进行了关联,
- 最后,在 State 的销毁方法中,我们对 ScrollController 进行了资源释放。
来一个简单的例子看看:
class ListViewDemo5 extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ScrollControllerState();
}
}
class _ScrollControllerState extends State<ListViewDemo5> {
ScrollController _controller; //ListView 控制器
bool isToTop = false; // 标示目前是否需要启用 "Top" 按钮
@override
void initState() {
_controller = ScrollController();
_controller.addListener(() {
// 为控制器注册滚动监听方法
if (_controller.offset > 500) {
// 如果 ListView 已经向下滚动了 500,则启用 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();
}
}
ScrollNotification
通过ScrollNotification,从而感知 ListView 的各类滚动事件。如,开始滚动、滚动中、滚动结束。 使用起来也很简单,因为 NotificationListener 是一个 Widget,为了监听滚动类型的事件,我们需要将 NotificationListener 添加为 ListView 的父容器从而捕获 ListView 中的通知。
Widget build(BuildContext context) {
return 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: 45,
itemBuilder: (context, index) =>
ListTile(title: Text("Index : $index")), //列表项创建方法
),
);
}
总结
通过这篇文件,简单的介绍了在项目中常用 的几个控件,通过这些常用的控件可以完成一些简单的页面需求了。篇幅也比较多,谢谢您的阅读。