Flutter Image Picker retrieveLostData触发StreamBuilder频繁显示加载页问题
问题根源
- 你在
FutureBuilder的future参数中直接传入了retrieveLostData(),这会导致每次页面触发build时,都会重新执行这个异步方法,生成全新的Future实例,FutureBuilder会反复进入等待状态,流程执行完成后又重新创建myPage组件。 myPage被反复销毁重建时,内部的StreamBuilder会重新监听数据库stream,每次重新订阅都会短暂进入ConnectionState.waiting状态,触发加载UI的显示。- 你的
retrieveLostData方法存在空安全隐患:当response.file为空时,你直接用!强解response.exception,如果此时没有异常(也就是exception也为空),就会抛出空指针异常,导致Future执行失败,触发页面再次重建,形成死循环。
修复步骤
1. 缓存Future实例,避免重复执行
在页面的State类中,提前初始化retrieveLostData的Future,不要在build方法里创建:
late final Future<void> _retrieveLostDataFuture; @override void initState() { super.initState(); // 仅在页面初始化时执行一次retrieveLostData _retrieveLostDataFuture = retrieveLostData(); }
然后把FutureBuilder的future参数替换为这个缓存的实例:
future: _retrieveLostDataFuture,
2. 修复retrieveLostData的空安全问题
修改异常处理逻辑,只有确认exception不为空时再写入日志:
Future<void> retrieveLostData() async { print("Retrieve Lost Data ${DateTime.now()}"); final LostDataResponse response = await CustomImagePicker.imagePicker.retrieveLostData(); if (response.file != null) { if (response.type == RetrieveType.video) { // 视频逻辑预留 } else { _savePicture(response.file!); } } else if (response.exception != null) { // 只有存在异常时才处理 String retrieveDataError = response.exception!.code; LogsCompanion log = LogsCompanion.insert(message: "IMAGE PROBLEM: ${retrieveDataError}", date: DateTime.now()); _appDatabase.logDao.insertLog(log); } }
3. 优化StreamBuilder的判断逻辑(可选)
你已经给StreamBuilder设置了initialData: [],正常情况下不会出现无数据同时处于waiting状态的情况,调整判断顺序可以避免不必要的加载显示:
StreamBuilder( initialData: [], stream: _appDatabase.myDao.getStream(), builder: (context, snapshot) { // 优先处理异常 if (snapshot.hasError) { return Text('加载失败:${snapshot.error}'); } // 有数据直接渲染,不管连接状态 final list = snapshot.data as List; if (list.isNotEmpty) { return ListView.separated( separatorBuilder: (context, index) => Container(height: 8), physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), itemCount: list.length, itemBuilder: (context, index) { return Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ listItem(list[index]), ], ); }, ); } // 空列表时返回下拉刷新组件 return RefreshIndicator( onRefresh: () async {}, child: const Center(child: Text('暂无数据')), ); }, ),
修改完成后retrieveLostData只会在页面初始化时执行一次,不会反复触发页面重建,StreamBuilder也不会被反复销毁重建,循环就会停止。
内容的提问来源于stack exchange,提问作者SaddleMantle
相关产品推荐
相关产品推荐

