我正在构建Flutter应用程序,并且试图将我的头转向导航和状态。我在下面构建了一个非常简单的应用程序,该应用程序有两个页面,两个页面上都带有增量按钮。它们都共享一个Scaffold
定义,因此两个页面上都有一个一致的导航抽屉。
基本上我想要的功能是FirstPage
和SecondPage
是单例。因此,如果您将FirstPage
上的计数器增加几次,请转到SecondPage
,然后通过抽屉(而不是返回按钮)返回FirstPage
,{{1} }的计数器仍应增加。
现在,如果执行此操作,则由于FirstPage
似乎正在创建FirstPage
的新实例。另外,如果您在Navigation.push()
上并出于某些原因使用抽屉再次单击“第一页”,则不应丢失状态。
我看到Flutter开发人员在这里提到了“ non-linear navigation”一词,这让我认为这样的事情是可能的。感谢您的帮助。
FirstPage
答案 0 :(得分:1)
我建议不要制作一个StatelessWidget来构建一个脚手架,并以一个子控件作为参数,而不是扩展Scaffold。通过按照自己的方式进行操作,您不会得到context
或其他任何内容,因此通常建议在Flutter中采用封装而不是继承。
除了这样做,您还可以分离前进/后退逻辑。您可以先执行push(secondPage)
,然后pop
回到首页,而不是每次都推送新页面。颤抖的导航是一个堆栈;每次按下按钮时,它都会添加到顶部,并且每次弹出按钮时,都将删除顶部元素。
如果您希望SecondPage保留其计数器,那将变得有些困难。您在这里有两个选择-第一个选择是在构建它时传递初始值。然后它将在内部递增该值,然后在您调用Navigator.pop(context, [value])
时将其传递回去。它将保存为第一个屏幕的状态,然后再次按下页面时,您将传递新的初始值。
第二个选项是将计数器的值保留在高于第二个页面的水平上(使用StatefulWidget并可能使用InheritedWidget)-即,在任何包含您的导航器/ materialapp的小部件中。不过,这可能有点矫kill过正。
编辑:为响应OP的评论,已经明确表明实际上存在多个页面,而不仅仅是计数器,信息更为复杂。
有多种方法可以处理此问题;一种是使用Redux作为策略来实现该应用;与flutter_persist一起使用时,它可以成为持久状态的强大工具,我相信还有一个插件可以与firestore集成以进行云备份(对此不确定)。
我自己并不是redux的忠实拥护者;在我看来,这增加了很多开销,这与flutter的简单性背道而驰(尽管我可以看到对于大型应用程序或团队而言,它如何创建一致性)。
一个更简单的选项如编辑前所述;使用InheritedWidget包含更高级别的状态。我在下面做了一个简单的例子:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => new MyAppState();
}
class MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return CounterInfo(
child: new MaterialApp(
routes: {
"/": (context) => new FirstPage(),
"/second": (context) => new SecondPage(),
},
),
);
}
}
class _InheritedCounterInfo extends InheritedWidget {
final CounterInfoState data;
_InheritedCounterInfo({
Key key,
@required this.data,
@required Widget child,
}) : super(key: key, child: child);
@override
bool updateShouldNotify(_InheritedCounterInfo old) => true;
}
class CounterInfo extends StatefulWidget {
final Widget child;
const CounterInfo({Key key, this.child}) : super(key: key);
static CounterInfoState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_InheritedCounterInfo) as _InheritedCounterInfo).data;
}
@override
State<StatefulWidget> createState() => new CounterInfoState();
}
class CounterInfoState extends State<CounterInfo> {
int firstCounter = 0;
int secondCounter = 0;
void incrementFirst() {
setState(() {
firstCounter++;
});
}
void incrementSecond() {
setState(() {
secondCounter++;
});
}
@override
Widget build(BuildContext context) => new _InheritedCounterInfo(data: this, child: widget.child);
}
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counterInfo = CounterInfo.of(context);
return new MyScaffold(
title: "First page",
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("${counterInfo.firstCounter}"),
RaisedButton(onPressed: counterInfo.incrementFirst, child: Icon(Icons.add)),
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counterInfo = CounterInfo.of(context);
return new MyScaffold(
title: "Second page",
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("${counterInfo.secondCounter}"),
RaisedButton(onPressed: counterInfo.incrementSecond, child: Icon(Icons.add)),
],
),
),
);
}
}
class MyScaffold extends StatelessWidget {
final String title;
final Widget child;
const MyScaffold({Key key, this.title, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: new Text(title)),
drawer: Drawer(
child: ListView(padding: EdgeInsets.zero, children: <Widget>[
SizedBox(height: 100.0),
ListTile(
title: Text("First Page"),
onTap: () => Navigator.of(context).pushReplacementNamed("/"),
),
ListTile(title: Text("Second Page"), onTap: () => Navigator.of(context).pushReplacementNamed("/second")),
])),
body: child,
);
}
}
无论您是采用这种方式还是考虑使用Redux,this都是一个有用的网站,具有各种Flutter体系结构模型,包括使用继承的小部件。