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

Flutter:如何在数据上传至后端时添加加载指示器?

解决方案

要实现等待期间的加载动画,只需添加加载状态管理并在UI中对应展示即可,具体修改如下:

修改后的完整代码

import 'dart:convert';
import 'dart:ffi';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import  'package:intl/intl.dart';
import 'package:stopfire/home.dart';

import 'camera_screen.dart';

class ConfirmationScreen extends StatefulWidget {
  final XFile? pictureFile;
  final Position? position;
  const ConfirmationScreen({this.pictureFile, this.position, Key? key}) : super(key: key);
  @override
  State<ConfirmationScreen> createState() => _ConfirmationScreenState();
}

class _ConfirmationScreenState extends State<ConfirmationScreen> {
  // 新增加载状态变量
  bool _isLoading = false;

  createAlertDialog(BuildContext context){
    return showDialog(context: context, builder: (context) {
      return AlertDialog(
        title: const Text("¡Tu alerta fue enviada!"),
        content: ElevatedButton(
          style: ElevatedButton.styleFrom(
              primary: Colors.red,
              textStyle: const TextStyle(color: Colors.white)
          ),
          onPressed: (){
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(
                builder: (context) => const HomePage(),
              ),
            );
          },
          child: const Text("VOLVER"),),
      );
    });
  }

  createErrorAlertDialog(BuildContext context){
    return showDialog(context: context, builder: (context) {
      return AlertDialog(
        title: const Text("Hubo un error y no recibimos la imagen, vuelve a intentarlo"),
        content: ElevatedButton(
          style: ElevatedButton.styleFrom(
              primary: Colors.red,
              textStyle: const TextStyle(color: Colors.white)
          ),
          onPressed: (){
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(
                builder: (context) => const HomePage(),
              ),
            );
          },
          child: const Text("VOLVER"),),
      );
    });
  }
  
  Future<int> uploadImage ()async{
    var stream  = http.ByteStream(widget.pictureFile!.openRead());
    stream.cast();

    var length = await widget.pictureFile!.length();

    //var uri = Uri.parse('http://10.0.2.2:5000/analyze');
    //var uri = Uri.parse("http://172.20.10.4:5000/analyze");
    var uri = Uri.parse("http://10.230.61.125:5000/analyze");
    var request = http.MultipartRequest('POST', uri);

    request.files.add(
        http.MultipartFile(
            'file',
            File(widget.pictureFile!.path).readAsBytes().asStream(),
            File(widget.pictureFile!.path).lengthSync(),
            filename: widget.pictureFile!.path.split("/").last
        )
    );

    String date = DateFormat("dd/MM/yy HH:mm:ss").format(DateTime.now());

    request.fields['taken_at'] = date ;
    request.fields['latitude'] = widget.position!.latitude.toString();
    request.fields['longitude'] = widget.position!.longitude.toString();
    request.fields['id_camera'] = "App StopFire" ;
    request.fields['camera_type'] = "Punto móvil" ;
    request.fields['zone'] = 'Patrullero' ;

    var response = await http.Response.fromStream(await request.send()).catchError((onError) => 500);
    var status = response.statusCode;
    return status;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Confirmar")),
      body: Column(
        children: [
          Image.file(File(widget.pictureFile!.path)),
          Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                primary: Colors.red, 
                textStyle: const TextStyle(color: Colors.white)
                ),
              onPressed: _isLoading ? null : (){
                Navigator.pop(context);
              },
                child: const Text("VOLVER"),),
            const Padding(
              padding: EdgeInsets.fromLTRB(30.0,0,0,0)),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                primary: Colors.green,
                textStyle: const TextStyle(color: Colors.white)
                ),
              onPressed: _isLoading ? null : () async {
                setState(() {
                  _isLoading = true;
                });
                // 弹出全屏加载弹窗,禁止用户操作
                showDialog(
                  context: context,
                  barrierDismissible: false,
                  builder: (_) => const Center(
                    child: CircularProgressIndicator(color: Colors.white),
                  ),
                );
                try {
                  final status = await uploadImage();
                  Navigator.pop(context); // 关闭加载弹窗
                  if (status == 200){
                    createAlertDialog(context);
                  } else {
                    createErrorAlertDialog(context);
                  }
                } catch (e) {
                  Navigator.pop(context);
                  createErrorAlertDialog(context);
                } finally {
                  setState(() {
                    _isLoading = false;
                  });
                }
              }, 
              child: _isLoading ? const CircularProgressIndicator(color: Colors.white) : const Text("ENVIAR"))
          ],
          ),
        ],
      ),
    );
  }
}

关键修改说明

  • 新增_isLoading变量追踪上传状态,加载期间禁用所有按钮防止重复操作
  • 点击「ENVIAR」后弹出全屏加载弹窗,明确提示用户当前处于等待状态
  • 上传完成或出现异常时,先关闭加载弹窗,再显示结果弹窗
  • 按钮自身也会切换为小型加载动画,双重强化加载提示

内容的提问来源于stack exchange,提问作者Juan Cruz Alric Cortabarria

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 07:10:20