Flutter Web Navigator 2.0刷新后数据丢失问题求助
问题根源
Web端刷新时,RouterDelegate会被重新实例化,内存中的setup、createAccountData等状态会全部丢失。而setNewRoutePath方法会根据当前路由信息重建状态,但你当前的路由信息未包含表单数据,因此导致状态被重置。
解决方案
方案一:用本地存储持久化临时表单数据
适合存储密码这类敏感数据,且不会将数据暴露在URL中。
- 添加依赖
在pubspec.yaml中引入本地存储库:
dependencies: shared_preferences: ^2.2.2
- 初始化时恢复状态
在RouterDelegate的initState中读取本地存储的临时数据:
@override void initState() { super.initState(); _restoreTempAccountData(); } Future<void> _restoreTempAccountData() async { final prefs = await SharedPreferences.getInstance(); // 恢复setup状态 setup = prefs.getBool('setup') ?? false; // 恢复表单字段 final name = prefs.getString('account_name'); final email = prefs.getString('account_email'); final telephone = prefs.getString('account_telephone'); final password = prefs.getString('account_password'); final country = prefs.getString('account_country'); if (name != null && email != null && telephone != null && password != null && country != null) { createAccountData = { 'name': name, 'email': email, 'telephone': telephone, 'password': password, 'country': country, }; } notifyListeners(); }
- 提交时保存状态
修改_handleSetUpTapped方法,将表单数据存入本地存储:
void _handleSetUpTapped(Map<String, String?> data) async { setup = true; createAccountData = data; final prefs = await SharedPreferences.getInstance(); // 保存状态和表单数据 await prefs.setBool('setup', true); await prefs.setString('account_name', data['name']!); await prefs.setString('account_email', data['email']!); await prefs.setString('account_telephone', data['telephone']!); await prefs.setString('account_password', data['password']!); await prefs.setString('account_country', data['country']!); notifyListeners(); }
- 退出时清除临时数据
在onPopPage或账户设置完成后,清理本地存储的临时数据:
onPopPage: (route, result) async { if (!route.didPop(result)) { return false; } createAccount = false; setup = false; createAccountData.clear(); // 清除本地存储 final prefs = await SharedPreferences.getInstance(); await prefs.remove('setup'); await prefs.remove('account_name'); await prefs.remove('account_email'); await prefs.remove('account_telephone'); await prefs.remove('account_password'); await prefs.remove('account_country'); notifyListeners(); return true; },
方案二:将非敏感表单数据与路由信息绑定
适合无需保密的字段(如姓名、邮箱),通过URL参数传递状态,刷新时从URL解析恢复数据(密码等敏感数据必须用方案一存储)。
- 定义路由配置类与解析器
创建支持解析账户设置参数的路由配置和解析器:
// 路由配置基类 abstract class AppRouteConfig {} // 登录路由配置 class LoginRouteConfig extends AppRouteConfig {} // 账户设置路由配置 class SetupRouteConfig extends AppRouteConfig { final String? name; final String? email; final String? telephone; final String? country; SetupRouteConfig({this.name, this.email, this.telephone, this.country}); } // 路由信息解析器 class AppRouteInformationParser extends RouteInformationParser<AppRouteConfig> { @override Future<AppRouteConfig> parseRouteInformation(RouteInformation routeInformation) async { final uri = Uri.parse(routeInformation.location!); if (uri.path == '/setup') { // 从URL参数中提取非敏感数据 return SetupRouteConfig( name: uri.queryParameters['name'], email: uri.queryParameters['email'], telephone: uri.queryParameters['telephone'], country: uri.queryParameters['country'], ); } return LoginRouteConfig(); } @override RouteInformation restoreRouteInformation(AppRouteConfig configuration) { if (configuration is SetupRouteConfig) { // 将非敏感数据拼接成URL return RouteInformation( location: '/setup?name=${configuration.name}&email=${configuration.email}&telephone=${configuration.telephone}&country=${configuration.country}', ); } return RouteInformation(location: '/login'); } }
- 从路由配置恢复状态
修改RouterDelegate的setNewRoutePath方法,根据路由配置重建状态:
@override Future<void> setNewRoutePath(AppRouteConfig configuration) async { if (configuration is SetupRouteConfig) { createAccount = true; setup = true; // 从本地存储读取敏感的密码数据 final prefs = await SharedPreferences.getInstance(); final password = prefs.getString('account_password'); createAccountData = { 'name': configuration.name, 'email': configuration.email, 'telephone': configuration.telephone, 'password': password, 'country': configuration.country, }; } else { createAccount = false; setup = false; createAccountData.clear(); } notifyListeners(); }
- 提交时更新路由信息
修改_handleSetUpTapped方法,更新路由信息并保存敏感数据:
void _handleSetUpTapped(Map<String, String?> data) { setup = true; createAccountData = data; // 保存密码到本地存储 SharedPreferences.getInstance().then((prefs) { prefs.setString('account_password', data['password']!); }); // 更新路由信息,触发状态同步 setNewRoutePath(SetupRouteConfig( name: data['name'], email: data['email'], telephone: data['telephone'], country: data['country'], )); }
关键注意事项
- 敏感数据(如密码)绝对不能放入URL,必须用本地存储或其他安全方式保存
- 完成账户设置或返回登录页时,务必清除临时存储的数据,避免数据残留
- Navigator 2.0的核心是路由状态与RouterDelegate状态同步,优先让状态由RouteInformation驱动,确保刷新、前进后退时状态一致
内容的提问来源于stack exchange,提问作者gradi ngalubenge
相关产品推荐
相关产品推荐

