Flutter中SharedPreference保存stripe_id后token返回null问题排查
问题描述
我创建了UserModel用于将所需数据保存到SharedPreference中,确保用户杀死应用后仍能获取数据。使用Provider状态管理实现UserViewModel类,登录时保存userId等数据;但在添加卡片页面获取stripe_id并调用saveUser保存后,回到Drawer Widget获取数据时token返回null。仅保存stripe_id时,原有token等字段会被覆盖为null,重新手动设置token则能正常获取,希望排查问题原因。
相关代码
用户模型类
class UserModel { String? token; String? userId; String? stripeId; String? userName; String? userEmailAddress; UserModel({this.activeCardId,this.token,this.userId,this.stripeId,this.userName}); UserModel.fromJson(Map<String, dynamic> json) { token = json['token']; userId = json['user_id']; stripeId = json['stripe_id']; userName=json['fullname']; userEmailAddress=json['email']; } Map<String, dynamic> toJson() { final Map<String, dynamic> data = <String, dynamic>{}; data['token'] = token; data['user_id'] = userId; data['stripe_id'] = stripeId; data['fullname']=userName; data['email']=userEmailAddress; return data; } }
SharedPreference管理类
class UserViewModel with ChangeNotifier{ Future<bool> saveUser(UserModel user)async{ final SharedPreferences sp = await SharedPreferences.getInstance(); sp.setString('token', user.token.toString()); sp.setString('user_id',user.userId.toString()); sp.setString('stripe_id',user.stripeId.toString()); sp.setString('userName',user.userName.toString()); sp.setString('userEmailAddress',user.userEmailAddress.toString()); notifyListeners(); return true ; } Future<UserModel> getUser()async{ final SharedPreferences sp = await SharedPreferences.getInstance(); final String? token = sp.getString('token'); final String? userId = sp.getString('user_id'); final String? stripeId=sp.getString('stripe_id'); final String? userName=sp.getString('userName'); final String? userEmailAddress=sp.getString('userEmailAddress'); return UserModel( token: token.toString(), userId: userId.toString(), stripeId: stripeId.toString(), userName:userName.toString(), userEmailAddress:userEmailAddress.toString(), ); } Future<bool> remove()async{ final SharedPreferences sp = await SharedPreferences.getInstance(); sp.remove('token'); return true; } }
登录时保存数据代码
final userPreference =Provider.of<UserViewModel>(context, listen: false); userPreference.saveUser(UserModel(userId: value['data']['id'].toString()));
获取数据代码
Future<UserModel> getUserDate() => UserViewModel().getUser(); getUserDate().then((value)async{ setState(() { GlobalVariables.token=value.token.toString(); }); });
保存stripe_id代码
final userPreference =Provider.of<UserViewModel>(context,listen: false); userPreference.saveUser(UserModel(stripe_id: createCard['id'].toString()));
问题原因与解决方案
核心问题
- 不完整的
UserModel实例覆盖原有数据:保存stripe_id时,你新建了一个仅包含stripe_id的UserModel对象,其他字段默认是null。调用saveUser时,这些null值被转成字符串"null",直接覆盖了SharedPreference里的原有有效数据。 - null值错误转换为字符串:
getUser方法中,当sp.getString返回null时,调用toString()会把null转为字符串"null",导致获取到的token等字段变成无效的"null"字符串,而非预期的null或原有值。
修复方案
1. 修改saveUser方法,只更新指定字段
先读取当前已保存的用户数据,仅更新传入UserModel中不为null的字段,避免覆盖原有有效数据:
Future<bool> saveUser(UserModel user) async { final SharedPreferences sp = await SharedPreferences.getInstance(); // 获取当前已保存的所有字段值 String? currentToken = sp.getString('token'); String? currentUserId = sp.getString('user_id'); String? currentStripeId = sp.getString('stripe_id'); String? currentUserName = sp.getString('userName'); String? currentEmail = sp.getString('userEmailAddress'); // 仅更新user对象中不为null的字段,其余保留原有值 sp.setString('token', user.token ?? currentToken ?? ""); sp.setString('user_id', user.userId ?? currentUserId ?? ""); sp.setString('stripe_id', user.stripeId ?? currentStripeId ?? ""); sp.setString('userName', user.userName ?? currentUserName ?? ""); sp.setString('userEmailAddress', user.userEmailAddress ?? currentEmail ?? ""); notifyListeners(); return true; }
2. 修复getUser方法的null值处理
直接返回SharedPreference读取的原始值,不要对null调用toString():
Future<UserModel> getUser() async { final SharedPreferences sp = await SharedPreferences.getInstance(); final String? token = sp.getString('token'); final String? userId = sp.getString('user_id'); final String? stripeId = sp.getString('stripe_id'); final String? userName = sp.getString('userName'); final String? userEmailAddress = sp.getString('userEmailAddress'); return UserModel( token: token, userId: userId, stripeId: stripeId, userName: userName, userEmailAddress: userEmailAddress, ); }
3. 优化数据保存逻辑(推荐)
保存特定字段时,先获取当前完整的用户实例,更新字段后再保存,避免创建不完整的UserModel:
final userPreference = Provider.of<UserViewModel>(context, listen: false); // 获取当前已保存的用户数据 UserModel currentUser = await userPreference.getUser(); // 更新stripe_id字段 currentUser.stripeId = createCard['id'].toString(); // 保存完整的用户对象 userPreference.saveUser(currentUser);
4. 修复获取数据的代码
不要每次新建UserViewModel实例,使用Provider提供的全局实例保证状态一致性:
// 在Widget的build方法中获取Provider实例 final userViewModel = Provider.of<UserViewModel>(context); // 获取用户数据 userViewModel.getUser().then((value) { setState(() { GlobalVariables.token = value.token; }); });
内容的提问来源于stack exchange,提问作者TimeToCode
相关产品推荐
相关产品推荐

