Home > Mobile >  Flutter, How can I add a loading spinner indicator while my data is being send to the back within my
Flutter, How can I add a loading spinner indicator while my data is being send to the back within my

Time:10-25

This is my current screen. I need to add a loading spinner so that the user doesn't need to wait for 5 seconds until the data is sent to the back and returns the expected response. I added a comment where I call the function "uploadImage" and it's going to send an image to my back and return a status code. With that status code, I display different popups. However, the time between the user pressing the button and the popups appearing is around 5 seconds depending on the internet connection. I would like to display a loading spinning animation until the popups appear with the result. Thanks for the help!

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> {

  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: (){
                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)
                ),
              // Image.file(File(widget.pictureFile!.path))
              onPressed: () async {

                final status = await uploadImage();
                if (status == 200){
                  createAlertDialog(context);
                }
                else {
                  createErrorAlertDialog(context);
                }
              }, 
              child: const Text("ENVIAR"))
          ],
          ),
        ],
      ),
    );
  }
}

CodePudding user response:

You can easily declare something like isLoading:


  ValueNotifier<bool> isLoading = ValueNotifier(false);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Confirmar")),
      body: ValueListenableBuilder(
  valueListenable: isLoading,
  builder: (context, value, child) {
    return Column(
        children: [
          if(value)
            const CircularProgressIndicator()
          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: (){
                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)
                ),
              // Image.file(File(widget.pictureFile!.path))
              onPressed: () async {
                isLoading.value = true;
                final status = await uploadImage();
                isLoading.value = false;
                if (status == 200){
                  createAlertDialog(context);
                }
                else {
                  createErrorAlertDialog(context);
                }
              }, 
              child: const Text("ENVIAR"))
          ],
          ),
        ],
      );
  },
)
    );
  }
}

CodePudding user response:

Wrap your top-most parent widget in a Stack.

To make sure that it displays when something is happening,

  • declare a setState inside your Button's onPressed, and set isLoading to true.
  • After the process is complete, declare another setState and assign false to isloading.
Stack (
  children: [
   // add all your children here

   if (isLoading)
        const Opacity(
          opacity: 0.8,
          child: ModalBarrier(
            dismissible: false,
            color: Colors.black,
          ),
        ),
    if (isLoading) const Center(child: CircularProgressIndicator())
 ]
);
  • Related