flutter-state生命周期探索

如今公司用flutter开发项目,写了几个月的业务,对于widget的生命周期也朦朦胧胧,如今得空来探知下。

测试界面


图片.png

最外层一个Scaffold包着父widget,是个StatefulBuilder,点击按钮父组件刷新,就会刷新StatefulBuilder
widget是个LifecycleStateful ,打印state生命周期的方法

class LifecycleDemoPage extends StatelessWidget {
  const LifecycleDemoPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        ...
        body: StatefulBuilder(builder: (context,setter){
          return Column(
            children: [
              ElevatedButton(onPressed: (){
                setter.call((){});
              }, child: Text('父组件刷新')),
              LifecycleStateful(),
            ],
          );
        }));
  }
}

class LifecycleStateful extends StatefulWidget {
  @override
  State<StatefulWidget> createState(){
    LogUtils.d("createState ${toStringShort()}#$hashCode");
    return  LifecycleStatefulState();
  }
}

class LifecycleStatefulState extends State<LifecycleStateful> {
  int click = 0;
  @override
  void initState() {
    super.initState();
    LogUtils.d("State initState ${toStringShort()}");
  }
  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    LogUtils.d("State dispose ${toStringShort()}");
  }
  @override
  void deactivate() {
    // TODO: implement deactivate
    super.deactivate();
    LogUtils.d("State deactivate ${toStringShort()}");
  }
  @override
  void activate() {
    // TODO: implement activate
    super.activate();
    LogUtils.d("State activate ${toStringShort()}");
  }
  @override
  void reassemble() {
    // TODO: implement reassemble
    super.reassemble();
    LogUtils.d("State activate ${toStringShort()}");
  }
  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    LogUtils.d("State didChangeDependencies ${toStringShort()}");
  }
  @override
  void didUpdateWidget(covariant LifecycleStateful oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    LogUtils.d("State didUpdateWidget ${toStringShort()}");
  }
  List<Widget> children =[];
  bool newChild = false;
  @override
  Widget build(BuildContext context) {
    LogUtils.d("State build ${toStringShort()}");

    if(children.isEmpty){
      children = [
        ElevatedButton(
            onPressed: () {
              setState(() {
                click++;
              });
            },
            child: Text("click $click")),
        ElevatedButton(
            onPressed: () {
              setState(() {
                children.add(Text("i am add"));
              });
            },
            child: Text("添加子child")),
        ElevatedButton(
            onPressed: () {
              setState(() {
                newChild=!newChild;
              });
            },
            child: Text("change")),
      ];
    }

    return newChild?ElevatedButton(onPressed: (){
      setState(() {
        newChild=!newChild;
      });
    }, child: Text('change')):Container(
      padding: EdgeInsets.all(16),
      child: Column(
        children:children ,
      ),
    );
  }
}

进入界面时LifecycleStateful生命周期方法调用输出

I/flutter ( 7336): LifecycleStateful.createState (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:38:14)
I/flutter ( 7336):LifecycleStatefulState.initState (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:48:14)
I/flutter ( 7336): LifecycleStatefulState.didChangeDependencies (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:78:14)
I/flutter ( 7336): LifecycleStatefulState.build (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:89:14)

结论:生命周期调用依次 createState ,State构造器,initState,didChangeDependencies,build方法

点击父组件刷新按钮,此时父控件刷新,作为子控件的LifecycleStateful生命周期方法调用输出

I/flutter (22291):  LifecycleStatefulState.didUpdateWidget (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:102:14)
I/flutter (22291):  LifecycleStatefulState.build (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:108:14)

父控件刷新,子控件LifecycleStateful会调用didUpdateWidget ,build

点击click,调用setState方法,只调用了build方法

I/flutter ( 7336):LifecycleStatefulState.build (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:89:14)

切换主题颜色后调用了didUpdateWidgetbuild方法

I/flutter (22291): LifecycleStatefulState.didUpdateWidget (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:92:14)
I/flutter (22291): LifecycleStatefulState.build (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:98:14)

返回退出,依次调用deactivatedispose 方法

I/flutter (22291):  LifecycleStatefulState.deactivate (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:78:14)
I/flutter (22291): LifecycleStatefulState.dispose (package:flutter_demo/demo/life_cycler/life_cycler_demo_page.dart:72:14)

可以看到从进入到退出initState只打印一次。类似ViewonAttachedToWindow方法,build方法
就类似draw方法每次刷新会调用。dispose方法类似onDetachedFromWindow。只不过View的生命周期方法还有其他,颗粒度更细些。

结论:生命周期开始调用initState,更新会多次调用builddidUpdateWidget ,销毁调用dispose

didChangeDependencies方法除进入界面调用,就没在调用过。看源码注释说是和InheritedWidget有关

/// Called when a dependency of this [State] object changes.
  ///
  /// For example, if the previous call to [build] referenced an
  /// [InheritedWidget] that later changed, the framework would call this
  /// method to notify this object about the change.
  ///

为此另写代码继承InheritedWidget 控件,
界面如下:就一个InheritedWidget包裹着一个Text,和一个按钮,
按钮点击一下,count+=1并刷新,

图片.png

示例代码

class ShareDataWidget  extends InheritedWidget {
  final int data; //需要在子树中共享的数据,保存点击次数
  ShareDataWidget({
    Key? key,
    required this.data,
    required Widget child,
  }) : super(key: key, child: child);

  //定义一个便捷方法,方便子树中的widget获取共享数据
  static ShareDataWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
  }
//定义一个便捷方法,方便子树中的widget获取共享数据
  static ShareDataWidget? of2(BuildContext context) {
    //return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
    return (context.getElementForInheritedWidgetOfExactType<ShareDataWidget>()!.widget as ShareDataWidget );
  }
  //该回调决定当data发生变化时,是否通知子树中依赖data的Widget
  @override
  bool updateShouldNotify(covariant ShareDataWidget oldWidget) {
    // TODO: implement updateShouldNotify
    return false;
  }
}
class _TestWidget extends StatefulWidget {
  @override
  __TestWidgetState createState() => __TestWidgetState();
}

class __TestWidgetState extends State<_TestWidget> {
  @override
  Widget build(BuildContext context) {
    //使用InheritedWidget中的共享数据
    return Text(ShareDataWidget.of(context)!.data.toString());
    // return Text("aaa");
  }
  @override
  void didUpdateWidget(covariant _TestWidget oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget");
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
    //如果build中没有依赖InheritedWidget,则此回调不会被调用。
    print("Dependencies change");
  }
}
class InheritedWidgetTestRoute extends BasePage {
  InheritedWidgetTestRoute({ super.title:"InheritedWidget数据共享"});
  int count = 0;
  @override
  Widget body(BuildContext context) {
    return  Center(
      child: ShareDataWidget( //使用ShareDataWidget
        data: count,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(bottom: 20.0),
              child: _TestWidget(),//子widget中依赖ShareDataWidget
            ),
            ElevatedButton(
              child: Text("Increment"),
              //每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
              onPressed: () => setState(() => ++count),
            )
          ],
        ),
      ),
    );
  }
}

注意上面updateShouldNotify方法返回false,点击按钮后,子widget调用

 I/flutter (22291): didUpdateWidget
 I/flutter (22291): build

修改updateShouldNotify返回true

I/flutter (22291): didUpdateWidget
I/flutter (22291): Dependencies change
I/flutter (22291): build

结论:InheritedWidgetupdateShouldNotify返回false时,子widget也只调用didUpdateWidgetbuild
updateShouldNotify返回true时,子widget依次调用didUpdateWidgetdidChangeDependenciesbuild

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容