Flutter学习记录 - Widget、State、Context

前言

如何学习使用一门工具进行开发?

对于这个问题,笔者认为应该从概念开始,了解概念后才更好去理解其原理,而只有在理解原理之后你才敢说会使用了。
类似Jetpack Compose,如果不理解State、可重组函数、重组作用域这些概念,又从何挖其原理呢?又如何去考虑避免或减少重组触发写出准确的代码呢?
笔者个人认为会使用,并不只是能够运用其出预期结果,而是会根据自己的理解使用正确/准确的方式实现预期结果。

Flutter是一个跨平台UI构架工具,其中的 WidgetStateContext 是每一个Flutter开发者都需要充分理解的重要概念之一,特别是对于初学者。
笔者作为一个Flutter初学者,本文将尝试解释这些概念,加深理解。

Widget

Widget的中文意思是小部件,用户所看到的界面基本都是Widget,当然也可能是Platform View(平台视图)。
有这么一句话:在flutter中,一切都是Widget。其实在flutter中,每个widget专注的功能很小,比如padding/alignment,在其他语言中它是一个修饰的属性,但是在flutter中,它却是一个widget。这样做的好处就是我们开发者可以通过这些基础的widgets任意组合,从而构建出复杂,个性化的大widget。但是这样的缺点也很明显,就是套层。跟套娃一样,组合多了,层级也更多,代码看起来就不那么舒服了。
以下是常见Widget Container中的build方法

@override
Widget build(BuildContext context) {
  Widget? current = child;

  if (child == null && (constraints == null || !constraints!.isTight)) {
    current = LimitedBox(
      maxWidth: 0.0,
      maxHeight: 0.0,
      child: ConstrainedBox(constraints: const BoxConstraints.expand()),
    );
  } else if (alignment != null) {
    current = Align(alignment: alignment!, child: current);
  }

  final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
  if (effectivePadding != null) {
    current = Padding(padding: effectivePadding, child: current);
  }

  if (color != null) {
    current = ColoredBox(color: color!, child: current);
  }

  if (clipBehavior != Clip.none) {
    assert(decoration != null);
    current = ClipPath(
      clipper: _DecorationClipper(
        textDirection: Directionality.maybeOf(context),
        decoration: decoration!,
      ),
      clipBehavior: clipBehavior,
      child: current,
    );
  }

  if (decoration != null) {
    current = DecoratedBox(decoration: decoration!, child: current);
  }

  if (foregroundDecoration != null) {
    current = DecoratedBox(
      decoration: foregroundDecoration!,
      position: DecorationPosition.foreground,
      child: current,
    );
  }

  if (constraints != null) {
    current = ConstrainedBox(constraints: constraints!, child: current);
  }

  if (margin != null) {
    current = Padding(padding: margin!, child: current);
  }

  if (transform != null) {
    current = Transform(transform: transform!, alignment: transformAlignment, child: current);
  }

  return current!;
}

通过代码可以看到,current这个widget通过一步一步根据使用传递的参数进行判断从而嵌入到不同的widget组合而成。

StatelessWidget & StatefulWidget

根据Widget是否包含状态,Widget又分为StatelessWidgetStatefulWidget两种Widget。
StatelessWidget无状态小部件,是不会随着绑定的值的改变而动态改变的。所以StatelessWidget的生命周期很简单: 初始化 –> 通过build方法渲染。
StatefulWidget有状态小部件,和StatelessWidget刚好相反,它需要绑定一个状态,且build方法在State中。当这个状态在改变的时候,将强制重建Widget。
StatefulWidget的生命周期如下:

stateDiagram
[*] --> createState
createState --> initState
initState --> didChangeDependencies
didChangeDependencies --> build
build --> addPostFrameCallback
addPostFrameCallback --> didUpdateWidget
didUpdateWidget --> deactivate
deactivate --> dispose
dispose --> [*]

State

前面我们了解了Widget,在flutter中,Widget的构建是基于State的,所以State是数据,是用来渲染widget的数据。
在flutter应用中,State又分为Ephemeral StateApp State

Ephemeral State

Ephemeral State是局部状态,一般作用于某个或某些Widget,在开发的过程中,我们可以针对某个页面/页面部分内容进行整理和定义Ephemeral State,而不需要使用状态管理技术来处理这些状态,使用setState()方法实现目的即可。

App State

App State是全局状态,它作用于整个App范围。相对于Ephemeral State,App State处理情况会更复杂点,它可以运用于整个app中任意时间的任意界面,而不是对某个或某些特定的Widget来管理。所以这里我们需要用到状态管理技术。
了解更多: List of state management approaches

Context

这里所谓的Context实际指的是BuildContext,每个Widget都有一个build方法,这个方法就有一个参数便是BuildContext。上文提到过,flutter的Widget专注于更小的功能,即使一个简单的Container也是由多个Widget组合而成,那么其组合之间的关系是通过什么管理的呢? 任何的Widget都可重用到其他地方,所以它在Widget组合树中,Widget不会保留任何信息。在Widget抽象类中有这么个方法createElement,其注释Inflates this configuration to a concrete instance. 翻译过来就是将配置扩展为具体实例。
另外在flutter源码中,BuildContext上也有这么一句注释

[BuildContext] objects are actually [Element] objects. The [BuildContext] interface is used to discourage direct manipulation of [Element] objects.

所以Widget和Context之间的关系是一对一的,而维护Widget树的关联信息在Element中,而Context则是为了避免直接操作Element。

总结

  1. Widget是flutter UI的基本,分为StatelessWidgetStatefulWidget,如果一个Widget在其生命周期中考虑变更且变更后将强制重建,这时候使用StatefulWidget,否则使用StatelessWidget。
  2. State是Widget的数据信息,分为Ephemeral StateApp StateEphemeral State多数用于StatefulWidget并在其内部管理,用setState方法实现更新。App State一般都通过状态管理技术进行管理。
  3. BuildContext和Widget存在一对一的关系,每个Widget都有一个Context。Context实际上是Element object,在构建一个widget时,会调用widget的createElement方法创建Element,而element对象中又存在parent,children,size等信息,从而构成element tree。