如何在 Flutter 应用退出前执行代码

57
我想在用户退出我的应用程序之前检测到并执行一些代码,但我不知道该如何实现。我尝试使用此软件包:https://pub.dev/packages/flutter_lifecycle_state,但是我遇到了以下错误:

flutter/.pub-cache/hosted/pub.dartlang.org/flutter_lifecycle_state-1.0.0/lib/flutter_lifecycle_state.dart:80:30: 错误:未找到Getter: 'suspending'。 case AppLifecycleState.suspending

如果您有任何解决此问题的方法或了解其他检测用户退出应用程序的方式,那将是很酷的事情。

1
你读过注意事项了吗?①Flutter端的小部件的生命周期方法调用都是由宿主应用程序端创建的。在应用程序突然关闭后,Flutter终端将不再接收任何消息。②当Flutter端的根页面正常关闭时,State#dispose方法不会被触发,因此我们的onDestroy方法也不会被触发,所以如果你想释放资源,你必须自己处理。 - Dev
啊,是的!你有没有其他解决这个问题的方案? - luc
1
取决于您在退出应用程序时想要执行什么操作。 - Dev
执行一个函数来删除 Firebase 上的文档。 - luc
2
我想在退出应用程序时删除Firebase文档。你解决了吗? - NullPointer
如何覆盖WidgetsBindingObserver的didRequestAppExit()方法呢? - undefined
7个回答

30

现在你无法完全做到想做的事情,但目前最好的方法是使用来自SDK的AppLifecycleState检查应用程序是否在后台/不活动状态运行(基本上是你的库试图要做的事情)

你正在使用的库已经过时了,自2019年11月的一次拉取请求后,AppLifecycleState.suspending被称为AppLifecycleState.detached

您可以在api.flutter.dev网站中查看AppLifecycleState枚举类型。

以下是如何观察包含活动的生命周期状态的示例:

import 'package:flutter/widgets.dart';

class LifecycleWatcher extends StatefulWidget {
  @override
  _LifecycleWatcherState createState() => _LifecycleWatcherState();
}

class _LifecycleWatcherState extends State<LifecycleWatcher> with WidgetsBindingObserver {
  AppLifecycleState _lastLifecycleState;

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

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

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    setState(() {
      _lastLifecycleState = state;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_lastLifecycleState == null)
      return Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr);

    return Text('The most recent lifecycle state this widget observed was: $_lastLifecycleState.',
        textDirection: TextDirection.ltr);
  }
}

void main() {
  runApp(Center(child: LifecycleWatcher()));
}

我认为在不活动的周期中删除您的数据,然后在恢复的周期中重新创建数据可能适合您。


那段代码是因为他能够看到状态如何工作以及如何使用生命周期事件来调用他的函数,可能会在暂停周期中删除数据,然后在恢复的周期中重新创建数据。这也是SDK执行他试图使用的库所做的方式。 - Josep Bové
1
这是最接近他想要做的方式,我编辑了帖子,因为他会看到这不完全是他所要求的,但目前这是唯一的方法。 - Josep Bové
_lastLifecycleState 需要被初始化(或者声明为 late?)。此外,Android Studio 表示空值检查是不可能的。 - Thomas Weller
_lastLifecycleState 需要被初始化(或者声明为 late?)。此外,Android Studio 表示空值检查是不可能的。 - undefined

18

audio_service 插件执行了类似的功能。策略是将您的应用程序包装在一个自定义小部件中,监听应用程序生命周期状态的更改,然后根据状态运行不同的代码。我并不是说您应该使用此插件,而是可以根据需要调整代码。请将下面的代码引用 AudioService 替换为您需要运行的任何代码。

这里是audio_service 的代码

/// A widget that maintains a connection to [AudioService].
///
/// Insert this widget at the top of your `/` route's widget tree to maintain
/// the connection across all routes. e.g.
///
/// ```
/// return MaterialApp(
///   home: AudioServiceWidget(MainScreen()),
/// );
/// ```
///
/// Note that this widget will not work if it wraps around [MateriaApp] itself,
/// you must place it in the widget tree within your route.
class AudioServiceWidget extends StatefulWidget {
  final Widget child;

  AudioServiceWidget({@required this.child});

  @override
  _AudioServiceWidgetState createState() => _AudioServiceWidgetState();
}

class _AudioServiceWidgetState extends State<AudioServiceWidget>
    with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    AudioService.connect();
  }

  @override
  void dispose() {
    AudioService.disconnect();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        AudioService.connect();
        break;
      case AppLifecycleState.paused:
        AudioService.disconnect();
        break;
      default:
        break;
    }
  }

  @override
  Future<bool> didPopRoute() async {
    AudioService.disconnect();
    return false;
  }

  @override
  Widget build(BuildContext context) {
    return widget.child;
  }
}

注意:

  • 对于大多数用户来说,退出应用程序通常只意味着将其隐藏。该应用程序仍在后台运行,直到系统为节省资源而终止它。

7
把主/首页的 Scaffold widget 放在 WillPopScope 中怎么样?
class MyGreatestApp extends StatefulWidget {
  @override
  _MyGreatestAppState createState() => _MyGreatestAppState();
}

class _MyGreatestAppState extends State<MyGreatestApp> {
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      child: Scaffold(...),
      onWillPop: _doNastyStuffsBeforeExit,
    );
  }

  Future<bool> _doNastyStuffsBeforeExit() async{
    // Since this is an async method, anything you do here will not block UI thread
    // So you should inform user there is a work need to do before exit, I recommend SnackBar

    // Do your pre-exit works here...

    // also, you must decide whether the app should exit or not after the work above, by returning a future boolean value:

    return Future<bool>.value(true); // this will close the app,
    return Future<bool>.value(false); // and this will prevent the app from exiting (by tapping the back button on home route)
  }
}

4
请注意,WillPopScope有一个效果,可以禁用在iOS上的“向右滑动返回”手势,这可能不是想要的结果。这种行为也是有意设计的(请参见https://github.com/flutter/flutter/issues/14203)。相反,您可以使用WidgetsBindingObserver.didPopRoute,因为它更低级一些(请参见Suragch的答案以获得示例)。 - Ryan Heise

4

1-) 首先,在你的有状态小部件中使用关键字“with”使用WidgetsBindingObserver。

2-) 在你的initState中使用WidgetsBinding.instance.addObserver(this);初始化WidgetsBinding。

3-) 在你的dispose中使用WidgetsBinding.instance.removeObserver(this);释放WidgetsBinding。

4-) 最后,使用didChangeAppLifecycleState来检查用户是否离开了应用程序。以下是示例;

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> 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:
        //Execute the code here when user come back the app.
        //In my case, I needed to show if user active or not,
        FirebaseMethods().updateLiveStatus(_authInstance.currentUser.uid, true);
        break;
      case AppLifecycleState.paused:
        //Execute the code the when user leave the app
        FirebaseMethods()
            .updateLiveStatus(_authInstance.currentUser.uid, false);
        break;
      default:
        break;
    }
  }

  @override
  Widget build(BuildContext context) {}
}

3
Flutter版本3.13添加了AppLifecycleListener,现在你可以监听show、pause、resume、restart等事件。
_listener = AppLifecycleListener(
  onShow: () => _handleTransition('show'),
  onResume: () => _handleTransition('resume'),
  onHide: () => _handleTransition('hide'),
  onInactive: () => _handleTransition('inactive'),
  onPause: () => _handleTransition('pause'),
  onDetach: () => _handleTransition('detach'),
  onRestart: () => _handleTransition('restart'),
  // This fires for each state change. Callbacks above fire only for
  // specific state transitions.
  onStateChange: _handleStateChange,
);

获取更多信息,请查看来自Flutter存储库的示例


2

我曾在Android端制作的一个插件中实现了类似的功能,思路是需要利用Flutter的MethodChannel类,在应用关闭之前从本地端调用你想要在Flutter端执行的方法。

在我的情况下,我实现了对onDetachedFromActivity的重写,该方法会在Flutter引擎分离之前被调用。 该方法可以在io.flutter.embedding.engine.plugins.activity.ActivityAware接口中找到。


0
在我的测试中,WidgetsBinding无法检测到onDestory。我认为Flutter的MethodChannel是在应用程序onDestory时执行特定功能的最佳方法。(就像Basel Abuhadrous的答案一样)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接