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

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()));
问题原因与解决方案

核心问题

  1. 不完整的UserModel实例覆盖原有数据:保存stripe_id时,你新建了一个仅包含stripe_id的UserModel对象,其他字段默认是null。调用saveUser时,这些null值被转成字符串"null",直接覆盖了SharedPreference里的原有有效数据。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 05:55:31