flutter - Flutter:持久化页面状态
问题描述
即使在阅读了 this和this之后,我似乎仍然无法在 Flutter 中存储页面状态。
我已经构建了一个示例应用程序,它有一个名为的主页MyHomePage
和一个名为SecondPage
. MyHomePage
有一个浮动操作按钮,通过SecondPage
显示Navigator.push(...)
。第二页包含一个带有指定控制器的文本字段。我想在关闭并重新打开后保留文本字段的文本SecondPage
。
我已经尝试了各种设置桶、页面状态和键的组合(受上面链接的启发),但我无法让它工作。
此外,我想自动存储整个页面状态 - 无需手动写入/检索每个值(以防我在页面上有很多文本字段)。
这是我的代码:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
class MyApp extends StatelessWidget {
final PageStorageBucket _bucket = new PageStorageBucket();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PageStorage(
bucket: _bucket,
child: MyHomePage(),
)
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage()));
}
}
class SecondPage extends StatefulWidget {
@override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
final _aController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
key: mykey,
autofocus: true,
),
)
);
}
}
编辑:
根据 Ajay 的回答,我能够大大简化工作代码。事实证明,为了手动保存小部件状态,您所需要的只是一个与实例PageStorageBucket
相结合的ValueKey
实例。
以下是我对 Ajay 的代码所做的修改:
- 删除了 after_layout 插件(
initState
方法就足够了)。 - 删除了全局
PageStorageKey
实例(将其替换ValueKey
为页面中需要使用它的本地实例)。 - 删除全局实例
PageStorageBucket
并将其替换为最终实例 inMyApp
,该实例通过构造函数属性传递给需要它的页面。PageStorage
从组件树中移除。
这是生成的代码(最简单的工作形式):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final bucket = PageStorageBucket();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(bucket: bucket,),
);
}
}
class MyHomePage extends StatefulWidget {
final PageStorageBucket bucket;
const MyHomePage({Key key, this.bucket}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => new SecondPage(bucket: widget.bucket,)));
}
}
class SecondPage extends StatefulWidget {
final PageStorageBucket bucket;
const SecondPage({Key key, this.bucket}) : super(key: key);
@override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
static const KEY_A = ValueKey("secondPage.A");
final _aController = TextEditingController();
@override
void initState() {
super.initState();
_aController.addListener(_updateValue);
String value = widget.bucket.readState(context, identifier: KEY_A) ?? "";
_aController.text = value;
}
_updateValue() {
widget.bucket.writeState(context, _aController.text, identifier: KEY_A);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
autofocus: true,
),
),
);
}
}
解决方案
您还需要读取和写入状态。
查看下面的代码。
注意:我使用after_layout来初始化文本控制器。
import 'package:after_layout/after_layout.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
final PageStorageBucket _bucket = new PageStorageBucket();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PageStorage(
bucket: _bucket,
child: MyHomePage(),
));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => new SecondPage()));
}
}
class SecondPage extends StatefulWidget {
@override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage>
with AfterLayoutMixin<SecondPage> {
final _aController = TextEditingController();
@override
void initState() {
super.initState();
_aController.addListener(_updateValue);
}
@override
void afterFirstLayout(BuildContext context) {
String value =
_bucket.readState(context, identifier: ValueKey(mykey)) ?? "";
print(value);
_aController.text = value;
}
_updateValue() {
_bucket.writeState(context, _aController.text, identifier: ValueKey(mykey));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
key: mykey,
autofocus: true,
),
),
);
}
}
推荐阅读
- azure-data-factory - 如何将数据从 csv 复制到 Azure SQL Server 表?
- javascript - 仅打印没有分页或任何其他内容的 ag-grid 数据的有效方法
- parsing - 以下标记定义永远无法匹配,因为先前的标记匹配相同的输入:INT,STRING
- asp.net-web-api - 从 asp.net webAPI 返回带有非拉丁字符的文件
- angular - 如何以角度调试测试?
- sql - Postgres SQL 如何在 SQL 数据库上透视表
- android - IONIC http.get 挂在模拟器或设备上,但在浏览器上工作
- flutter - firebase_crashlytics 在颤振中使用 try / catch 时是否有效?
- angularjs - 错误 Webpack: Uncaught ReferenceError: require is not defined
- javascript - p5.js 控制我播放的帧数