我创建了一个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())
],
)
),
);
}
}
运行应用程序时,可以正确看到导航栏和抽屉。我可以单击抽屉中的链接在我的页面之间导航。但是,每次我导航到页面时,该页面上的所有小部件都会重置为其初始状态。我想确保小部件不会重置。另一种思考方式是:我只希望在应用程序的整个生命周期中每个页面的一个实例,而不是每当用户导航到它们时都创建它们的新实例。
我尝试创建触发onTap事件时Drawer使用的Page1的静态实例,但这不起作用。我在想这个吗?我需要转换为有状态的小部件吗?
答案 0 :(得分:1)
哦,你要请客了……这会很长(抱歉),但是请在决定和采取行动之前先阅读所有内容-我保证可以节省您的时间。
有很多解决此问题的方法,但是总的来说,您要问的是状态管理(这实际上是软件工程,更多信息在这里Understanding state management, and why you never will)。
我会尽力解释您的具体情况。
问题:
将Navigator
视为应用程序状态的List
,您可以通过其各种方法(例如pop()
,push()
等进行操作)请注意,正在发生的事情很明显-按下按钮实际上是在删除当前状态(页面),然后紧接着按下状态的新实例(页面)。
解决方案:
正如我所说,有许多解决此问题的方法,例如,您可能会想将状态(对特定“页面”所做的更改)存储在var中,并在在“页”,在创建该页面的新实例时,但是您很快就会遇到其他问题。这就是为什么我认为没有人可以为这个问题提供简单的解决方案...
首先,我是否可以建议您阅读一些有用的信息: Flutter official docs on state management-当您进入本文的“选项”部分时,有趣的部分开始了,并且很快就会变得势不可挡,但请不要担心:P
请务必也阅读答案开头提到的中篇文章,我发现它确实很有帮助。
这些内容足以帮助您做出决定,此外,在中型和YouTube视频上还有很多文章涉及Flutter的状态管理问题(甚至有一些来自框架作者)-搜索“使用Flutter进行状态管理”。
现在是我个人的意见:
如果这是一个非常简单的用例,并且您不打算进行扩展(请相信我,这几乎是不可能的),则可以将StatefulWidget
与setState()
结合使用,也许InheritedWidget
(用于将依赖项注入树中,或者像React的人一样称其为“ lifting state up”)。或者代替上面的内容,也许看看scoped_model,它可以为您抽象所有这些内容(因此,我还没有玩过)。
我现在在一个实际项目中使用的是bloc
和flutter_bloc
(BLoC =业务逻辑组件),我将不对其进行详细介绍,但基本上它采用了scoped_model
更进一步,而不会使抽象过度复杂。 bloc
负责抽象出应用程序的“业务逻辑”,flutter_bloc
负责在用户界面中“注入”状态并对状态变化做出反应(在此问题上,Flutter的官方立场是{{3 }})。
BLoC具有输入和输出,它将事件作为输入(可以是用户输入,或者实际上是任何类型的事件),并产生状态。总结一下,就是bloc
。
UI = f(State)
是上手的好方法。 我强烈推荐它。请仔细阅读所有内容。
(ps这可能是我个人的观点,但最终,Flutter的状态管理全部基于某种形式,使用InheritedWidget
和setState()
来响应用户输入或其他外部应该改变应用程序状态的因素,所以我认为BLoC模式确实可以抽象那些:P)