学习flutter 2.flutter基础

11 阅读3分钟

概述

flutter基于Widget树渲染内容 代码形如

// 注意 Widget并不绑定state. widget总是会销毁重建 state则可能复用
// 因此属性是final 并且有一个创建state的函数
// 可以使用自身的属性创建state以设置其初始值
class A extends StatefulWidget {
  // 样板代码 使用key控制state销毁重建
  const A({super.key, required this.a});
  final String a;

  // 继承StatefulWidget并覆盖createState
  @override
  State<A> createState() => _AState();
}

// state包含了实际的Widget
class _AState extends State<A> {
  int num = 0;

  void increment() {
   // 继承自State 调用后会重新渲染 即使这个函数什么都不做
    setState(() {
      num++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      // widget来自State<A> 表明它从属的widget 它也具有属性a 
      Text('a: $widget.a'),
      Text('$num'),
      // 注意 这里的Text实例在是编译时确定不变的 使用const进行优化
      OutlinedButton(onPressed: increment, child: const Text('+1'))
    ]);
  }
}

flutter渲染分三级 Widget->Element->RenderObject.按react的概念类比理解,Widget=组件,Element=fiber,state则是允许自定义的fiber内容

BuildContext

build函数的参数/State类的context属性 它是Widget树中的位置标识 本质是Widget所对应的 Element 实例

它可以用于查找沿Widget树向上查找特定类型的Widget

  • Theme.of(context)
  • Navigator.of(context)

和react的useContext作用相同
需要注意的是 需要等Element创建之后才能使用

Builder

    Scaffold(
        body: ElevatedButton(
      onPressed: () {
        ScaffoldMessenger.of(context).showSnackBar('a');
      },
      child: Text('Show'),
    ));

以上代码会报错 是因为需要试图从context的父Widget中找到一个Scaffold 但它使用的context是当前的Element 它的子元素才包含了Scaffold

Builder就可以解决这个问题 它可以为子元素提供深层次的context

    Scaffold(
        body: Builder(
      // 这个context是Builder的context 可以找到Scaffold
      builder: (context) => ElevatedButton(
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackBar('a');
        },
        child: Text('Show'),
      ),
    ));

InheritedWidget

功能等同于context 为下方widget提供共享数据

class CounterInherited extends InheritedWidget {
  final int counter;
  
  // 被标记为const的构造函数 自身可以被创建为const值 
  const CounterInherited({
    required this.counter,
    required super.child,
    super.key,
  });

  // CounterInherited.of(context) 让下方widget获取此处的数据
  static CounterInherited? of(BuildContext context) {
    // 让使用它的widget订阅当前的InheritedWidget
    return context.dependOnInheritedWidgetOfExactType<CounterInherited>();
  }

  // 是否需要通知订阅者
  @override
  bool updateShouldNotify(CounterInherited oldWidget) {
    return oldWidget.counter != counter;
  }
}

注意 InheritedWidget只是数据的提供者 如果想要改变数据 需要配合StatefulWidget 将数据和修改能力同时共享

state的生命周期

在state构造后 常规的生命周期函数如下 他们都继承自State类 并且覆写时必须调用super里对应的函数

  • initState state创建后.此时context已经就绪,对应的Element已经创建并挂载.
  • didChangeDependencies initState后调用.当前state依赖的InheritedWidget变化后调用(在任何位置使用InheritedWidget都会被自动记录).
  • didUpdateWidget Widget变化后调用(首次渲染不调用).接受oldWidget.在此函数调用后,build才会被调用.
  • deactivate 临时移除时(例如路由切换).暂停动画、释放资源.
  • activate 首次渲染后或者重新加入时.
  • dispose 彻底销毁时.不能调用setState.

StatelessWidget

没有state的widget

class A extends StatelessWidget {
  const A({super.key});
  
  @override
  Widget build(BuildContext context) {
      //return xxx
  }
}

与react的对比

在react中 每个组件都是props+state+render的聚合

但flutter中 widget只负责props的部分 任何形式的"数据变化"都必须依赖State