Flutter 中的生命周期

Posted

技术标签:

【中文标题】Flutter 中的生命周期【英文标题】:Life cycle in flutter 【发布时间】:2017-05-19 15:43:28 【问题描述】:

flutter 是否有类似Activity.resume() 的方法可以告诉开发者用户已经回到活动。

当我在 Page-B 中从 Internet 中挑选数据并返回到 Page-A 时,如何让 Page-A 知道数据已准备好。

【问题讨论】:

我没有完全理解这个问题:您只是想在导航堆栈上的当前屏幕之前将数据传递到屏幕吗? 查看此链接medium.com/flutter-community/… 【参考方案1】:

    createState(): 当Framework被指示构建StatefulWidget时,它会立即调用createState()

    mounted 为真:createState 创建您的状态类时,会为该状态分配一个buildContextbuildContext 过于简化,是放置此小部件的小部件树中的位置。这是一个更长的解释。 所有小部件都有一个bool this.mounted 属性。当分配buildContext 时,它变为真。卸载小部件时调用setState 是错误的。

    initState(): 这是创建小部件时调用的第一个方法(当然是在类构造函数之后)。initState 被调用一次且仅一次。它必须调用super.initState()

    didChangeDependencies(): 在第一次构建小部件时,在initState 之后立即调用此方法。

    build(): 这个方法经常被调用。它是必需的,并且必须返回一个 Widget。

    didUpdateWidget(Widget oldWidget): 如果父窗口小部件发生变化并且必须重新构建这个窗口小部件(因为它需要给它不同的数据),但它正在用相同的runtimeType 重新构建,那么这个方法就会被调用。 这是因为 Flutter 正在重用状态,这是长期存在的。在这种情况下,您可能需要再次初始化一些数据,就像在 initState 中一样。

    setState(): 这个方法经常被框架本身和开发者调用。它用于通知框架数据已更改

    deactivate(): 当从树中删除 State 时调用 Deactivate,但它可能会在当前帧更改完成之前重新插入。这种方法之所以存在,主要是因为 State 对象可以从树中的一个点移动到另一个点。

    dispose(): dispose() 在移除 State 对象时调用,这是永久的。此方法是您应该取消订阅和取消所有动画、流等的地方。

    mounted 为假: 状态对象永远无法重新挂载,如果调用setState会抛出错误。

【讨论】:

您应该澄清这些方法存在于哪些类上。 createState 和 initState 位于两个不同的类中。 嗨@Shelly,如果应用程序由于内存不足而崩溃,将调用页面的哪个方法? FutureBuilder 的正确使用位置是什么?是initState 还是didChangeDependencies @Rap 请更新链接或从此处删除。谢谢。 当小部件不可见但仍然存在时是否没有方法?例如向堆栈添加屏幕时?【参考方案2】:

构造函数

这个函数不是生命周期的一部分,因为此时widget属性的State为空,如果要在构造函数中访问widget属性是行不通的。但构造函数必须是第一次调用。

创建状态

当 Flutter 被指示构建一个StatefulWidget 时,它会立即调用createState()

初始状态

将此对象插入树时调用。

在调用时插入渲染树时,该函数在生命周期中只调用一次。这里可以做一些初始化,比如初始化State变量。

设置状态

setState() 方法经常被 Flutter 框架本身和开发者调用。

didChangeDependencies

当此 [State] 对象的依赖项发生更改时调用。

didUpdateWidget

在小部件配置更改时调用。

停用

从树中删除此对象时调用。 在 dispose 之前,我们会调用这个函数。

处置

从树中永久删除此对象时调用。

didChangeAppLifecycleState

当系统将应用程序置于后台或将应用程序返回到前台时调用。

这是一个很好的详细文档:https://www.bookstack.cn/read/flutterbyexample/aebe8dda4df3319f.md

    import 'package:flutter/material.dart';

    class ScreenLifecyle extends StatefulWidget 
    ScreenLifecyleState state;

    //createState(): When the Framework is instructed to build a StatefulWidget, it immediately calls createState()
    @override
    State<StatefulWidget> createState() 
        // TODO: implement createState
        return ScreenLifecyleState();
    
    

    class ScreenLifecyleState extends State<ScreenLifecyle> 
    /*
    mounted is true: When createState creates your state class, a buildContext is assigned to that state.
    BuildContext is, overly simplified, the place in the widget tree in which this widget is placed.
    Here's a longer explanation. All widgets have a bool this.mounted property.
    It is turned true when the buildContext is assigned. It is an error to call setState when a widget is unmounted.
    mounted is false: The state object can never remount, and an error is thrown is setState is called.
    */

    /*
    This is the first method called when the widget is created (after the class constructor, of course.)
    initState is called once and only once. It must called super.initState().
    */
    @override
    void initState() 
        // TODO: implement initState
        super.initState();
        print("initState");
    

    /*
    This method is called immediately after initState on the first time the widget is built.
    */
    @override
    void didChangeDependencies() 
        // TODO: implement didChangeDependencies
        super.didChangeDependencies();
        print("didChangeDependencies");
    

    /*
    build(): This method is called often. It is required, and it must return a Widget.
    */
    @override
    Widget build(BuildContext context) 
        print("build");

        // TODO: implement build
        return Container();
    

    /*
    If the parent widget changes and has to rebuild this widget (because it needs to give it different data),
    but it's being rebuilt with the same runtimeType, then this method is called.
    This is because Flutter is re-using the state, which is long lived.
    In this case, you may want to initialize some data again, as you would in initState.
    */
    @override
    void didUpdateWidget(ScreenLifecyle oldWidget) 
        print("didUpdateWidget");

        // TODO: implement didUpdateWidget
        super.didUpdateWidget(oldWidget);
    

    @override
    void setState(fn) 
        print("setState");

        // TODO: implement setState
        super.setState(fn);
    

    /*
    Deactivate is called when State is removed from the tree,
    but it might be reinserted before the current frame change is finished.
    This method exists basically because State objects can be moved from one point in a tree to another.
    */
    @override
    void deactivate() 
        // TODO: implement deactivate
        print("deactivate");
        super.deactivate();
    

    /*
    Dispose is called when the State object is removed, which is permanent.
    This method is where you should unsubscribe and cancel all animations, streams, etc.
    */
    @override
    void dispose() 
        // TODO: implement dispose
        super.dispose();
     

       @override
        void didChangeAppLifecycleState(AppLifecycleState state) 
            super.didChangeAppLifecycleState(state);
            switch (state) 
            case AppLifecycleState.inactive:
                print('appLifeCycleState inactive');
                break;
            case AppLifecycleState.resumed:
                print('appLifeCycleState resumed');
                break;
            case AppLifecycleState.paused:
                print('appLifeCycleState paused');
                break;
            case AppLifecycleState.suspending:
                print('appLifeCycleState suspending');
                break;
            
        

  
 

【讨论】:

【参考方案3】:

这里有一个例子:https://github.com/flutter/flutter/blob/master/examples/layers/services/lifecycle.dart

你需要使用WidgetsBindingObserver

【讨论】:

【参考方案4】:

只有 StatefulWidget 保持状态。 它的生命周期如下

createState() : 当我们构建一个新的StatefulWidget时,这个立即调用createState(),并且这个override方法必须存在 initState() :它是Widget创建后调用的第一个方法,相当于我们的onCreate()和viewDidLoad() didChangeDependencies() :在第一次构建小部件时,在 initState() 之后立即调用此方法 build() : 在 didChangeDependencies() 之后调用。所有的 GUI 都在这里渲染,并且每次需要渲染 UI 时都会调用 didUpdateWidget() : 一旦父 Widget 发生变化并需要重绘 UI 就会被调用 deactivate() :框架在从树中移除此 State 对象时调用此方法 dispose() :当此对象及其状态从树中永久删除并且永远不会再次构建时调用。

AppLifecycleState如下

inactive - 应用程序处于非活动状态,并且没有 接收用户输入。 仅限 iOS

暂停 - 应用程序当前对用户不可见,不 响应用户输入,并在后台运行。

已恢复 - 应用程序可见并响应用户输入。

suspending - 应用程序将暂时暂停。 安卓 仅限

【讨论】:

如何在 Page1 上检测到我从 Page2 移回的位置? 我试过deactivate,但它在加载时也会收到调用 试过处理了吗? @Dani 你在使用 Stateful Wideget 吗? 不,只是Stateless,但它必须是这样的【参考方案5】:

应用生命周期

对于 LifeCycle,您需要使用 WidgetsBindingObserver 它在应用程序运行在前台和后台时有效。

import 'package:flutter/widgets.dart';
  class YourWidgetState extends State<YourWidget> with WidgetsBindingObserver 

       @override
      void initState() 
        super.initState();
        WidgetsBinding.instance.addObserver(this);
      


      @override
      void dispose() 
        WidgetsBinding.instance.removeObserver(this);
        super.dispose();
      


       @override
      void didChangeAppLifecycleState(AppLifecycleState state) 
        if (state == AppLifecycleState.resumed) 
           //do your stuff
        
      
    

但就我而言,当我从一个屏幕移动到另一个屏幕时,我无法捕捉到OnResume 的情况。所以下面的代码类似于startActivityForResult

当您到达另一个活动时使用此代码

Navigator.push(context,
              MaterialPageRoute(builder: (context) => ProductDetails(pid: productList[index]["pid"],),
                  settings: RouteSettings(name: '/productdetail')),).then((value)
                    setState(() 
                      length=value;
                    );
                    debugPrint('CHECK BACK FROM DETAIL $length');
            );

当你按下返回时

onPressed: ()Navigator.pop(context,length);

【讨论】:

【参考方案6】:

我认为 Flutter 应用程序生命周期回调在这里不会对您有所帮助。你可以试试这个逻辑。

在第一页(导航到第二页时)

Navigator.push(context, MaterialPageRoute(builder: (context) => Page2())).then((value) 
  print("Value returned form Page 2 = $value");
;

在第 2 页(导航回第 1 页时)

Navigator.pop(context, returnedValue);

生命周期回调

void main() => runApp(HomePage());

class HomePage extends StatefulWidget 
  @override
  _HomePageState createState() => _HomePageState();


class _HomePageState extends State<HomePage> with WidgetsBindingObserver 
  @override
  void initState() 
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) 
    super.didChangeAppLifecycleState(state);
    print("Current state = $state");
  

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Lifecycle")),
        body: Center(child: Text("Center"),),
      ),
    );
  

  @override
  void dispose() 
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  

【讨论】:

@OliverDixon 我回答了 OP 的问题。不知道你在找什么。 把这个 Navigator.pop(context, returnedValue); ??在第二页【参考方案7】:

    createState():当我们创建有状态的小部件时,Flutter 框架会指示 createState() 方法。

    @覆盖 _DeveloperLibsWidgetState createState() => _DeveloperLibsWidgetState();

2.mounted(true/false):一旦我们创建了一个 State 对象,框架就会在调用 initState() 方法之前通过将 State 对象与 BuildContext 关联来安装 State 对象。所有小部件都有一个 bool 安装属性。分配 buildContext 时变为 true。

bool get mounted => _element != null;

    initState():这是在类构造函数之后创建有状态小部件时调用的第一个方法。 initState() 只被调用一次。它必须调用 super.initState()。

    @覆盖 初始化状态() super.initState(); // 去做

    didChangeDependencies():在第一次构建小部件时,在 initState() 方法之后立即调用此方法。

    @保护 @mustCallSuper 无效 didChangeDependencies()

    build():它显示了小部件所代表的用户界面部分。框架在几种不同的情况下调用此方法: 调用 initState() 方法后。 框架在调用 didUpdateWidget 后总是调用 build() 方法 在收到调用 setState 更新屏幕后。

    @覆盖 小部件构建(BuildContext 上下文,MyButtonState 状态) 返回容器(颜色:常量颜色(0xFF2DBD3A));

    didUpdateWidget(Widget oldWidget):如果父小部件更改配置并且必须重建此小部件。但它是用相同的 runtimeType 重建的,然后调用了 didUpdateWidget() 方法。

    @mustCallSuper @受保护 void didUpdateWidget(covariant T oldWidget)

    setState():该方法由框架和开发者调用。我们可以更改 State 对象的内部状态,并在传递给 setState() 的函数中进行更改。

    @覆盖 _DeveloperLibsWidgetState createState() => _DeveloperLibsWidgetState();

    deactivate():当 State 从小部件树中移除时调用,但可能会在当前帧更改完成之前重新插入。

    @保护 @mustCallSuper 无效停用()

    dispose():当状态对象被永久删除时调用。在这里您可以取消订阅和取消所有动画、流等。

    @保护 @mustCallSuper 无效处置() 断言(_debugLifecycleState == _StateLifecycle.ready); 断言(() _debugLifecycleState = _StateLifecycle.defunct; 返回 true; ());

【讨论】:

【参考方案8】:

因为这里的每个人都在谈论应用程序生命周期,而不是解决 OP 提出的问题。这是问题的答案。

要求是他想从页面 A 打开页面 B,假设从页面 B 中选择文件,一旦选择了文件,他想回到页面 A 并需要处理页面 A 中选择的文件。就像在android 我们可以在 onActivityResult() 方法中做到这一点。以下是我们在 Flutter 中可以实现的方式。

您可以如下从页面A打开页面B

Map results =  await Navigator.of(context).push(MaterialPageRoute(
      builder: (BuildContext context) 
        return new PageB(title: "Choose File");
        ,
      ));

    if (results != null && results.containsKey('selection')) 
      setState(() 
        _selection = results['selection'];
      );

    **//here you can do whatever you want to do with selection variable.**

    

在页面 B 中,您可以选择文件或您需要返回页面 A 的任何内容,如下所示(选择后返回文件或任何其他变量。

Navigator.of(context).pop('selection':file);

【讨论】:

【参考方案9】:

您可以在 Flutter 扩展 LifecycleState 类中模拟 onResumeonPause 状态。确保使用 push() 或 pushNamed() 方法推送新路由。

/// Inherit this State to be notified of lifecycle events, including popping and pushing routes.
///
/// Use `pushNamed()` or `push()` method to track lifecycle events when navigating to another route.
abstract class LifecycleState <T extends StatefulWidget> extends State<T>
    with WidgetsBindingObserver 
  ResumeResult resumeResult = new ResumeResult();
  bool _isPaused = false;

  AppLifecycleState lastAppState = AppLifecycleState.resumed;

  void onResume() 

  void onPause() 

  /// Use instead of Navigator.push(), it fires onResume() after route popped
Future<T> push<T extends Object>(BuildContext context, Route<T> route, [String source]) 
    _isPaused = true;
    onPause();

    return Navigator.of(context).push(route).then((value) 
        _isPaused = false;

        resumeResult.data = value;
        resumeResult.source = source;

        onResume();
        return value;
    );


/// Use instead of Navigator.pushNamed(), it fires onResume() after route popped
Future<T> pushNamed<T extends Object>(BuildContext context, String routeName, Object arguments) 
    _isPaused = true;
    onPause();

    return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments).then((value) 
        _isPaused = false;

        resumeResult.data = value;
        resumeResult.source = routeName;

        onResume();
        return value;
    );


  @override
  void initState() 
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  

  @override
  void dispose() 
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) 
    if (state == AppLifecycleState.paused) 
      if (!_isPaused) 
        onPause();
      
     else if (state == AppLifecycleState.resumed &&
        lastAppState == AppLifecycleState.paused) 
      if (!_isPaused) 
        onResume();
      
    
    lastAppState = state;
  


class ResumeResult 
  dynamic data;
  String source;

【讨论】:

【参考方案10】:

我知道我可能会迟到一点,但会回答这个问题以防其他人需要答案, 请参考这个解决方案https://***.com/a/58504433/3037840 但我想澄清一点,在您的有状态小部件中,在实现 RouteAware mixin 后,请注意:

@override
  void didPushNext()  //similar to onPause
    // will be called when a new route has been pushed, and the current route is no longer visible. acts similar to onPause
  

  @override
  void didPopNext()  //similar to onResume
     // will be called when the top route has been popped off, and the current route shows up. acts similar to onResume when you are navigated back to your activity/fragment
  

【讨论】:

以上是关于Flutter 中的生命周期的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 中的生命周期分析

flutter实践 - flutter中的生命周期

flutter 生命周期

Flutter生命周期

Flutter生命周期

Flutter 中的生命周期