Flutter Web:刷新浏览器后如何防止ValueNotifier被清除?
Flutter Web刷新后保留ValueNotifier状态的解决方案
ValueNotifier是内存级别的状态管理,浏览器刷新相当于重启Flutter应用,内存中的状态会被完全清除;而SharedPreferences这类持久化存储是存在浏览器本地的,所以不会丢失。要解决这个问题,核心思路是把ValueNotifier的状态和持久化存储绑定,实现状态的自动保存与恢复。
方案1:封装带持久化的ValueNotifier子类
自定义一个继承自ValueNotifier的类,在初始化时从存储读取状态,状态变化时自动保存到存储。
代码实现
import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; class PersistedValueNotifier<T> extends ValueNotifier<T> { final String storageKey; final SharedPreferences prefs; final T Function(dynamic) deserialize; final dynamic Function(T) serialize; PersistedValueNotifier({ required this.storageKey, required this.prefs, required T initialValue, required this.deserialize, required this.serialize, }) : super(initialValue) { // 初始化时从存储加载状态 _loadState(); } Future<void> _loadState() async { final storedData = prefs.get(storageKey); if (storedData != null) { value = deserialize(storedData); } } @override set value(T newValue) { super.value = newValue; // 状态变化时自动保存到存储 _saveState(newValue); } Future<void> _saveState(T value) async { final data = serialize(value); if (data is String) { await prefs.setString(storageKey, data); } else if (data is int) { await prefs.setInt(storageKey, data); } else if (data is bool) { await prefs.setBool(storageKey, data); } else if (data is double) { await prefs.setDouble(storageKey, data); } else if (data is List<String>) { await prefs.setStringList(storageKey, data); } } }
使用示例
1. 初始化SharedPreferences并创建持久化Notifier
void main() async { WidgetsFlutterBinding.ensureInitialized(); final prefs = await SharedPreferences.getInstance(); runApp(MyApp(prefs: prefs)); } class MyApp extends StatelessWidget { final SharedPreferences prefs; const MyApp({super.key, required this.prefs}); @override Widget build(BuildContext context) { // 示例:持久化计数器状态 final counterNotifier = PersistedValueNotifier<int>( storageKey: 'app_counter', prefs: prefs, initialValue: 0, deserialize: (val) => val as int, serialize: (val) => val, ); // 示例:持久化自定义对象(如User) final userNotifier = PersistedValueNotifier<User>( storageKey: 'current_user', prefs: prefs, initialValue: User('Guest', 20), deserialize: (val) => User.fromJson(jsonDecode(val as String)), serialize: (user) => jsonEncode(user.toJson()), ); return MaterialApp.router( routerConfig: GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => HomePage( counterNotifier: counterNotifier, userNotifier: userNotifier, ), ), ], ), ); } } // 自定义User类示例 class User { final String name; final int age; User(this.name, this.age); factory User.fromJson(Map<String, dynamic> json) => User(json['name'], json['age']); Map<String, dynamic> toJson() => {'name': name, 'age': age}; }
2. 在页面中使用
class HomePage extends StatelessWidget { final PersistedValueNotifier<int> counterNotifier; final PersistedValueNotifier<User> userNotifier; const HomePage({ super.key, required this.counterNotifier, required this.userNotifier, }); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ValueListenableBuilder<int>( valueListenable: counterNotifier, builder: (context, count, _) => Text('计数器:$count'), ), const SizedBox(height: 20), ValueListenableBuilder<User>( valueListenable: userNotifier, builder: (context, user, _) => Text('当前用户:${user.name},${user.age}岁'), ), ], ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => counterNotifier.value++, child: const Icon(Icons.add), ), const SizedBox(height: 10), FloatingActionButton( onPressed: () => userNotifier.value = User('New User', 25), child: const Icon(Icons.person), ), ], ), ); } }
方案2:全局单例状态管理
把ValueNotifier放在全局单例中,在单例初始化时从存储恢复状态,同时监听状态变化自动保存。
代码实现
import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; class AppGlobalState { static final AppGlobalState _instance = AppGlobalState._internal(); late SharedPreferences _prefs; // 定义全局的ValueNotifier late ValueNotifier<int> counter; late ValueNotifier<User> currentUser; factory AppGlobalState() => _instance; AppGlobalState._internal(); // 初始化方法,必须在runApp前调用 Future<void> init() async { _prefs = await SharedPreferences.getInstance(); _initCounter(); _initCurrentUser(); } void _initCounter() { final storedCount = _prefs.getInt('counter') ?? 0; counter = ValueNotifier(storedCount); // 监听变化,自动保存 counter.addListener(() { _prefs.setInt('counter', counter.value); }); } void _initCurrentUser() { final storedUser = _prefs.getString('current_user'); final initialUser = storedUser != null ? User.fromJson(jsonDecode(storedUser)) : User('Guest', 20); currentUser = ValueNotifier(initialUser); currentUser.addListener(() { _prefs.setString('current_user', jsonEncode(currentUser.value.toJson())); }); } } // User类同方案1 class User { final String name; final int age; User(this.name, this.age); factory User.fromJson(Map<String, dynamic> json) => User(json['name'], json['age']); Map<String, dynamic> toJson() => {'name': name, 'age': age}; }
使用示例
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 初始化全局状态 await AppGlobalState().init(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const HomePage(), ), ], ), ); } } class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { final globalState = AppGlobalState(); return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ValueListenableBuilder<int>( valueListenable: globalState.counter, builder: (context, count, _) => Text('计数器:$count'), ), const SizedBox(height: 20), ValueListenableBuilder<User>( valueListenable: globalState.currentUser, builder: (context, user, _) => Text('当前用户:${user.name},${user.age}岁'), ), ], ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => globalState.counter.value++, child: const Icon(Icons.add), ), const SizedBox(height: 10), FloatingActionButton( onPressed: () => globalState.currentUser.value = User('New User', 25), child: const Icon(Icons.person), ), ], ), ); } }
核心原理
浏览器刷新会销毁Flutter应用的所有内存对象,包括ValueNotifier。通过将状态持久化到浏览器本地存储(SharedPreferences本质是浏览器的localStorage),在应用重启(刷新后)时从存储中读取数据并恢复到ValueNotifier,同时监听状态变化实时保存,就能实现刷新后状态不丢失的效果。
内容的提问来源于stack exchange,提问作者farhad rezvani
相关产品推荐
相关产品推荐

