Flutter+Firebase开发聊天应用出现chatRoomsStream未初始化报错
问题原因
你使用late修饰了chatRoomsStream变量,但该变量的初始化逻辑是异步执行的:
initState调用异步方法getUserInfo时不会阻塞后续执行,Flutter会立刻执行build方法渲染页面build方法调用chatRoomList()时直接访问了还未完成赋值的chatRoomsStream,触发Late初始化异常chatRoomsStream的赋值需要等待getUserNameSharedPreference、getChatRooms两个异步操作都完成后才会执行,时机上必然晚于首次页面渲染
修复方案
方案1:改为可空类型加加载判断(最简单稳定)
将chatRoomsStream改为可空类型,访问前先判断是否完成初始化,同时增加加载占位UI:
// 声明修改为可空 Stream<QuerySnapshot>? chatRoomsStream; Widget chatRoomList() { final stream = chatRoomsStream; // 未初始化时显示加载中 if (stream == null) { return const Center(child: CircularProgressIndicator()); } return StreamBuilder( stream: stream, builder: (context, snapshot) { return snapshot.hasData ? ListView.builder( itemCount: (snapshot.data as QuerySnapshot).docs.length, itemBuilder: (context, index) { final doc = (snapshot.data as QuerySnapshot).docs[index]; return ChatRoomsTile( doc["chatroomid"] .toString().replaceAll("_", "") .replaceAll(Constants.myName, ""), doc["chatroomid"] ); }, ) : const Center(child: CircularProgressIndicator()); }, ); }
方案2:用FutureBuilder等待初始化完成再渲染
把初始化逻辑包装在FutureBuilder中,异步操作完成前显示加载页,避免提前访问未初始化的变量:
// 修改getUserInfo返回Future Future getUserInfo() async { Constants.myName = (await HelperFunctions.getUserNameSharedPreference())!; final value = await databaseMethods.getChatRooms(Constants.myName); setState(() { chatRoomsStream = value; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Image.asset("assets/images/logo.png", height: 50.0), actions: [ GestureDetector( onTap: () { authMethods.signOut(); Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const Authenticate() )); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16), child: const Icon(Icons.exit_to_app)), ), ],), body: FutureBuilder( future: getUserInfo(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return chatRoomList(); } return const Center(child: CircularProgressIndicator()); }, ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.pushReplacement(context, MaterialPageRoute( builder: (context) => const SearchScreen() )); }, child: const Icon(Icons.search), ), ); }
额外隐藏问题修复
你原代码的ListView.builder中存在取值逻辑错误,后续即使初始化问题解决也会触发崩溃:
// 错误写法:错误将下标index当做Map取chatroomid字段 (snapshot.data as QuerySnapshot).docs[index["chatroomid"]] // 正确写法:先取下标对应的文档,再取文档的字段值 final doc = (snapshot.data as QuerySnapshot).docs[index]; doc["chatroomid"]
内容的提问来源于stack exchange,提问作者VenoM
相关产品推荐
相关产品推荐

