【问题标题】:Flutter - Keep page static throughout lifecycle of app?Flutter - 在应用程序的整个生命周期中保持页面静态?
【发布时间】:2019-06-01 12:46:34
【问题描述】:

我创建了一个 AppDrawer 小部件来包装我的主抽屉导航并在一个地方引用它,如下所示:

class AppDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Drawer(
        child: new ListView(
          children: <Widget>[
            new ListTile(
              title: new Text("Page1"),
              trailing: new Icon(Icons.arrow_right),
              onTap: () {
                Navigator.of(context).pop();
                Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => Page1.singleInstance));
              }
            ),
            new ListTile(
              title: new Text("Page2"),
              trailing: new Icon(Icons.arrow_right),
              onTap: () {
                Navigator.of(context).pop();
                Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new Page2("Page 2")));
              }
            ),
          ]
        ),
      );
  }
}

我还创建了一个自定义 AppScaffold 小部件,它只返回一个一致的 AppBar、我的自定义 AppDrawer 和正文:

class AppScaffold extends StatelessWidget {
  final Widget body;
  final String pageTitle;

  AppScaffold({this.body, this.pageTitle});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(title: new Text(pageTitle), backgroundColor: jet),
      drawer: AppDrawer(), 
      body: body
    );
  }
}

我创建了两个页面:Page1 和 Page2。它们现在很简单,看起来像这样:

class Page1 extends StatelessWidget {
  final String pageText;

  Page1(this.pageText);

  static Page1 get singleInstance => Page1("Page1");

  Widget build(BuildContext context) {
    return AppScaffold(
      pageTitle: this.pageText,
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            Center(child: SomeCustomWidget())
          ],
        )
      ),
    );
  }
}

class Page2 extends StatelessWidget {
  final String pageText;

  Page2(this.pageText);

  @override
  Widget build(BuildContext context) {
    return AppScaffold(
      pageTitle: this.pageText,
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            Center(child: SomeOtherCustomWidget())
          ],
        )
      ),
    );
  }
}

当我运行我的应用程序时,我可以正确地看到导航栏和抽屉。我可以单击抽屉中的链接在我的页面之间导航。但是,每次我导航到一个页面时,该页面上的所有小部件都会重置为其初始状态。我想确保小部件不会被重置。另一种思考方式是:我只希望在应用程序的整个生命周期中每个页面都有一个实例,而不是在用户导航到它们时创建新的实例。

我尝试创建一个 Page1 的静态实例,当触发 onTap 事件时 Drawer 使用该实例,但这不起作用。我想错了吗?我需要转换为有状态小部件吗?

【问题讨论】:

    标签: flutter


    【解决方案1】:

    哦,你要请客了...这会有点长(抱歉),但请在做出决定和采取行动之前阅读所有内容 - 我保证我会节省你的时间。

    这个问题有很多不同的解决方案,但总的来说,您要问的是状态管理(这实际上是软件工程,更多信息在这里 - Understanding state management, and why you never will)。

    我会尽力解释你的具体情况……

    问题:

    Navigator 视为应用程序状态的List,您可以通过其各种方法(即pop()push() 等)对其进行操作,记住这一点很清楚正在发生什么 -在按下按钮时,您实际上是在删除当前状态(页面),然后您正在推送您的状态(页面)的新实例。

    解决方案:

    正如我所说,这个问题有很多解决方案,例如,您可能想将状态(您对特定“页面”所做的更改)存储在 var 的某处,并在导航时注入该 var “ pages”,在创建该页面的新实例时,但您很快就会遇到其他问题。这就是为什么我认为没有人可以为这个问题提供简单的解决方案......

    首先,我可以建议您阅读有关此问题的一些有用信息: Flutter official docs on state management - 当你进入“选项”部分时,有趣的部分就开始了,很快就会让人不知所措,但不要害怕:P

    请务必阅读我的答案开头提到的中篇文章,我发现它真的很有帮助。

    这些阅读内容足以帮助您做出决定,此外还有大量关于 Medium 和 YouTube 视频的文章涉及 Flutter 的状态管理问题(甚至有一些来自框架作者)——只是搜索“使用 Flutter 进行状态管理”。

    现在我个人的意见

    如果这是一个非常简单的用例并且您不打算发展(相信我几乎从来没有),您可以将StatefulWidgets 与setState() 结合使用,也许InheritedWidget (用于树下的依赖注入,或者像 React 人一样将其称为“lifting state up”)。或者代替上面的,也许看看scoped_model,它为你抽象了所有这些(虽然,我没有玩过它)。

    我现在在实际项目中使用的是blocflutter_bloc(BLoC = 业务逻辑组件),我不会详细介绍它,但基本上它采用了scoped_model 的概念更进一步,而不会使抽象过于复杂。 bloc 负责抽象出应用程序的“业务逻辑”,flutter_bloc 负责在 UI 中“注入”状态并对状态变化做出反应(Flutter 官方对此的立场是 UI = f(State))。 BLoC 具有输入和输出,它将事件作为输入(可以是用户输入或其他任何类型的事件)并产生状态。总之就是关于bloc。 一个很好的入门方法是BLoC's official documentation我强烈推荐它。只需浏览所有内容。

    (p.s. 这可能是我个人的看法,但最终 Flutter 中的状态管理都是基于某种形式的使用 InheritedWidgetsetState() 来响应用户输入或其他应该改变的外部因素应用程序状态,所以我认为 BLoC 模式真的很适合抽象那些:P)

    【讨论】:

    • UI = f(State) 是 Fl​​utter 设计的核心,而不是立场。这是解释 Flutter 工作原理的好方法。但是,即使是忠实粉丝,我也不建议从 BLoC 开始。有很多陷阱(文本输入状态、后退按钮、确认对话框、验证、数据保存通知),并且在复选框单击之类的事情之间存在挂起状态,其值传播是一团糟。你没有提到Provider,如果你不打算重用AngularDart等的代码,这对于简单的架构来说可能是一个更容易的开始。
    • @J.Williams 好点,感谢您的澄清。我确实故意省略了 Provider,因为它已经在我提供的链接中(不是双关语),但它绝对值得研究。我会把它放在 scoped_model 和 BLoC 之间。
    • 这正是我想要的。谢谢!我设法连接了 Bloc 库,现在我的应用程序正在跨页面视图持久化数据:)
    猜你喜欢
    • 1970-01-01
    • 2019-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多