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_
相关产品推荐
相关产品推荐

