You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:09:09