如何缓存授权数据(access_token)?macOS应用免重复登录实现问询
解决方案
1. 应用启动时的异步判断逻辑
由于SharedPreferences的操作是异步的,直接在home属性中判断令牌会导致初始化时机问题,我们可以用FutureBuilder来处理异步校验流程:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '你的macOS应用', home: FutureBuilder<String?>( // 异步获取缓存的access_token future: PreferencesProvider().getAccess(), builder: (context, snapshot) { // 初始化加载阶段,可自定义启动页(比如显示应用Logo) if (snapshot.connectionState == ConnectionState.waiting) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } // 根据令牌是否存在,跳转对应页面 return snapshot.data != null ? const MainWidget() : const StartScreen(); }, ), ); } }
2. 优化PreferencesProvider的初始化逻辑
当前实现中每次调用存储/读取方法都会重新执行_initPreferences,可以修改为仅初始化一次,避免重复异步操作:
class PreferencesProvider { static PreferencesProvider? _instance; SharedPreferences? _preferences; Future<void>? _initFuture; // 存储初始化Future,确保只执行一次 factory PreferencesProvider() { _instance ??= PreferencesProvider._(); return _instance!; } PreferencesProvider._() { _initFuture = _initPreferences(); } Future<void> _initPreferences() async { _preferences = await SharedPreferences.getInstance(); } // 所有操作前先等待初始化完成 Future<void> _ensureInitialized() async { await _initFuture; } Future<void> setAccess(String value) async { await _ensureInitialized(); await _preferences!.setString("ACCESS_TOKEN", value); } Future<String?> getAccess() async { await _ensureInitialized(); return _preferences!.getString("ACCESS_TOKEN"); } Future<void> clearAccess() async { await _ensureInitialized(); await _preferences!.remove("ACCESS_TOKEN"); } Future<void> setRefresh(String value) async { await _ensureInitialized(); await _preferences!.setString("REFRESH_TOKEN", value); } Future<String?> getRefresh() async { await _ensureInitialized(); return _preferences!.getString("REFRESH_TOKEN"); } // 补充你用到的setUserId方法(原代码中调用了但未实现) Future<void> setUserId(String value) async { await _ensureInitialized(); await _preferences!.setString("USER_ID", value); } }
3. 修正授权Cubit中的异步存储问题
原代码中链式调用异步方法不会等待操作完成,可能导致令牌未存储完成就执行后续逻辑,需要改为等待每个异步操作:
if (data['success']) { final prefs = PreferencesProvider(); await prefs.clearAccess(); await prefs.setUserId(data['userId']); await prefs.setAccess(data['accessToken']); await prefs.setRefresh(data['refreshToken']); _apiProvider.setToken(data["accessToken"]); }
如果想提升效率,也可以用Future.wait并行执行存储操作:
if (data['success']) { final prefs = PreferencesProvider(); await Future.wait([ prefs.clearAccess(), prefs.setUserId(data['userId']), prefs.setAccess(data['accessToken']), prefs.setRefresh(data['refreshToken']), ]); _apiProvider.setToken(data["accessToken"]); }
额外提示
如果需要处理令牌过期的情况,可以在启动时不仅判断令牌是否存在,还可以调用接口校验令牌有效性,再决定跳转页面。
内容的提问来源于stack exchange,提问作者newbie2210
相关产品推荐
相关产品推荐

