Flutter圈子Flutter

自定义Flutter控件

2018-01-31  本文已影响485人  何小有

在Flutter开发中,我们会经常和各种控件打交道,它们也能满足业务的大部分需求。但是,我们往往需要将多个控件组合起来,才能实现业务的需求,而且这样写出来的代码维护起来非常困难。因此,我们可以把那些需要多个控件组合才能实现的功能自定义化,成为一个自定义控件,易于维护。

网络图片

无状态控件

Flutter框架给我们提供了StatelessWidget和StatefulWidget两个抽象类,用于自定义控件,首先我们看一下StatelessWidget抽象类。它可以定义一个不需要可变状态的控件,我们可以称其为“无状态控件”,它通过构建一系列其他控件来描述用户界面的一部分,构建过程以递归方式执行,直到用户界面的描述完全具体化。

比如,下面是一个名为“GreenBoard”的StatelessWidget子类的框架,它的里面包括一个Container控件,中文名称是“容器”,然后设置color属性,也就是背景色为绿色。

class GreenBoard extends StatelessWidget {
  const GreenBoard({ Key key }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: const Color(0xFF2DBD3A)
    );
  }
}

上面代码中,应用了《Dart入门—类与方法》的知识,大家可以去简单了解下。另外,我们覆盖了build这个抽象方法,该方法用于描述由此控件所实现的那一部分用户界面。其中,抽象类BuildContext是该控件在控件树中的位置句柄。

通常情况下,控件的构造函数参数不止一个,每个参数对应一个final修饰的属性,修改上一个例子,使其成为一个可以设置背景颜色和子控件的通用控件。

class Board extends StatelessWidget {
  const Board({
    Key key,
    this.color: const Color(0xFF2DBD3A),
    this.child,
  }) : super(key: key);

  final Color color;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: color, 
      child: child,
    );
  }
}

按照Flutter框架的语法规范,控件的构造函数只能使用命名参数,命名参数可以使用@required注解为必需参数。另外语法规范还规定了,第一个参数是key,最后一个参数是childchildren或其他类似参数。

如果一个无状态控件的父控件会定期更改控件的配置,或者它依赖于频繁更改的继承控件,那么优化build方法的性能,以保持流畅的渲染性能是非常重要的。有以下做法可以用于减少重新构建无状态控件的影响:

有状态控件

在了解完如何使用StatelessWidget定义一个无状态控件后,我们学习如何使用StatefulWidget定义一个具有可变状态的控件,我们可以称其为“有状态控件”。首先要搞清楚的是,状态是什么?状态是在构建控件时可以同步读取的信息,并且在控件的生命周期内可以改变,控件的使用者应该在状态发生变化时使用State.setState方法及时通知框架。

StatefulWidget的实例本身是不可变的,我们需要将其可变状态存储在由createState方法创建的单独的State对象中,或者存储在该State所订阅的对象中。例如,我们将之前名为“GreenBoard”的StatelessWidget子类改造成StatefulWidget的子类框架。

class GreenBoard extends StatefulWidget {
  const GreenBoard({ Key key }) : super(key: key);

  @override
  _GreenBoardState createState() => new _GreenBoardState();
}

class _GreenBoardState extends State<GreenBoard> {
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: const Color(0xFF2DBD3A)
    );
  }
}

只要我们调用了一个StatefulWidget,框架就会调用createState,这意味着,在控件树中,可能有多个不同位置的State对象与同一个StatefulWidget关联。同样的,如果一个StatefulWidget被我们从树中移除,并且再次被我们插入到树中,框架将再次调用createState来创建一个新的State对象,简化了State对象的生命周期。

我们再将之前名为“Board”的StatelessWidget子类改造成StatefulWidget的子类框架。除了可以设置背景颜色和子控件,还有一个可以被调用的,用来改变它的内部状态的方法。

class Board extends StatefulWidget {
  const Board({
    Key key,
    this.color: const Color(0xFF2DBD3A),
    this.child,
  }) : super(key: key);

  final Color color;
  final Widget child;

  _BoardState createState() => new _BoardState();
}

class _BoardState extends State<Board> {
  double _size = 1.0;

  void grow() {
    setState(() { _size += 0.1; });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: widget.color,
      transform: new Matrix4.diagonal3Values(_size, _size, 1.0),
      child: widget.child,
    );
  }
}

StatefulWidget有两种不同的类型:

有以下做法可以用于减少重新构建有状态控件的影响:

关于有状态与无状态的选择

如果自定义的控件可以与用户进行交互,比如通过键盘输入内容、通过滑动屏幕移动滑块、点击时改变状态,又或者是随着时间的推移而变化,比如数据Feed会更新状态。这时我们应该选择使用StatefulWidget创建一个有状态控件。

如果自定义的控件仅依赖于对象本身的配置信息,仅仅是用于展示给定的信息。那我们应该选择使用StatelessWidget创建一个无状态控件。

上一篇下一篇

猜你喜欢

热点阅读