You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 18:35:28