本文主要介绍在 Flutter 失败时重新运行你的启动逻辑
有时,应用程序必须在启动之前运行异步函数。像加密交易工具这样的东西必须在线,所以他们会在开始时提出登录请求,在线游戏也是如此,或者在我的情况下,一个应用程序在启动时从磁盘(或网络,如果这是第一次)。将它构建到 HomeView 中会很容易,但是一些应用程序,比如我的,根据配置有 4 个不同的启动屏幕,我们不会将它构建到每个视图中。
当启动逻辑失败时,我们希望为用户提供重试的选项。因此,我们需要能够将应用程序再次置于与启动时相同的状态。我们将保持示例简单。当应用程序启动时,我们将运行异步函数。MaterialApp 的主页将是一个 StreamBuilder,它根据流值显示不同的视图。
执行
这就是我们将如何实现功能。在启动 Future 以获取重要数据时将运行。在未来,我们将在 StreamController 经历状态时向其添加值。MaterialApp 的 home 将是一个 StreamBuilder,它监听来自前面提到的控制器的流。根据来自该流的值,我们将显示不同的 UI。更具体地说,以下用户界面:
- NoData / Busy:带有加载指示器的文本(我们将使用 CircularProgressIndicator,但您也可以使用SpinKit
- 成功:黄色视图显示主页
- 错误:带有重试按钮的文本,可重新运行未来以准备好重要数据
我们将从一个基本的应用程序开始
void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Container(), ); } } 复制代码
我们想在应用程序启动时调用 Future,但我希望将 MyApp 保留为无状态小部件,因此我们将创建一个有状态包装器来为我们执行此操作。创建一个名为 stateful_wrapper.dart 的新文件。它是一个有状态的小部件,它接受一个名为 onInit 的函数和一个子小部件。覆盖 initState 函数并在覆盖中调用 onInit。
class StatefulWrapper extends StatefulWidget { final Function onInit; final Widget child; StatefulWrapper({Key key, this.onInit, this.child}) : super(key: key); _StatefulWrapperState createState() => _StatefulWrapperState(); } class _StatefulWrapperState extends State<StatefulWrapper> { @override void initState() { widget.onInit(); super.initState(); } @override Widget build(BuildContext context) { return widget.child; } } 复制代码
接下来我们将添加我们将使用的状态枚举。你应该把它放在它自己的文件中,我把它放在 main.dart 文件中作为例子
enum StartupState { Busy, Success } 复制代码
现在让我们添加将发出我们的状态的 StreamController 和将完成重要工作并将状态添加到流中的未来。
class MyApp extends StatelessWidget { final StreamController<StartupState> _startupStatus = StreamController<StartupState>(); @override Widget build(BuildContext context) { return MaterialApp( home: Container(), ); } Future getImportantData({bool isError = false}) async { _startupStatus.add(StartupState.Busy); await Future.delayed(Duration(seconds: 2)); if (isError) { _startupStatus.add(StartupState.Error); } else { _startupStatus.add(StartupState.Success); } } } 复制代码
现在我们可以将它们联系在一起。我们希望在视图初始化时调用未来,因此我们将使用我们的StatefulWrapper
并传递一个onInit
函数。我们将传递 isError true 以便我们可以遍历所有状态。UI 将是一个 Scaffold,根子StatefulWrapper
节点是我们的,而该包装器的子节点将是一个接受startupStatus
流的 StreamBuilder 。
@override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: StatefulWrapper( onInit: () => getImportantData(isError: true), child: StreamBuilder<StartupState>( stream: _startupStatus.stream, builder: (context, snapshot) { }, ), ), ), ); } 复制代码
现在我们终于可以添加我们的用户界面了。第一个 UI 位,我们将检查snapShot
hasData,或者它是否繁忙,我们将显示加载指示器。
if (!snapshot.hasData || snapshot.data == StartupState.Busy) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ Text('Show your app logo here'), CircularProgressIndicator() ], ), ); } 复制代码
然后我们要检查快照是否为错误类型。如果是,我们将返回错误消息以及可用于重试的 IconButton。
if (snapshot.hasError) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ Text('${snapshot.error} Retry?'), IconButton( icon: Icon( Icons.refresh, size: 55, ), onPressed: () { getImportantData(); }, ) ], )); } 复制代码
在构建器函数的最后,它成功了,我们将显示一个黄色容器。
return Container(color: Colors.yellow); 复制代码
这就是您可以在应用程序启动时设置简单重试的方法。如果您运行该应用程序,您将看到加载指示,完成后您将收到错误消息。点击重试按钮将重新运行 Future,因此您将再次看到加载,然后您将看到成功