Flutter Realm重启App后Current User为null,如何持久化用户数据?
问题分析
你遇到的核心问题是Realm用户会话未在App重启后自动恢复,导致currentUser为null。从代码来看,你虽然读取了本地存储的LoginData,但并未利用它去恢复Realm用户会话,同时AppServices初始化时也没有检查Realm App自带的持久化用户。
解决方案
1. 重构AppServices类,添加会话恢复逻辑
让AppServices持有Realm App实例,并在初始化时检查已持久化的用户,同时新增用refresh token恢复会话的方法:
import 'package:realm/realm.dart'; class AppServices extends ChangeNotifier { final App _realmApp; User? _currentUser; User? get currentUser => _currentUser; // 初始化时创建Realm App实例,并检查现有持久化用户 AppServices(String appId, Uri baseUrl) : _realmApp = App(AppConfiguration(appId, baseUrl: baseUrl)) { // Realm会自动持久化用户,先尝试读取本地已保存的用户 _currentUser = _realmApp.currentUser; } // 用refreshToken恢复会话 Future<void> restoreSession(String refreshToken) async { try { final user = await _realmApp.logIn(Credentials.refreshToken(refreshToken)); _currentUser = user; notifyListeners(); // 通知UI更新 } catch (e) { // 恢复失败时清除无效的本地凭据 await LocalStorageService().clearLoginCred(); } } // 登录方法(补充完整登录逻辑,存储refreshToken) Future<void> login(String email, String password) async { final user = await _realmApp.logIn(Credentials.emailPassword(email, password)); _currentUser = user; // 存储refreshToken用于后续会话恢复 await LocalStorageService().saveLoginCred(LoginData(refreshToken: user.refreshToken)); notifyListeners(); } // 登出方法 Future<void> logout() async { await _currentUser?.logOut(); _currentUser = null; await LocalStorageService().clearLoginCred(); notifyListeners(); } }
2. 修改main函数,提前恢复会话
在启动App前,先初始化AppServices并利用本地存储的LoginData恢复会话:
void main() async { WidgetsFlutterBinding.ensureInitialized(); final realmConfig = json.decode(await rootBundle.loadString('assets/config/realm.json')); String appId = realmConfig['appId']; Uri baseUrl = Uri.parse(realmConfig['baseUrl']); final LoginData? storedLoginData = await LocalStorageService().currentLoginCred(); // 初始化AppServices final appServices = AppServices(appId, baseUrl); // 如果有存储的refreshToken,尝试恢复会话 if (storedLoginData?.refreshToken != null) { await appServices.restoreSession(storedLoginData!.refreshToken); } runApp(MultiProvider( providers: [ ChangeNotifierProvider.value(value: appServices), ProxyProvider<AppServices, Realm?>( update: (context, app, previousRealm) { if (app.currentUser != null) { previousRealm?.close(); return initRealm(app.currentUser!); } return null; }, dispose: (_, realm) => realm?.close(), ) ], child: const App(), )); }
3. 调整App组件,监听用户状态变化
使用Consumer监听AppServices的用户状态,确保初始路由能正确响应用户恢复结果:
class App extends StatelessWidget { const App({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Consumer<AppServices>( builder: (context, appServices, child) { return WillPopScope( onWillPop: () async => false, child: MaterialApp( title: 'Target Eagle App', theme: ThemeData.dark(), initialRoute: appServices.currentUser != null ? '/' : '/welcomePage', routes: { '/': (context) => const HomePage(), '/login': (context) => const LogIn(true) }, ), ); }, ); } }
4. 确保LocalStorageService正确存储refreshToken
确认本地存储服务能正确保存和读取用户的refresh token(以shared_preferences为例):
import 'package:shared_preferences/shared_preferences.dart'; class LocalStorageService { final Future<SharedPreferences> _prefs = SharedPreferences.getInstance(); Future<void> saveLoginCred(LoginData loginData) async { final prefs = await _prefs; await prefs.setString('refreshToken', loginData.refreshToken); } Future<LoginData?> currentLoginCred() async { final prefs = await _prefs; final refreshToken = prefs.getString('refreshToken'); if (refreshToken != null) { return LoginData(refreshToken: refreshToken); } return null; } Future<void> clearLoginCred() async { final prefs = await _prefs; await prefs.remove('refreshToken'); } } class LoginData { final String refreshToken; LoginData({required this.refreshToken}); }
关键说明
- Realm会自动持久化用户,但如果用户的refresh token过期,恢复会话会失败,此时需要引导用户重新登录。
- 必须存储refresh token而非临时的access token,因为refresh token可以用来获取新的access token,实现会话持久化。
- 使用
notifyListeners()确保UI能及时响应用户状态变化。
内容的提问来源于stack exchange,提问作者Shorifuzzaman
相关产品推荐
相关产品推荐

