如何在Flutter应用程序中强制重新启动(在生产模式下)?

119
在生产模式下,有没有一种方法可以强制应用程序进行完全重启(我不是在谈论开发时的热重新加载!)。
实际使用情况: - 在初始化过程中,应用程序检测到没有网络连接。缺乏网络连接可能会阻止正确启动(例如,加载外部资源,如JSON文件...)。 - 在初始握手期间,需要下载某些重要资源的新版本(更新类型)。
在这两种情况下,我希望应用程序进行完全重启,而不必在ApplicationState级别构建复杂的逻辑。
12个回答

228

您可以将整个应用程序包装在一个StatefulWidget中。当您想要重新启动应用程序时,使用具有不同Key的子元素重建该StatefulWidget。

这将使您失去整个应用程序的状态。

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)从任何地方重置您的应用程序。


5
非常好用,非常感谢。 - boeledi
37
这不是正确的答案,因为它没有在操作系统级别上“重新启动应用程序”。例如,由于操作系统内存问题,您可能需要这样做,并且只有完全将应用程序从内存中删除并重新启动才能清除它。我已经看到了可以在Android清单中完成此操作的示例。请谷歌搜索。 - James Gardiner
33
@JamesGardiner,那么你对这个问题有什么解决方案? - Anis Alibegić
1
以上关于Flutter 1.12.1弃用的评论需要更改为final _RestartWidgetState state = context.findAncestorStateOfType<State<RestartWidget>>();,基于[https://dev59.com/FlIH5IYBdhLWcg3weukq]。 - Richard Johnson
4
对我来说效果很好。但是当我在MaterialApp()中添加了“navigatorKey”时,它就停止工作了。有什么建议吗?我使用“navigatorKey”来推送屏幕而无需上下文。 - J. Diaz
显示剩余16条评论

57

1
我尝试了这个包,但它对我不起作用,对象没有重置。或者是它没有重置对象? - Aman Rawat
对我没用,当我调用rebirth()方法时什么也没有发生。 - busuu
不再工作了 https://github.com/mobiten/flutter_phoenix/issues/20 - m123

24

我开发了restart_app插件,在pub.dev上获得了200多个赞,使用本地API来重新启动整个应用程序。


2023年5月25日更新:

该软件包现在支持所有Android、iOS和Web平台。


1
但它是否适用于iOS设备? - hemandroid
1
很棒的包。在Flutter Web上为我工作。 - Mikhail Mazurovskiy
@K.Amanov 这是关于空安全性的。 - Hossein Yousefpour
1
非常好的答案,对我来说完美地运作。 - DL Studio
1
从版本1.2.0开始,现在它也支持iOS。 - Hossein Yousefpour
显示剩余4条评论

15

关于@Remi的答案,我想补充一点。该答案在大多数情况下重启应用程序非常有效。但是,如果你经常进行导航路由并到达一个状态,会出现以下错误: The method 'restartApp' was called on null。 要解决此错误,您需要知道上下文并多次使用Navigator.of(context).pop();返回。对我来说,解决方法是回到初始路由。这将注入所有新状态。在需要重新启动的位置,只需添加此行。

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

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

在遇到同样的问题后,我必须说这个解决方案对我非常有效。可以从任何地方调用,只需要一行代码。保持简单! - Tanguy
是的,简单且满足需求。 - Sneg
这实际上是主要解决方案。 - Onalo Joseph
@Shahryar,我无法感谢你的足够,这是最好的解决方案。 - Imron Gamidli

9
如此简单 :flutter_restart
dependencies:
flutter_restart: ^0.0.3

使用方法:

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

@PålBrattberg 我在我的项目中使用它,它运行良好。 - Mina Farid
1
抱歉之前那个笼统的评论。我在iOS、Android、Web和macOS上运行过,并且在macOS上尝试了一下,但是没有成功。但对于移动端来说,这很可能会起作用。不过,基于纯Flutter的答案更值得推荐,因为它们很可能可以在更多的平台上运行。 - Pål Brattberg

9

你也可以使用runApp(new MyWidget)函数来实现类似的功能。

这个函数的作用是:

将给定的widget填充并附加到屏幕上。

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

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

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


3
问题在于这可能会保留状态。 - Rémi Rousselet
@RémiRousselet,那么你对这个问题有什么解决方案? - Anis Alibegić
嘿@RémiRousselet,我目前正在使用具有作用域块模式的这种方法。您能帮助我了解何时可能会保留状态吗?祝贺您的I/O公告。 - user8467470
如果您在传递给runApp的小部件中指定了一个UniqueKey,那应该是可以的。更糟糕的是,它会让需要刷新的小部件了解得太多。 - Rémi Rousselet
通过接受的答案,刷新事件不知道根部小部件是什么。使用“runApp”解决方案,必须知道该根部件是什么。 - Rémi Rousselet
2
这个不起作用。再次调用runapp什么也不会发生。 - Axes Grinds

5

从技术角度来说,这并不是一次真正的重启,但它适用于大多数情况:

// Remove any route in the stack
Navigator.of(context).popUntil((route) => false);

// Add the first route. Note MyApp() would be your first widget to the app.
Navigator.push(
  context,
  CupertinoPageRoute(builder: (context) => const MyApp()),
);

这看起来足够简洁。 - evals
我喜欢这个,这是对我的需求来说最简单和最优雅的。我把它放入了一个定时器中。 - Tom

5
我发现Hosseinrestart_app包对本地重启(不仅在Flutter层面)也非常有用。
对于每个遇到MissingPluginException错误的人,请再次在设备上重新安装应用,这意味着热重载将无法工作。该应用程序具有需要在Android / iOS应用程序中编译的本机方法。

谢谢,伙计。我觉得你答案中的包链接有问题。正确的链接是:https://pub.dev/packages/restart_app - Hossein Yousefpour
只是更新一下,该软件包已更新到v1.2.0,并支持iOS系统。 - Hossein Yousefpour

5

我想在注销后重新启动我的应用程序。 所以我使用了https://pub.dev/packages/flutter_phoenix (flutter phoenix)。 这对我很有帮助。

  1. 通过在Flutter应用程序目录中的终端上运行此命令来安装flutter_phoenix。 $ flutter pub add flutter_phoenix
  2. 在“main.dart”中导入它。
  3. 将您的根组件包装在Phoenix内部。
     runApp(
    Phoenix(
      child: MyApp()
  ));

  1. 现在你可以在需要重新启动你的应用程序时调用以下代码:
  2. Phoenix.rebirth(context)

注意: flutter_phoenix 不能在操作系统级别上重新启动应用程序,它只能在应用程序级别上重新启动。


1

我尝试了上面建议的方法,但都没有起作用,而且我正在使用getx。 所以最终我修改了被接受的答案并加入了延迟作为解决方法,现在它可以工作了。

   class RestartAppWidget extends StatefulWidget {
  RestartAppWidget({this.child});

  final Widget child;

  static void restartApp(BuildContext context) {
    context.findAncestorStateOfType<_RestartAppWidgetState>().restartApp();
  }

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

class _RestartAppWidgetState extends State<RestartAppWidget> {
  bool restarting = false;

  void restartApp() async {
    restarting = true;   // restart variable is set to true
    setState(() {});
    Future.delayed(Duration(milliseconds: 300)).then((value) {
      setState(() {
        restarting = false; //restart variable is set to false
      });
    });
    // setState(() {
    //   key = UniqueKey();
    // });
  }

  @override
  Widget build(BuildContext context) {
    if (restarting) {
      return SizedBox();   //an empty Sizedbox is displayed for 300 milliseconds you can add a loader if you want
    }
    return SizedBox(
      child: widget.child,
    );
  }
}`

将根小部件用 RestartAppWidget 包装起来。
  runApp(RestartAppWidget(
      child: MyApp(),
    ))

你可以使用这段代码在Flutter层面重新启动应用

RestartAppWidget.restartApp(Get.context);

在调用 restartApp() 之前,您只需要调用 'Get.reset()' 即可。 - Hossein Yousefpour
这似乎是一个更好的方法,很不错。 - undefined

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