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

Flutter中如何在AlertDialog内实现ProgressIndicator加载效果

解决Flutter弹窗等待数据加载后才显示的问题

你当前的问题核心是:把网络请求逻辑放在了弹出对话框之前,导致必须等数据加载完成才会触发弹窗。要实现点击按钮立刻弹出对话框、内部先显示加载指示器,加载完成后切换为数据容器,需要把网络请求和状态管理移到对话框内部。

下面给你两种简单可行的实现方案:


方案一:用FutureBuilder快速实现(推荐新手)

FutureBuilder可以自动监听异步任务的状态,帮你自动切换加载中、加载完成、加载失败的UI,代码简洁高效。

完整示例代码

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter AlertDialog Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  // 封装网络请求
  Future<List<dynamic>> fetchPosts() async {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts?_limit=3'));
    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception('加载失败');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('弹窗加载示例')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 点击立刻弹框,不等数据
            showDialog(
              context: context,
              builder: (context) {
                return AlertDialog(
                  title: const Text('加载的数据'),
                  // 用FutureBuilder处理异步状态
                  content: FutureBuilder<List<dynamic>>(
                    future: fetchPosts(),
                    builder: (context, snapshot) {
                      // 加载中:显示圆形进度条
                      if (snapshot.connectionState == ConnectionState.waiting) {
                        return const SizedBox(
                          height: 100,
                          child: Center(child: CircularProgressIndicator()),
                        );
                      }
                      // 加载失败:显示错误信息
                      else if (snapshot.hasError) {
                        return Text('出错了:${snapshot.error}');
                      }
                      // 加载成功:显示数据容器
                      else if (snapshot.hasData) {
                        return Column(
                          mainAxisSize: MainAxisSize.min, // 让内容自适应高度
                          children: snapshot.data!.map((post) {
                            return Container(
                              margin: const EdgeInsets.symmetric(vertical: 8),
                              padding: const EdgeInsets.all(12),
                              decoration: BoxDecoration(
                                border: Border.all(color: Colors.grey.shade300),
                                borderRadius: BorderRadius.circular(8),
                              ),
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(post['title'], style: const TextStyle(fontWeight: FontWeight.bold)),
                                  const SizedBox(height: 4),
                                  Text(post['body'], style: const TextStyle(fontSize: 13)),
                                ],
                              ),
                            );
                          }).toList(),
                        );
                      }
                      // 无数据情况
                      else {
                        return const Text('暂无数据');
                      }
                    },
                  ),
                  actions: [
                    TextButton(
                      onPressed: () => Navigator.pop(context),
                      child: const Text('关闭'),
                    ),
                  ],
                );
              },
            );
          },
          child: const Text('弹出对话框'),
        ),
      ),
    );
  }
}

方案二:自定义StatefulWidget(更灵活)

如果后续需要扩展功能(比如添加重试按钮、手动刷新),可以把弹窗内容封装成独立的带状态组件,逻辑更清晰。

自定义弹窗内容组件

class DialogContent extends StatefulWidget {
  const DialogContent({super.key});

  @override
  State<DialogContent> createState() => _DialogContentState();
}

class _DialogContentState extends State<DialogContent> {
  late Future<List<dynamic>> _postFuture;

  @override
  void initState() {
    super.initState();
    // 组件初始化时发起请求
    _postFuture = fetchPosts();
  }

  Future<List<dynamic>> fetchPosts() async {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts?_limit=3'));
    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception('加载失败');
    }
  }

  // 重试请求方法(可扩展)
  void _retryFetch() {
    setState(() {
      _postFuture = fetchPosts();
    });
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<dynamic>>(
      future: _postFuture,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const SizedBox(
            height: 100,
            child: Center(child: CircularProgressIndicator()),
          );
        } else if (snapshot.hasError) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('出错了:${snapshot.error}'),
              TextButton(
                onPressed: _retryFetch,
                child: const Text('重试'),
              ),
            ],
          );
        } else if (snapshot.hasData) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: snapshot.data!.map((post) {
              return Container(
                margin: const EdgeInsets.symmetric(vertical: 8),
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey.shade300),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(post['title'], style: const TextStyle(fontWeight: FontWeight.bold)),
                    const SizedBox(height: 4),
                    Text(post['body'], style: const TextStyle(fontSize: 13)),
                  ],
                ),
              );
            }).toList(),
          );
        } else {
          return const Text('暂无数据');
        }
      },
    );
  }
}

使用自定义组件

在showDialog中直接引用这个组件即可:

showDialog(
  context: context,
  builder: (context) {
    return AlertDialog(
      title: const Text('加载的数据'),
      content: const DialogContent(),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('关闭'),
        ),
      ],
    );
  },
);

内容的提问来源于stack exchange,提问作者Ritwik Das

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:25:59