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

Flutter初始化SharedPreferences报LateInitializationError如何解决

错误根源

两处实现问题直接导致报错:

  • 类成员late SharedPreferences prefs声明后从未完成赋值,storeToken方法内获取的是同名局部prefs变量,和类级别的prefs字段无关,首次构建执行userLoggedIn()时直接访问未初始化的late字段,必然抛出LateInitializationError
  • 把prefs改成可空类型SharedPreferences? prefs后,没有做非空判断就直接调用containsKey方法,自然触发空指针报错。
修复步骤

1. 应用启动时提前初始化SharedPreferences

SharedPreferences.getInstance()是异步方法,必须在runApp前完成全局初始化,避免组件构建时拿不到实例:

void main() async {
  // 初始化Flutter引擎绑定
  WidgetsFlutterBinding.ensureInitialized();
  // 全局获取SharedPreferences实例,后续全程复用
  final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
  runApp(
    ChangeNotifierProvider(
      create: (_) => AuthProvider(sharedPrefs),
      child: const MyApp(),
    )
  );
}

2. 改造AuthProvider,通过构造函数注入已初始化的prefs实例

移除原来未赋值的late/可空prefs声明,通过构造函数传入初始化完成的实例,同时修正storeToken的实现,不要重复获取局部prefs变量:

class AuthProvider with ChangeNotifier{
  bool _isLoading = false;
  bool get isLoading => _isLoading;
  User user = User();
  // 构造函数注入prefs,声明为final确保不可变,不需要late/可空修饰
  final SharedPreferences prefs;
  AuthProvider(this.prefs){
    // 初始化时如果本地已存token,自动更新请求头
    if(userLoggedIn()){
      final savedToken = prefs.getString(ApiConstants.kEY_ACCESS_TOKEN)!;
      updateHeader(savedToken);
      token = savedToken;
    }
  }
   
  String token = '';
  Map<String, String> _mainHeaders = {
    'Content-Type': 'application/json; charset=UTF-8',
  };

  void updateHeader(String token) {
    _mainHeaders = {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer $token',
    };
  }

  Future<ResponseModel> login(String username, String password) async {
    print("Getting token");
    _isLoading = true;
    notifyListeners();
    http.Response response = await dologin(username, password);
   
    var answer = User.fromJson(jsonDecode(response.body));
    late ResponseModel responseModel;
    if (response.statusCode == 200) {
      responseModel = ResponseModel(true, answer.token!);
      // 直接复用类成员prefs存储token,不需要重复调用getInstance
      await prefs.setString(ApiConstants.kEY_ACCESS_TOKEN, answer.token!);
      token = answer.token!;
      updateHeader(answer.token!);
    } else {
      responseModel = ResponseModel(false, response.reasonPhrase!);
    }
    _isLoading = false;
    notifyListeners();
    return responseModel;
  }

  // 直接复用已初始化的prefs判断登录状态,无初始化/空指针风险
  bool userLoggedIn() => prefs.containsKey(ApiConstants.kEY_ACCESS_TOKEN);

  // 补充登出方法,直接复用prefs清除本地凭证
  Future<void> logout() async {
    await prefs.remove(ApiConstants.kEY_ACCESS_TOKEN);
    token = '';
    _mainHeaders = {
      'Content-Type': 'application/json; charset=UTF-8',
    };
    notifyListeners();
  }
}

3. 保留原有路由判断逻辑即可

原来的Consumer逻辑不需要修改,此时AuthProvider初始化时已经持有可用的prefs实例,调用userLoggedIn()不会再触发初始化相关报错:

child: MaterialApp(
  home: Consumer<AuthProvider>(
    builder: ((context, auth, _){
      return auth.userLoggedIn() ? const HomePage() : const loginScreen();
    })
  ),
  // 其余MaterialApp配置不变
)

注意:不要在业务方法里重复调用SharedPreferences.getInstance()创建局部变量,既容易出现变量作用域导致的赋值遗漏问题,也会产生不必要的异步开销,全局初始化一次后全程复用实例即可。

内容的提问来源于stack exchange,提问作者Amir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 04:48:17