Flutter中Dialog内await http.get(url)失效问题的排查与解决
问题描述
我实现了一个用于获取网页标题的Widget,代码如下:
Widget ref_box(BuildContext context,String reference){ Future <String> scrap_title(url_received) async { print('response init'); if (url_received =='' || url_received ==' ') return 'No reference'; final url = Uri.parse(url_received); print('response parsed'); final resp = await http.get(url); print('response on'); var elements; if (resp.statusCode ==200){ dom.Document doc = parser.parse(resp.body); elements = doc.querySelector('title'); // final linkElemnt=doc.getElementsByClassName('entry-title'); //var html = dom.Document.html(resp.body).head!.text.trim(); } return Future.value(elements!.text); } return Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding(padding: EdgeInsets.symmetric(vertical: 3)), FutureBuilder<String>( future: scrap_title(reference), builder: (BuildContext context, AsyncSnapshot snapshot){ if (snapshot.hasData == false) { return CircularProgressIndicator(); } else return Text(snapshot.data); }, ), //Text(ref_title), Padding(padding: EdgeInsets.symmetric(vertical: 3)) ], ); }
我在两个地方调用了这个ref_box:一处在Stateful类中,另一处在Dialog弹窗中。Dialog中的调用代码如下:
void Function()? _showPost_Dialog(String content, String writer, int like,int shared,String ref) { showDialog( context: context, builder: (context) { String lls = like.toString(); // return object of type Dialog return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0)), //this right here child: Container(height: MediaQuery.of(context).size.height*0.7, padding: EdgeInsets.symmetric(horizontal: 10), child:GestureDetector(onTap: (){ if(ref==' ' ||ref=='') { print('Reference 없음'); return; } _showWeb(writer, ref); }, child: ref_box(context, ref), ), );
经测试,在Stateful类中调用ref_box运行正常,但在Dialog中调用时却无法正常工作。通过print日志发现,Dialog中调用时await http.get(url)无法执行。请问该问题产生的原因是什么?如何修改才能让它在Dialog中也能正常工作?
问题原因与解决方案
原因分析
- Dialog生命周期与Future创建时机冲突:
ref_box的build方法内直接创建scrap_title的Future,Dialog在显示过程中可能因布局变化多次重建,导致请求被重复触发或被中断,await后的代码无法执行。 - 未处理异步异常:
http.get可能抛出网络错误、URL解析错误等异常,但代码没有捕获这些异常,一旦出错请求会静默失败,没有任何提示。 - 空安全隐患:
elements!.text未做空判断,若网页无title标签会直接触发空指针异常,导致请求流程中断。
修改方案
1. 重构代码:将Future管理移至State类
把异步请求的创建移到Dialog所在的State类中,避免每次build都重新发起请求,同时添加异常处理:
// 假设Dialog所在的Widget是Stateful类,修改其State如下 class _YourPageState extends State<YourPage> { Future<String>? _titleFuture; void _showPost_Dialog(String content, String writer, int like, int shared, String ref) { // 打开弹窗前初始化请求Future,确保只执行一次 _titleFuture = _scrapTitle(ref); showDialog( context: context, builder: (context) { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), child: Container( height: MediaQuery.of(context).size.height * 0.7, padding: EdgeInsets.symmetric(horizontal: 10), child: GestureDetector( onTap: () { if (ref == ' ' || ref == '') { print('Reference 없음'); return; } _showWeb(writer, ref); }, // 传入外部创建好的Future child: refBox(context, _titleFuture!), ), ), ); }, ); } Future<String> _scrapTitle(String urlReceived) async { print('response init'); if (urlReceived.isEmpty || urlReceived.trim().isEmpty) return 'No reference'; try { final url = Uri.parse(urlReceived); print('response parsed'); final resp = await http.get(url); print('response on'); if (resp.statusCode == 200) { dom.Document doc = parser.parse(resp.body); final titleElement = doc.querySelector('title'); return titleElement?.text.trim() ?? 'No title found'; } else { return 'Request failed: ${resp.statusCode}'; } } catch (e) { print('Fetch error: $e'); return 'Failed to load title'; } } } // 修改ref_box为接收外部Future的Widget Widget refBox(BuildContext context, Future<String> titleFuture) { return Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding(padding: EdgeInsets.symmetric(vertical: 3)), FutureBuilder<String>( future: titleFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else if (snapshot.hasData) { return Text(snapshot.data!); } else { return const Text('No reference'); } }, ), const Padding(padding: EdgeInsets.symmetric(vertical: 3)) ], ); }
2. 关键修改点说明
- Future移至build之外:确保请求只在打开弹窗时发起一次,避免Dialog重建导致重复请求或中断。
- 添加异常捕获:用
try-catch包裹网络请求,处理所有可能的异常,防止请求静默失败。 - 完善FutureBuilder状态判断:通过
connectionState准确判断等待状态,同时处理错误状态,提升调试效率和用户体验。 - 空安全处理:对
titleElement做空判断,避免空指针异常。
内容的提问来源于stack exchange,提问作者MooNChilD Song
相关产品推荐
相关产品推荐

