【问题标题】:Flutter: how to force an application restart (in production mode)?Flutter:如何强制应用程序重启(在生产模式下)?
【发布时间】:2018-10-11 10:08:14
【问题描述】:

生产模式中,有没有办法强制完全重启应用程序(我不是在谈论开发时的热重载!)。

实际用例:

  • 在初始化过程中,应用程序检测到没有网络连接。缺少网络连接可能会阻止正确启动(例如,加载 JSON 文件等外部资源...)。

  • 在初始握手期间,需要下载一些重要资源的新版本(一种更新)。

在这两个用例中,我希望应用程序继续完全重启,而不是在 ApplicationState 级别构建复杂的逻辑。

非常感谢您的提示。

【问题讨论】:

    标签: flutter


    【解决方案1】:

    您可以将整个应用程序包装到一个有状态的小部件中。当你想重新启动你的应用程序时,用一个拥有不同Key 的孩子重建那个有状态的小部件。

    这会让你失去应用的整个状态。

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(
        RestartWidget(
          child: MaterialApp(),
        ),
      );
    }
    
    class RestartWidget extends StatefulWidget {
      RestartWidget({this.child});
    
      final Widget child;
    
      static void restartApp(BuildContext context) {
        context.findAncestorStateOfType<_RestartWidgetState>().restartApp();
      }
    
      @override
      _RestartWidgetState createState() => _RestartWidgetState();
    }
    
    class _RestartWidgetState extends State<RestartWidget> {
      Key key = UniqueKey();
    
      void restartApp() {
        setState(() {
          key = UniqueKey();
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return KeyedSubtree(
          key: key,
          child: widget.child,
        );
      }
    }
    

    在此示例中,您可以使用 RestartWidget.restartApp(context) 从任何地方重置您的应用。

    【讨论】:

    • 像魅力一样工作。非常感谢。
    • 这不应该是正确的答案,因为它不会在操作系统级别“重新启动应用程序”。例如,由于操作系统内存问题,您可能需要执行此操作,并且要清除它,只有从内存中完全删除应用程序并重新启动它会有所帮助。我已经看到了可以在 Manifest 中的 android 级别上完成此操作的示例。谷歌一下。
    • @JamesGardiner 那么你对这个问题的解决方案是什么?
    • 上述关于 Flutter 1.12.1 弃用的评论需要根据 [stackoverflow.com/questions/59448102/…final _RestartWidgetState state = context.findAncestorStateOfType&lt;State&lt;RestartWidget&gt;&gt;(); 更改为final _RestartWidgetState state = context.findAncestorStateOfType&lt;State&lt;RestartWidget&gt;&gt;();
    • 它对我很有用。但是我在 MaterialApp() 中添加了navigatorKey,它停止工作。有什么建议?我使用navigatorKey 在没有上下文的情况下推送屏幕。
    【解决方案2】:

    flutter_phoenix package 基于Rémi Rousselet's answer,使其更加简单。

    void main() {
      runApp(
        Phoenix(
          child: App(),
        ),
      );
    }
    

    然后当你需要重启应用时,只需调用:

    Phoenix.rebirth(context);
    

    【讨论】:

    • 我试过这个包,但它对我不起作用,对象不会重置。还是它没有重置对象?
    • 对我不起作用,当我调用 rebirth() 方法时没有任何反应
    【解决方案3】:

    我开发了restart_app 插件来本地重启整个应用程序。


    更新:

    对于遇到此异常的任何人:

    MissingPluginException(No implementation found for method restartApp on channel restart)
    

    只需停止并重建应用程序。

    【讨论】:

    • MissingPluginException(No implementation found for method restartApp on channel restart) 是因为 Flutter 2(我尝试在 1.22.6 上使用它)还是其他原因?
    • 请将其迁移到空安全,这是一个非常有用的插件
    • 你碰巧知道linux的解决方案吗?
    • 但它是否适用于 iOS 设备
    • 很棒的包装。为我的 Flutter Web 工作
    【解决方案4】:

    你也可以使用 runApp(new MyWidget) 函数来做类似的事情

    这就是这个函数的作用:

    给给定的小部件充气并将其附加到屏幕上。

    小部件在布局期间被赋予强制它填满整个屏幕的约束。如果您希望将小部件与屏幕的一侧(例如顶部)对齐,请考虑使用对齐小部件。如果你希望你的小部件居中,你也可以使用中心小部件

    再次调用 runApp 将从屏幕上分离前一个根小部件并将给定的小部件附加到它的位置。将新的小部件树与之前的小部件树进行比较,并将任何差异应用于底层渲染树,类似于在调用 State.setState 后重建 StatefulWidget 时发生的情况。

    https://docs.flutter.io/flutter/widgets/runApp.html

    【讨论】:

    • 这样做的问题是它可能会保留状态
    • @RémiRousselet 那么你对这个问题的解决方案是什么?
    • 嘿@RémiRousselet,我目前正在使用这种方法和范围块模式。你能帮我理解什么时候可以保留状态吗?祝贺 I/O 公告。
    • 如果您为传递给runApp 的小部件指定UniqueKey,那应该没问题。更糟糕的是它让想要刷新的小部件知道太多。
    • 这不起作用。再次调用 runapp 完全没有任何作用。
    【解决方案5】:

    这么简单package:flutter_restart

    dependencies:
    flutter_restart: ^0.0.3
    

    使用:

    void _restartApp() async {
      FlutterRestart.restartApp();
    }
    

    【讨论】:

    • @PålBrattberg 我在我的项目中使用它并且工作正常。
    • 对那条笼统的评论感到抱歉。我在 iOS、Android、Web 和 macOS 上运行,并在 macOS 上尝试了它不起作用。但对于移动设备,这可能会奏效。不过,我会推荐其中一个纯 Flutter 答案,因为它们可能适用于更多平台。
    【解决方案6】:

    我发现 Hossein's restart_app 包对于本机重启也非常有用(不仅在 Flutter 级别)。

    对于遇到 MissingPluginException 错误的每个人,只需在设备上重新安装应用程序,这意味着热重载将不起作用。该应用程序具有需要在 Android/iOS 应用程序中编译的原生方法。

    【讨论】:

    【解决方案7】:

    我只想添加关于我已经尝试过@Remi 的答案,它在大多数情况下都可以很好地重启应用程序。答案的唯一问题是,如果您广泛地进行导航路线,您可能会进入一个状态,它会给您一个错误,例如, The method 'restartApp' was called on null。 要解决此错误,您必须知道上下文并多次使用 Navigator.of(context).pop();。对我来说,解决方案是走最初的路线。它将从一个新的注入所有状态。在你想重新启动的地方添加这条线。

     Navigator.pushNamedAndRemoveUntil(context,'/',(_) => false);
    

    如果您只想重新启动特定的小部件,那么 Remi 解决方案非常棒。不过,感谢雷米的解决方案。它可以帮助我理解 Flutter 中的状态。

    【讨论】:

      猜你喜欢
      • 2023-03-22
      • 2015-07-21
      • 1970-01-01
      • 2018-05-31
      • 1970-01-01
      • 2015-05-10
      • 2022-07-19
      • 2014-09-17
      相关资源
      最近更新 更多