Flutter在底层做了大量的渲染化工作、我们只需要通过组合、嵌套不同类型的Widget、就可以构建出任意功能、任意复杂度的界面。
Widget有StatelessWidget和StatefulWidget两种类型。StatefulWidget应对有交互、需要动态变化视觉效果的场景。而StatelessWidget用于处理静态的、无状态的视图展示。在构建界面过程中,会大量使用StatefulWidget来处理静态的视图展示需求。

UI编程方式

  • 如何调整一个控件(Widget)的展示样式,UI编程范式
  • 原生和JavaScript,视图开发是命令式的。需要精确的告诉操作系统或浏览器用何种方式去做事情。假如想要修改某个界面的某个文案,需要找到具体的文本控件并调用他的控件方法命令。才能完成文字变更
  • Flutter的视图开发时声明式的。其核心思想就是将视图和数据分离。
  • 设计好Widget布局方案之外、还需要提前维护一套文案数据集。并为需要变换的Widget绑定数据集中的数据。使Widget根据这个数据完成渲染
  • 需要变更界面文案时,只需要改变数据集中的文案数据。并通知Flutter框架触发Widget的重新渲染即可。开发者无需再精确关注UI编程中的各个过程细节。只需维护好数据集即可。
  • 命令式编程强调精确控制过程细节,而声明式编程强调通过意图输出结果整体。
  • 对应到Flutter中,意图是绑定了组件状态的State、结果是重新渲染后的组件。在Widget的生命周期内,应用到State中的任何更改都将强制Widget重新构建。
  • 对于组件完成创建后就无需变更的场景。状态的绑定是可选项。这里可选就区分出了Widget的两种类型。即StatelessWidget不带绑定状态。而StatefulWidget带绑定状态。
  • 当你所要构建的用户界面不随任何状态信息的变化而变化时,需要选择使用StatelessWidget,反之则选用StatefulWidget。前者一般用于静态内容的展示,而后者则用于存在交互反馈的内容呈现中。

    StatelessWidget

  • 在Flutter中,Widget采用由父到子、自顶向下的方式进行构建。父Widget控制着子Widget的显示样式,其样式配置由父Widget在构建时提供。

  • 用这种方式构建出的Widget,有些(Text、Container、Row、Column等)在创建时,除了这些配置参数之外不依赖与其他任何信息。一旦创建成功,不关心,也不响应任何数据变化进行重绘。这样的Widget被称为StatelessWidget(无状态组件)

image.png

  1. class Text extends StatelessWidget {
  2. // 构造方法及属性声明部分
  3. Const Text(this.data,{
  4. key key,
  5. this.textAlign,
  6. this.textDirection,
  7. // 其他参数
  8. ...
  9. }):assert(data != null),
  10. textSpan = null,
  11. super(key: key);
  12. final String data;
  13. final TextAlign textAlign;
  14. final TextDirection textDirection;
  15. //其他属性
  16. ...
  17. @override
  18. Widget build(BuildContext context) {
  19. ...
  20. Widget result = RichText(
  21. // 初始化配置
  22. ....
  23. )
  24. );
  25. ...
  26. return reslut;
  27. }
  28. }
  29. Widget是否能通过初始化参数完全控制其UI展示效果?如果能。就可以使用StatelessWidget来设计构造函数接口

StatefulWidget

与StatelessWidget相对应的。有些Widget(比如image、Checkbox)的展示,除了父Widget初始化时传入的静态配置之外、还需要处理用户的交互、(比如、用户点击按钮)或其他内部数据的变化,比如网络数据回包,并体现在UI上。

  • 这些Widget创建完成后,还需要关心和响应数据变化来进行重绘。这一类Widget被称为StatefulWidget(有状态组件)。
  • image.png
  • StatefulWidget是以State类代理Widget构建的设计方式实现的。以Image部分源码为例,说明StatefulWidget的构建过程。
  • Image类的构造函数会接收要被这个类使用的属性参数。Image类并没有build方法来创建视图,而是通过CreatState方法创建了一个类型为_ImageState的state对象。然后由这个对象负责视图的创建、
  • state对象持有并处理了Image类中的状态变化。从_imageInfo属性来说明
  • _imageInfo属性用来Widget加载真实的图片,一旦State对象通过_handleImageChanged方法监听到_imageInfo属性发生了变化。就会立即调用_ImageState类的setState方法通知Flutter框架:我这的数据变了。请使用更新后的_imageInfo数据重新加载图片。Flutter框架则会标记视图状态。更新UI ``` class Image extends StatefulWidget { //构造方法及属性声明部分 const Image({ key key, @required this.image, // 其他参数 }) : assert(image != null),

    1. super(key: key);

    final ImageProvider image; // 其他属性 …

    @override _ImageState createState() => _ImageState(); … }

class_ImageState extends StateWidget的State到底是什么? - 图3 { ImageInfo _imageInfo; // 其他属性 …

void _handleImageChanged(ImageInfo imageInfo, bool synchronousCall){ setState(() { _imageInfo = imageInfo; }); } …

@override Widget build(BuildContext context) { final RawImage image = RawImage( image:_imageInfo?.image, // 其他初始化配置 … ); return image; } … }

Image以一种动态的方式运行,监听变化、更新视图、与StatelessWidget通过父Widget完全控制UI展示不同。 StatefulWidget的父Widget仅定义了它初始化状态。而其自身视图运行的状态则需要自己处理。并根据处理情况 及时更新UI展示。 ```

StatefulWidget不是万金油、要慎用

  • SatefulWidget的滥用会直接影响Flutter应用的渲染性能。
  • 如果我们的根布局是一个StatefulWidget,再其State中每次调用一次更新UI、都将是一整个页面所有的Widget的销毁和重建、
  • 正确评估你的视图展示需求,避免无所谓的StatefulWidget使用。是提高Flutter应用渲染性能最简单也是最直接的手段。
  • 除了我们主动的通过State刷新UI之外,在一些特殊的场景下,Widget的bulid方法可能执行多次。因此,我们不应该在方法内部,放置太多耗时的操作。