页面导航时保持状态:如何持久化第二页的地点选择列表状态?
这是个很常见的Flutter页面状态丢失问题,本质是因为你从第二页pop回第一页时,第二页的Widget实例被销毁了,再次push时会重新创建,所以之前的选择状态就没了。下面给你几种不同场景下的解决方案,你可以根据自己的需求选择:
方案一:用AutomaticKeepAliveClientMixin保持页面状态
这是Flutter自带的状态保持方案,适合页面只是被push/pop、没有被彻底移除路由栈的场景。
你只需要让第二页的State类混入AutomaticKeepAliveClientMixin,并实现wantKeepAlive返回true,同时在build方法里调用super.build(context):
class SecondPage extends StatefulWidget { final List<String> initialSelected; // 接收第一页传来的已选地点 const SecondPage({Key? key, required this.initialSelected}) : super(key: key); @override State<SecondPage> createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> with AutomaticKeepAliveClientMixin { late List<String> _selectedLocations; @override void initState() { super.initState(); _selectedLocations = List.from(widget.initialSelected); } // 开启状态保持 @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { // 必须调用super.build,否则状态保持不生效 super.build(context); return Scaffold( appBar: AppBar(title: const Text('选择地点')), body: ListView( children: [ for (final location in ['北京', '上海', '广州', '深圳']) CheckboxListTile( title: Text(location), value: _selectedLocations.contains(location), onChanged: (value) { setState(() { value == true ? _selectedLocations.add(location) : _selectedLocations.remove(location); }); }, ), ], ), floatingActionButton: FloatingActionButton( onPressed: () => Navigator.pop(context, _selectedLocations), child: const Icon(Icons.save), ), ); } }
注意:如果你的路由操作是pushReplacement或者页面被从路由栈中移除,这个方案就不生效了。
方案二:全局状态管理(以Riverpod为例)
如果你的应用比较复杂,或者多个页面需要共享这个选择状态,全局状态管理是更合适的方案。它会把状态保存在独立于Widget树的容器里,不管页面怎么重建,状态都不会丢失。
首先定义一个状态Provider:
final selectedLocationsProvider = StateProvider<List<String>>((ref) => []);
第一页读取并展示状态:
class FirstPage extends ConsumerWidget { const FirstPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final selectedLocations = ref.watch(selectedLocationsProvider); return Scaffold( appBar: AppBar(title: const Text('首页')), body: Wrap( children: selectedLocations.map((loc) => Chip(label: Text(loc))).toList(), ), floatingActionButton: FloatingActionButton( onPressed: () => Navigator.push( context, MaterialPageRoute(builder: (_) => const SecondPage()), ), child: const Icon(Icons.add), ), ); } }
第二页修改全局状态:
class SecondPage extends ConsumerWidget { const SecondPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final selectedLocations = ref.watch(selectedLocationsProvider); return Scaffold( appBar: AppBar(title: const Text('选择地点')), body: ListView( children: [ for (final location in ['北京', '上海', '广州', '深圳']) CheckboxListTile( title: Text(location), value: selectedLocations.contains(location), onChanged: (value) { ref.read(selectedLocationsProvider.notifier).update((state) { final newList = List.from(state); value == true ? newList.add(location) : newList.remove(location); return newList; }); }, ), ], ), floatingActionButton: FloatingActionButton( onPressed: () => Navigator.pop(context), child: const Icon(Icons.save), ), ); } }
方案三:在第一页保存状态,跳转时传递
这是最基础的方案,适合只有两个页面交互的简单场景,不需要额外依赖。
第一页保存已选状态,跳转时把当前状态传递给第二页,等待返回后更新状态:
class FirstPage extends StatefulWidget { const FirstPage({Key? key}) : super(key: key); @override State<FirstPage> createState() => _FirstPageState(); } class _FirstPageState extends State<FirstPage> { List<String> _selectedLocations = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('首页')), body: Wrap( children: _selectedLocations.map((loc) => Chip(label: Text(loc))).toList(), ), floatingActionButton: FloatingActionButton( onPressed: () async { final result = await Navigator.push<List<String>>( context, MaterialPageRoute( builder: (_) => SecondPage(initialSelected: _selectedLocations), ), ); if (result != null) { setState(() => _selectedLocations = result); } }, child: const Icon(Icons.add), ), ); } }
第二页接收初始状态并修改,返回时把新状态带回第一页:
class SecondPage extends StatefulWidget { final List<String> initialSelected; const SecondPage({Key? key, required this.initialSelected}) : super(key: key); @override State<SecondPage> createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> { late List<String> _currentSelected; @override void initState() { super.initState(); _currentSelected = List.from(widget.initialSelected); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('选择地点')), body: ListView( children: [ for (final location in ['北京', '上海', '广州', '深圳']) CheckboxListTile( title: Text(location), value: _currentSelected.contains(location), onChanged: (value) { setState(() { value == true ? _currentSelected.add(location) : _currentSelected.remove(location); }); }, ), ], ), floatingActionButton: FloatingActionButton( onPressed: () => Navigator.pop(context, _currentSelected), child: const Icon(Icons.save), ), ); } }
方案四:本地持久化(以SharedPreferences为例)
如果需要即使重启APP也能保留选择状态,就需要把状态保存到本地存储。这里用SharedPreferences做示例,你也可以用Hive、SQLite等更强大的存储方案。
首先添加依赖到pubspec.yaml:
dependencies: shared_preferences: ^2.2.2
第二页加载并保存本地状态:
class SecondPage extends StatefulWidget { const SecondPage({Key? key}) : super(key: key); @override State<SecondPage> createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> { late List<String> _selectedLocations; final _prefs = SharedPreferences.getInstance(); @override void initState() { super.initState(); _loadSavedLocations(); } Future<void> _loadSavedLocations() async { final prefs = await _prefs; setState(() { _selectedLocations = prefs.getStringList('selected_locations') ?? []; }); } Future<void> _saveAndReturn() async { final prefs = await _prefs; await prefs.setStringList('selected_locations', _selectedLocations); Navigator.pop(context, _selectedLocations); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('选择地点')), body: ListView( children: [ for (final location in ['北京', '上海', '广州', '深圳']) CheckboxListTile( title: Text(location), value: _selectedLocations.contains(location), onChanged: (value) { setState(() { value == true ? _selectedLocations.add(location) : _selectedLocations.remove(location); }); }, ), ], ), floatingActionButton: FloatingActionButton( onPressed: _saveAndReturn, child: const Icon(Icons.save), ), ); } }
第一页也可以从本地加载初始状态:
class FirstPage extends StatefulWidget { const FirstPage({Key? key}) : super(key: key); @override State<FirstPage> createState() => _FirstPageState(); } class _FirstPageState extends State<FirstPage> { late List<String> _selectedLocations; final _prefs = SharedPreferences.getInstance(); @override void initState() { super.initState(); _loadSavedLocations(); } Future<void> _loadSavedLocations() async { final prefs = await _prefs; setState(() { _selectedLocations = prefs.getStringList('selected_locations') ?? []; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('首页')), body: Wrap( children: _selectedLocations.map((loc) => Chip(label: Text(loc))).toList(), ), floatingActionButton: FloatingActionButton( onPressed: () async { final result = await Navigator.push<List<String>>( context, MaterialPageRoute(builder: (_) => const SecondPage()), ); if (result != null) { setState(() => _selectedLocations = result); } }, child: const Icon(Icons.add), ), ); } }
总结选择建议
- 简单页面跳转保持状态:选方案一;
- 多页面共享状态:选方案二;
- 仅两个页面交互:选方案三;
- 需要持久化到本地(重启APP不丢失):选方案四。
内容的提问来源于stack exchange,提问作者David De Jesus

