Flutter 中常用的几个控件

465 阅读7分钟

从几个常用的控件,进入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,
);

图片

在开发中图片的来源大致分为:

  1. 本地资源文件
  2. 本地文件
  3. 网络请求 图片的构造方法还提供了填充模式 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。

image.png 可以看到:

  • FloatingActionButton: 一个圆形的悬浮按钮;
  • FlatButton: 扁平化的按钮,默认透明背景,被点击后会呈现灰色背景。
  • RaisedButton 凸起的按钮。 可以看到里面的两个参数:
  • onPressed: 点击事件,不写默认不可以点击;
  • child:里面可以放置想要的控件,上面放的是Text。

列表

除了文本、图片、按钮控件,最常用的还有 列表控件。

在Flutter 中提供的列表控件是:ListView。 先看看最简单的使用:

image.png 在 ListView 中直接添加 ListTile就完成显示了,当然我们可以通过 scrollDirection: Axis.horizontal把方向设置为水平。

但是很多时候,我们不需要一开始就创建出所有的item ,而是当需要显示的时候才显示出来,因为一次性创建这么多的item 会非常消耗性能。这时候,ListView 提供了另一个构造方法。ListView.builder

image.png

这样是和我们真是项目开发中的一样了,通过点击按钮,模拟网络请求,然后得到数据后,更新页面。

分割线:

在 列表中经常使用到分割线,在Flutter 中有两种方式:

  1. 把分割线放到item 视图中,成为视图的一部分;
  2. 通过构造方法,传入分割线。通过 命名构造函数 ListView.separated 第一种就不用过多介绍了,现在来看看第二种是如何实现的:

image.png 只需要添加 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, //列表元素个数
            ),
          ),
        ]),

QQ20210316-225809.gif

可以看到实现起来还是挺简单的,至少比使用Android 原生来实现简单多了。

ScrollControler

通过 ScrollControler 来监听滚动的回调。

  1. 首先,我们在 State 的初始化方法里,创建了 ScrollController,并通过 _controller.addListener 注册了滚动监听方法回调,
  2. 在视图构建方法 build 中,我们将 ScrollController 对象与 ListView 进行了关联,
  3. 最后,在 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();
  }
}

QQ20210318-220128.gif

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")), //列表项创建方法
      ),
    );
  }

总结

通过这篇文件,简单的介绍了在项目中常用 的几个控件,通过这些常用的控件可以完成一些简单的页面需求了。篇幅也比较多,谢谢您的阅读。