Flutter中如何等待函数执行完成后再显示对应组件
问题背景
开发二维码扫描添加联系人功能,已完成二维码生成、扫码相机模块开发,预期实现用户扫描他人二维码后弹出Dialog展示被扫描用户信息。运行时抛出'Null' is not type of 'String'报错,已初步定位为数据库拉取用户信息存在耗时,data字段仍为null时相关组件已经开始构建导致。
问题根因
- 异步流程缺失加载状态管理:扫码成功后立即触发UI构建弹窗,但异步方法
getQRData()未执行完成,data仍为初始null值 - 空值场景无兼容处理:构建弹窗内容时直接读取
data['nickname']等字段,未判断data是否已赋值,null值传入要求String类型的组件参数时直接触发类型错误 - 数据返回后未触发界面刷新:原逻辑中
getQRData()拿到数据库返回结果后,未调用setState通知框架更新UI,数据加载完成后弹窗内容也不会自动刷新
修复步骤
1. 新增状态标记
在对应State类中初始化状态变量,区分加载中、加载完成的不同场景:
// 存储拉取到的用户信息,初始为null Map<String, dynamic>? data; // 数据加载状态标记 bool isLoading = false;
2. 改造异步数据拉取方法
在数据拉取前后更新状态,补充异常捕获,避免加载状态卡死,同时给可能为null的字段加空安全处理:
void getQRData() async { // 进入加载状态 setState(() { isLoading = true; }); try { await UserDatabase.getContactData( contactId: barcode!.code.toString(), data: data, ); final userData = await UserDatabase.getUserShortData( userId: barcode!.code.toString(), // 加空安全,避免data为null时读取mobile_no报错 phoneNumber: data?['mobile_no'], ); // 数据拉取完成,更新数据、关闭加载态,触发UI刷新 setState(() { data = userData; isLoading = false; }); print(data); } catch (e) { // 请求异常时也要关闭加载态,避免一直转圈 setState(() { isLoading = false; }); // 可在此处补充错误提示逻辑,比如toast提示"获取对方信息失败,请重试" print('拉取联系人信息异常: $e'); } }
3. 改造Dialog构建逻辑
根据加载状态、数据是否存在展示对应内容,所有读取map字段的位置加空值兜底,避免null传入String类型参数:
Widget build(BuildContext context){ /// 其他原有代码 if (barcode != null) Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), child: Container( height: 160, padding: EdgeInsets.only(top: 10, right: 10, left: 10), // 加载中或数据未就绪时展示加载指示器 child: isLoading || data == null ? const Center(child: CircularProgressIndicator()) // 数据就绪后展示实际联系人信息 : Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ // 扫码用户头像 BuildProfilePicture( radius: 20, // 空值兜底,字段不存在时传空字符串 nickname: data!['nickname'] ?? '', profileLink: '', ), // 扫码用户昵称 Text( // 空值兜底,避免昵称字段为null时报错 data!['nickname'] ?? '未知用户', style: primaryColor600Style.copyWith(fontSize: 18), textAlign: TextAlign.center, ), // 添加提示文案 Padding( padding: EdgeInsets.symmetric(horizontal: 13), child: Text( 'Add this person to your contact list', textAlign: TextAlign.center, style: primaryColor400Style.copyWith( fontSize: 12, color: Colors.black, ), ), ), // 添加联系人按钮 Flexible( child: GestureDetector( onTap: () { controller?.resumeCamera(); // 点击后重置状态,避免下次扫码残留旧数据 setState(() { barcode = null; data = null; }); // 在此处补充实际保存联系人到本地的逻辑 }, child: Container( height: 30, decoration: BoxDecoration( color: accentColor, borderRadius: BorderRadius.circular(15), ), child: Center( child: Text( 'Add to contact', style: TextStyle( color: Colors.white, ), ), ), ), ), ) ], ), ), ), }
优化建议
- 扫码回调处增加重复触发拦截,避免用户在弹窗弹出前重复扫码,导致多次发起数据库请求
- 所有从接口/数据库返回的map中读取的字段,都建议加
?? 兜底值的空值处理,避免后端/数据库返回null时触发类型错误 - 弹窗关闭场景(比如点击蒙层关闭)也要记得重置
barcode、data、isLoading状态,避免状态残留
内容的提问来源于stack exchange,提问作者Kim San
相关产品推荐
相关产品推荐

