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'sonPressed
, and setisLoading
to true. - After the process is complete, declare another
setState
and assign false toisloading
.
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())
]
);