如何使用Flutter Bloc实现应用关闭后的状态与数据持久化?
解决Flutter Bloc应用重启后状态丢失的问题
要实现Bloc状态的持久化,核心是将状态保存到本地存储,并在Bloc初始化时读取恢复状态。以下是基于shared_preferences(轻量本地存储方案)的完整解决方案:
步骤1:添加依赖
在pubspec.yaml中加入shared_preferences依赖:
dependencies: flutter: sdk: flutter flutter_bloc: ^8.1.3 shared_preferences: ^2.2.2
执行flutter pub get安装依赖。
步骤2:改造状态类,支持序列化
给状态类添加唯一标识,方便将状态转换为可存储的字符串:
abstract class LocationState {} class GPSState extends LocationState { final String type = "gps"; } // 重命名子类避免与父类同名冲突 class ManualLocationState extends LocationState { final String type = "location"; }
步骤3:修改Bloc,实现状态持久化逻辑
在Bloc中添加读取本地状态和保存状态到本地的逻辑,同时改用新版Bloc的on语法处理事件:
class LocationBloc extends Bloc<LocationEvent, LocationState> { final Future<SharedPreferences> _prefs = SharedPreferences.getInstance(); static const String _stateStorageKey = "location_selected_state"; LocationBloc() : super(GPSState()) { // 初始化时加载本地保存的状态 _loadSavedState(); // 处理事件并持久化状态 on<LocationEvent>((event, emit) async { LocationState newState; switch (event) { case LocationEvent.selectGPS: newState = GPSState(); break; case LocationEvent.selectLocation: newState = ManualLocationState(); break; } emit(newState); await _saveStateToLocal(newState); }); } // 从本地存储加载状态 Future<void> _loadSavedState() async { final prefs = await _prefs; final savedStateType = prefs.getString(_stateStorageKey); if (savedStateType != null) { LocationState restoredState; switch (savedStateType) { case "gps": restoredState = GPSState(); break; case "location": restoredState = ManualLocationState(); break; default: restoredState = GPSState(); } emit(restoredState); } } // 将状态保存到本地存储 Future<void> _saveStateToLocal(LocationState state) async { final prefs = await _prefs; if (state is GPSState) { await prefs.setString(_stateStorageKey, state.type); } else if (state is ManualLocationState) { await prefs.setString(_stateStorageKey, state.type); } } }
步骤4:修复UI中的RadioListTile选中问题
原代码中直接用new GPSState()作为Radio的value,会导致与Bloc中的state对象引用不匹配,选中状态异常。改用状态标识字符串作为value:
// SettingsScreen中的RadioListTile修改 RadioListTile( title: const Text('GPS'), value: "gps", groupValue: state is GPSState ? "gps" : "location", onChanged: (value) { context.read<LocationBloc>().add(LocationEvent.selectGPS); }, ), RadioListTile( title: const Text('Location'), value: "location", groupValue: state is ManualLocationState ? "location" : "gps", onChanged: (value) { context.read<LocationBloc>().add(LocationEvent.selectLocation); }, ),
完整修改后的代码
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: BlocProvider( create: (context) => LocationBloc(), child: const HomeScreen(), ), ); } } enum LocationEvent { selectGPS, selectLocation, } abstract class LocationState {} class GPSState extends LocationState { final String type = "gps"; } class ManualLocationState extends LocationState { final String type = "location"; } class LocationBloc extends Bloc<LocationEvent, LocationState> { final Future<SharedPreferences> _prefs = SharedPreferences.getInstance(); static const String _stateStorageKey = "location_selected_state"; LocationBloc() : super(GPSState()) { _loadSavedState(); on<LocationEvent>((event, emit) async { LocationState newState; switch (event) { case LocationEvent.selectGPS: newState = GPSState(); break; case LocationEvent.selectLocation: newState = ManualLocationState(); break; } emit(newState); await _saveStateToLocal(newState); }); } Future<void> _loadSavedState() async { final prefs = await _prefs; final savedStateType = prefs.getString(_stateStorageKey); if (savedStateType != null) { LocationState restoredState; switch (savedStateType) { case "gps": restoredState = GPSState(); break; case "location": restoredState = ManualLocationState(); break; default: restoredState = GPSState(); } emit(restoredState); } } Future<void> _saveStateToLocal(LocationState state) async { final prefs = await _prefs; if (state is GPSState) { await prefs.setString(_stateStorageKey, state.type); } else if (state is ManualLocationState) { await prefs.setString(_stateStorageKey, state.type); } } } class HomeScreen extends StatelessWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Home'), ), body: BlocBuilder<LocationBloc, LocationState>( builder: (context, state) { return Center( child: Text( state is GPSState ? 'GPS' : 'Location', style: const TextStyle(fontSize: 24), ), ); }, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const SettingsScreen()), ); }, child: const Icon(Icons.settings), ), ); } } class SettingsScreen extends StatelessWidget { const SettingsScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Settings'), ), body: BlocBuilder<LocationBloc, LocationState>( builder: (context, state) { return Column( children: [ RadioListTile( title: const Text('GPS'), value: "gps", groupValue: state is GPSState ? "gps" : "location", onChanged: (value) { context.read<LocationBloc>().add(LocationEvent.selectGPS); }, ), RadioListTile( title: const Text('Location'), value: "location", groupValue: state is ManualLocationState ? "location" : "gps", onChanged: (value) { context.read<LocationBloc>().add(LocationEvent.selectLocation); }, ), ], ); }, ), ); } }
关键说明
- 对于复杂状态(包含多个字段),可以结合
json_serializable将状态序列化为JSON字符串存储; - 状态变更后立即保存到本地,确保应用退出前状态已持久化;
- Bloc初始化时优先加载本地状态,保证重启应用后恢复用户之前的选择。
内容的提问来源于stack exchange,提问作者Auvee
相关产品推荐
相关产品推荐

