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

Flutter技术咨询:API列表中CircularProgressIndicator的放置位置

解决Flutter加载PDF时显示CircularProgressIndicator的问题

核心思路

要在点击列表项加载PDF时显示加载指示器,需要通过状态变量控制加载状态,并在UI层根据状态渲染指示器。具体步骤如下:

  • 添加布尔类型状态变量标记PDF是否正在加载
  • 点击列表项时更新加载状态,加载完成后恢复状态
  • 在UI中叠加显示加载指示器(覆盖全屏或列表项区域)

修改后的完整代码

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:fluttertest/controller/pdf_api.dart';
import 'package:fluttertest/view/announce_card.dart';
import 'package:fluttertest/view/pdf_viewer.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('REST API Example'),
        ),
        body: Center(
          child: FetchDataWidget(),
        ),
      ),
    );
  }
}

class FetchDataWidget extends StatefulWidget {
  @override
  _FetchDataWidgetState createState() => _FetchDataWidgetState();
}

class _FetchDataWidgetState extends State<FetchDataWidget> {
  Map<String, dynamic> data = {"message": []};
  // 添加状态变量:标记PDF是否正在加载
  bool _isLoadingPdf = false;

  @override
  void initState() {
    super.initState();
  }

  Future<Map<String, dynamic>> fetchData() async {
    var headers = {
      'API-KEY': '(Confidential)',
      'Authorization': '(Confidential)'
    };
    var request =
        http.Request('GET', Uri.parse('http://vmo.nuboxtech.com/sfa-api/news'));
    request.headers.addAll(headers);

    http.StreamedResponse response = await request.send();

    if (response.statusCode == 200) {
      var raw = await response.stream.bytesToString();
      data = json.decode(raw);
      print(data);
      print("Data datatype: ${data.runtimeType}");
    } else {
      print(response.reasonPhrase);
    }
    return data;
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
          child: FutureBuilder(
            future: fetchData(),
            builder: (BuildContext context, snapshot) {
              if (snapshot.hasData) {
                print("Has data: ${snapshot.requireData.toString()}");
                return ListView.builder(
                  itemCount: snapshot.requireData["message"].length,
                  itemBuilder: (context, index) {
                    return GestureDetector(
                      onTap: () async {
                        // 点击时设置加载状态为true
                        setState(() {
                          _isLoadingPdf = true;
                        });
                        try {
                          String pdf = snapshot.requireData["message"][index]['File'];
                          final url = pdf;
                          final file = await PDFApi.loadNetwork(url);
                          openPDF(context, file);
                        } catch (e) {
                          // 处理加载失败的情况
                          print('PDF加载失败: $e');
                          Get.snackbar('错误', 'PDF加载失败,请稍后重试');
                        } finally {
                          // 无论成功失败,恢复加载状态
                          setState(() {
                            _isLoadingPdf = false;
                          });
                        }
                      },
                      child: AnnounceCard(
                          title: snapshot.requireData["message"][index]['Title'],
                          theImage: snapshot.requireData["message"][index]['Image']),
                    );
                  },
                );
              } else if (snapshot.hasError) {
                print("Has error: ${snapshot.error}");
                return Center(child: Text('数据加载失败'));
              } else {
                print("No data");
                return Center(child: CircularProgressIndicator());
              }
            },
          ),
        ),
        // 加载状态显示全屏指示器
        if (_isLoadingPdf)
          Container(
            color: Colors.black.withOpacity(0.5),
            child: Center(
              child: CircularProgressIndicator(color: Colors.white),
            ),
          )
      ],
    );
  }

  void openPDF(BuildContext context, File file) {
    Get.to(PDFViewerPage(
      file: file,
    ));
  }
}

关键修改点说明

  1. 添加状态变量:在_FetchDataWidgetState中新增_isLoadingPdf,用于追踪PDF加载状态
  2. 更新点击逻辑:
    • 点击时通过setState将_isLoadingPdf设为true,触发UI更新显示指示器
    • 使用try-catch-finally确保无论加载成功或失败,最终都会恢复加载状态,隐藏指示器
  3. UI层叠加指示器:用Stack包裹原内容,当_isLoadingPdf为true时,显示半透明背景的全屏加载指示器,提升用户体验

可选优化方案

  • 如果希望每个列表项单独显示加载状态,可将_isLoadingPdf改为int? _loadingIndex,记录当前加载的列表项索引,在对应item上显示小型指示器
  • 使用GetX状态管理替代setState,适配更复杂的状态场景
  • 将加载指示器封装为独立组件,提高代码复用性

内容的提问来源于stack exchange,提问作者arief ariefshamsuddin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 10:14:53