Flutter 上的小部件的 onResume() 和 onPause()

Posted

技术标签:

【中文标题】Flutter 上的小部件的 onResume() 和 onPause()【英文标题】:onResume() and onPause() for widgets on Flutter 【发布时间】:2017-11-04 00:43:25 【问题描述】:

现在,一个小部件只有在第一次创建小部件时触发的 initeState() 和在销毁小部件时触发的 dispose()。有没有一种方法可以检测小部件何时回到前台?当一个小部件因为另一个小部件刚刚被前景化而即将进入后台时? 相当于 android 触发 onResume 和 onPause 触发 ios 触发 viewWillAppear 和 viewWillDisappear

【问题讨论】:

您的用例是什么?了解您正在尝试做什么可以帮助我们提供更多信息。谢谢! 答案太棒了!但我所寻找的正是***.com/a/44417260/3217522 看看 FocusDetector。它正在监听小部件的前台可见性:pub.dev/packages/focus_detector 【参考方案1】:

有一个抽象类调用者WidgetsBindingObserver

https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) 
    setState(() 
            _notification = state;
    );
  

有“状态”,可以管理为

switch(state) 
  case AppLifecycleState.resumed:
    // Handle this case
    break;
  case AppLifecycleState.inactive:
    // Handle this case
    break;
  case AppLifecycleState.paused:
    // Handle this case
    break;
  case AppLifecycleState.suspending:
    // Handle this case
    break;

【讨论】:

我试过了,但从未调用过 didChangeAppLifecycleState()。我还能做什么? 你添加了“with WidgetsBindingObserver”吗?请参阅docs.flutter.io/flutter/widgets/… 的文档 谢谢。后来我意识到生命周期是如何工作的。它并不是为了解决我想要解决的问题。我找到了另一种解决问题的方法。 这是应用程序级别的生命周期,这意味着,当整体恢复或不活动或暂停时,而不是单个小部件。 这是应用程序生命周期,而不是小部件生命周期【参考方案2】:

这是一个完整的例子,展示了如何正确处理事情,为了测试这个,按下主页按钮并恢复应用程序,你会看到didChangeAppLifecycleState被调用。

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


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

    // Add the observer. 
    WidgetsBinding.instance!.addObserver(this);
  

  @override
  void dispose() 
    // Remove the observer
    WidgetsBinding.instance!.removeObserver(this);

    super.dispose();
  

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) 
    super.didChangeAppLifecycleState(state);

    // These are the callbacks
    switch (state) 
      case AppLifecycleState.resumed:
        // widget is resumed
        break;
      case AppLifecycleState.inactive:
        // widget is inactive
        break;
      case AppLifecycleState.paused:
        // widget is paused
        break;
      case AppLifecycleState.detached:
        // widget is detached
        break;
    
  

  @override
  Widget build(BuildContext context) => Scaffold();

【讨论】:

太棒了!这一定是公认的答案! 实际上这段代码 sn -p 监控的是应用程序的生命周期,而不是小部件。如果你测试它,你会发现简历上的“第一个”没有被调用。这是因为应用程序已经恢复。需要为 Android 片段/活动等效项找到另一个解决方案:)【参考方案3】:

您想要执行此操作的最常见情况是,如果您正在运行动画并且您不想在后台消耗资源。在这种情况下,您应该使用TickerProviderStateMixin 扩展您的State,并使用您的State 作为vsyncAnimationController 参数。 Flutter 只会在你的 State 可见时调用动画控制器的监听器。

如果您希望在 PageRoute 被其他内容遮盖时处置存在于您的 PageRoute 中的 States,您可以将 falsemaintainState 参数传递给您的 PageRoute 构造函数.如果您这样做,您的State 将在隐藏时重置自身(及其子级),并且必须使用作为构造函数参数传递给其widget 的属性在initState 中重新构建自身。如果您不想完全重置,可以使用模型或控制器类或PageStorage 来保存用户的进度信息。

这是一个演示这些概念的示例应用程序。

import 'package:flutter/material.dart';

void main() 
  runApp(new MaterialApp(
    onGenerateRoute: (RouteSettings settings) 
      if (settings.name == '/') 
        return new MaterialPageRoute<Null>(
          settings: settings,
          builder: (_) => new MyApp(),
          maintainState: false,
        );
      
      return null;
    
  ));


class MyApp extends StatefulWidget 
  MyAppState createState() => new MyAppState();


class MyAppState extends State<MyApp> with TickerProviderStateMixin 
  AnimationController _controller;

  @override
  void initState() 
    print("initState was called");
    _controller = new AnimationController(vsync: this)
      ..repeat(min: 0.0, max: 1.0, period: const Duration(seconds: 1))
      ..addListener(() 
        print('animation value $_controller.value');
      );
    super.initState();
  

  @override
  void dispose() 
    print("dispose was called");
    _controller.dispose();
    super.dispose();
  

  int _counter = 0;

  @override
  Widget build(BuildContext context) 
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('home screen')
      ),
      body: new Center(
        child: new RaisedButton(
          onPressed: () 
            setState(() 
              _counter++;
            );
          ,
          child: new Text('Button pressed $_counter times'),
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.remove_red_eye),
        onPressed: () 
          Navigator.push(context, new MaterialPageRoute(
            builder: (BuildContext context) 
              return new MySecondPage(counter: _counter);
            ,
          ));
        ,
      ),
    );
  


class MySecondPage extends StatelessWidget 
  MySecondPage( this.counter );

  final int counter;

  Widget build(BuildContext context) 
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Certificate of achievement'),
      ),
      body: new Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          new Icon(Icons.developer_mode, size: 200.0),
          new Text(
            'Congrats, you clicked $counter times.',
            style: Theme.of(context).textTheme.title,
            textAlign: TextAlign.center,
          ),
          new Text(
            'All your progress has now been lost.',
            style: Theme.of(context).textTheme.subhead,
            textAlign: TextAlign.center,
          ),
        ],
      ),
    );
  

【讨论】:

如果它只是您想要处理/重新创建的选项卡控制器的一个选项卡怎么办?而且您不想弄乱保存/恢复会很痛苦的其他选项卡?【参考方案4】:

我有点晚了,但为将来可能正在寻找它的人提供了完美的解决方案。 Navigator.push() 实际上是一个未来。这意味着它具有then() 回调函数。因此,在您从第二个屏幕调用 Navigator.pop() 之后,将调用 then()。甚至您可以从第二个屏幕发送一些数据并访问第一个屏幕中的数据。

示例:

//from Screen A
Navigator.of(context).push(MaterialPageRoute(builder:(context)=>B()))
.then((value)=> refresh() );

//in Screen B with data
Navigator.pop(context,[1]);

//or without data
Navigator.pop(context);

所以refresh() 将在屏幕 A 的简历上被调用。

【讨论】:

如果屏幕 B 没有Navigator.pop(context,[1]); 怎么办。所以屏幕 A 上的then 永远不会运行。【参考方案5】:

我创建 visibility_aware_state 是因为它的行为类似于 Android 的 Activity.onResume()。它还考虑了弹出和推送导航。

class Example extends StatefulWidget 
  @override
  _ExampleState createState() => _ExampleState();


class _ExampleState extends VisibilityAwareState<Example> 
  @override
  Widget build(BuildContext context) 
    // return your widget
  

  @override
  void onVisibilityChanged(WidgetVisibility visibility) 
    switch(visibility) 
      case WidgetVisibility.VISIBLE:
        // Like Android's Activity.onResume()
        break;
      case WidgetVisibility.INVISIBLE:
        // Like Android's Activity.onPause()
        break;
      case WidgetVisibility.GONE:
        // Like Android's Activity.onDestroy()
        break;
    
    super.onVisibilityChanged(visibility);
  

【讨论】:

很好,感谢您构建和分享此解决方案。我有一个基类设置,WidgetsBindingObserver@override 的解决方案 void didChangeAppLifecycleState(AppLifecycleState state) 在那里不起作用。这在该设置中确实有效【参考方案6】:

Mamnarock您的回答正确但不完整,您分享的链接不可用。

完整代码如下:

import 'package:flutter/material.dart';

class YourClass extends StatefulWidget 

  @override
  _YourClassState createState() => _YourClassState();


class _YourClassState extends State<YourClass>
    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) 
    switch (state) 
      case AppLifecycleState.resumed:
        // Handle this case
        break;
      case AppLifecycleState.inactive:
        // Handle this case
        break;
      case AppLifecycleState.paused:
        // Handle this case
        break;
      case AppLifecycleState.detached:
        // Handle this case
        break;
    
  

  @override
  Widget build(BuildContext context) 
    return Container();
  

正如评论中提到的 TeeTracker:

这是一个应用级别的生命周期,这意味着当整体恢复、不活动或暂停时,而不是单个小部件。

【讨论】:

以上是关于Flutter 上的小部件的 onResume() 和 onPause()的主要内容,如果未能解决你的问题,请参考以下文章

三元条件没有重建我的小部件 - Flutter

Flutter:StreamProvider 的奇怪行为,使用不完整数据重建的小部件

Flutter Provider 重建不必要的小部件

用于从 REST API 获取状态并自动刷新的小部件的 Flutter 食谱?

Flutter 滚动视图到列上的焦点小部件

iOS 上的 Flutter FCM 不调用 onMessage 和 onResume