在onPressed函数中调用docRef.snapshots()报错的原因及解决方法
问题:Firestore读取"url"字段时抛出Bad state异常
我为ElevatedButton编写了如下代码片段:
onPressed: () async { var docRef = await context .read<FirestoreRepository>() .firestore .collection('users') .doc(userData.selfRef.id) .collection('checkout_sessions') .add({ 'price': pricePlans.first.selfRef.id, 'success_url': 'https://success.com', 'cancel_url': 'https://cancel.com' }); debugPrint('doc added'); docRef .snapshots() .listen((ds) { if (ds.exists) { var url = ds.get('url'); if (context.mounted) { Navigator.push(context, MaterialPageRoute(builder: (_) { return CheckoutPage(url: url); })); // 注:原代码存在语法错误,缺少闭合的大括号与括号 });
调用docRef.snapshots()时触发未捕获异常断点,报错信息:
Bad state: field "url" does not exist within the DocumentSnapshotPlatform
我的模型文件代码如下:
class CheckoutSessionModel extends Equatable { final String model; final String sessionId; final String url; final String price; const CheckoutSessionModel({ required this.model, required this.sessionId, required this.url, required this.price, }); factory CheckoutSessionModel.fromSnapshot( DocumentSnapshot<Map<String, dynamic>> snapshot) { final data = snapshot.data()!; return CheckoutSessionModel( model: data['mode'] as String? ?? '', sessionId: data['sessionId'] as String? ?? '', url: data['url'] as String? ?? '', price: data['price'] as String? ?? ''); } @override List<Object> get props => [model, sessionId, url, price]; }
报错原因
- 初始文档无"url"字段:通过
add()写入Firestore的初始文档仅包含price、success_url、cancel_url三个字段,url字段是后续由第三方服务(如Stripe支付扩展)异步生成写入的。而docRef.snapshots()会立即触发第一次快照,此时文档还未生成url字段。 ds.get()的特性:Firestore的DocumentSnapshot.get()方法在目标字段不存在时会直接抛出Bad state异常,代码中未做任何判空处理,导致触发断点。
修复方案
方案1:使用可选访问避免异常
直接通过data()获取文档数据后做可选访问,字段不存在时返回null,判断非空后再执行跳转:
docRef.snapshots().listen((ds) { if (ds.exists) { // 用可选访问符,字段不存在时返回null final url = ds.data()?['url']; if (url != null && context.mounted) { Navigator.push(context, MaterialPageRoute(builder: (_) { return CheckoutPage(url: url); })); } } });
方案2:复用现有模型类的处理逻辑
CheckoutSessionModel已经通过data['url'] as String? ?? ''处理了字段不存在的情况,直接用模型类解析快照,再判断url非空后跳转:
docRef.snapshots().listen((ds) { if (ds.exists) { final session = CheckoutSessionModel.fromSnapshot(ds); // 模型中字段不存在时返回空字符串,判断非空后跳转 if (session.url.isNotEmpty && context.mounted) { Navigator.push(context, MaterialPageRoute(builder: (_) { return CheckoutPage(url: session.url); })); } } });
方案3:使用get()的默认值参数
Firestore的get()方法支持传入defaultValue参数,字段不存在时返回指定默认值,避免抛出异常:
docRef.snapshots().listen((ds) { if (ds.exists) { // 字段不存在时返回null final url = ds.get('url', defaultValue: null); if (url != null && context.mounted) { Navigator.push(context, MaterialPageRoute(builder: (_) { return CheckoutPage(url: url); })); } } });
另外注意:原代码的listen回调函数存在语法错误,缺少闭合的大括号和括号,修复时需要补上,否则无法正常编译。
内容的提问来源于stack exchange,提问作者M99Coder
相关产品推荐
相关产品推荐

