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

Flutter Firestore StreamProvider报int非double?子类型异常排查

问题根因

报错核心是字段类型不匹配:

  • 你定义的UserModel中balance字段为double?类型,但Firestore存储数据时不会做数值类型校验,部分用户文档的balance存的是int类型(比如余额为0、50这类整数值时,很容易直接写入int),Dart不支持int到double的隐式转换,直接赋值就会触发type 'int' is not a subtype of type 'double?'错误。
  • 你的StreamProvider没有配置错误捕获,流触发异常后直接向上抛出,才出现你看到的provider层报错。
    你提到相同逻辑在其他场景能正常运行,是因为其他查询返回的文档里balance字段全是double类型(比如存的是3.14、0.0这类浮点值),当前查询的用户列表里至少有一个文档的balance是int,所以触发报错。
    另外你代码里还有一个隐藏bug:_usersListFromSnapshot方法中connected字段的默认值写的是空字符串'',但UserModel里connected是List<dynamic>?类型,字符串赋值给List类型也会触发类型错误,只是这次balance的报错先触发了而已。
修复方案

1. 做显式类型兼容,统一数值转换逻辑

所有从Firestore读取数据的位置,对可能出现类型不一致的字段做显式转换,兼容int和double两种数值类型,同时修复connected字段的默认值错误。
首先修改_usersListFromSnapshot方法:

// users list from snapshot
List<UserModel>? _usersListFromSnapshot(QuerySnapshot snapshot) {
  return snapshot.docs.map((doc) {
    // 统一处理balance类型,兼容int和double
    final rawBalance = doc.get('balance');
    double? parsedBalance;
    if (rawBalance is int) {
      parsedBalance = rawBalance.toDouble();
    } else if (rawBalance is double) {
      parsedBalance = rawBalance;
    }
    // 处理connected类型,修正默认值错误
    final rawConnected = doc.get('connected');
    return UserModel(
      uid: doc.get('uid'),
      username: doc.get('username') ?? '',
      email: doc.get('email') ?? '',
      type: doc.get('type') ?? '',
      balance: parsedBalance,
      usertag: doc.get('usertag') ?? '',
      connected: rawConnected is List ? rawConnected : [],
    );
  }).toList();
}

再修改UserModel.fromMap工厂方法,保持类型转换逻辑一致:

// receive data from the server
factory UserModel.fromMap(map) {
  // 统一处理balance类型
  final rawBalance = map['balance'];
  double? parsedBalance;
  if (rawBalance is int) {
    parsedBalance = rawBalance.toDouble();
  } else if (rawBalance is double) {
    parsedBalance = rawBalance;
  }
  // 处理connected类型
  final rawConnected = map['connected'];
  return UserModel(
    uid: map['uid'],
    username: map['username'],
    email: map['email'],
    type: map['type'],
    balance: parsedBalance,
    usertag: map['usertag'],
    connected: rawConnected is List ? rawConnected : []
  );
}

2. 给流增加错误捕获,避免异常直接导致UI崩溃

修改StreamProvider的配置,对流的错误做兜底处理:

StreamProvider<List<UserModel>?>.value(
  value: AuthService().usersList.handleError((error) {
    // 可在此处打印错误日志,方便后续排查问题
    debugPrint('拉取关联用户列表失败: $error');
    return []; // 出错时返回空列表作为兜底
  }),
  initialData: [],
  child: ConnectedListBuilder(),
),

3. 从根源规范数据写入

后续往Firestore写入balance字段时,统一将数值转为double类型再存储,比如写balance: 100.toDouble(),避免同一字段出现多种数值类型。

额外优化建议

你现在用SingleChildScrollView包裹ListView.builder,会导致ListView一次性加载所有列表项,不仅性能差还可能出现滚动冲突,建议去掉外层的SingleChildScrollView,直接将Padding包裹的ListView作为body的子元素即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:33:20