Show circular progress indicator, async task - flutter

I am trying to show a circular progress indicator, while I run an async task. The async task is triggered by a button click and is also directing the user to a new page. The code looks like this:
child: GestureDetector(
onTap: () async{
//some async task that takes a few seconds
Navigator.push( ...
}
I want the user, when he clicks the button to first see a circular progress indicator and when the task is complete he should be directed to another screen. But I have no idea how to show him this progress indicator.
Thanks for your answers in advance!

I made a little simulation of what you want:
Check the code below:
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// boolean variable to decide when to show progress indicator
bool showProgress = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: InkWell(
onTap: () async {
// set the progress indicator to true so it will be visible
setState(() {
showProgress = true;
});
// perform asynchronous task here
await Future.delayed(Duration(seconds: 4), null);
setState(() {
// set the progress indicator to true so it would not be visible
showProgress = false;
// navigate to your desired page
Navigator.push(context,
MaterialPageRoute(builder: (context) => AnotherScreen()));
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Your widgets can stay here',
style: TextStyle(fontSize: 20),
),
SizedBox(
height: 20,
),
Container(
height: 50,
width: 50,
color: Colors.blue,
child: Center(
// use ternary operator to decide when to show progress indicator
child: showProgress
? CircularProgressIndicator()
: Text('Tap me'),
),
),
],
),
),
),
);
}
}
Check output below:
Output here
NOTE: For the sake of simplicity(the example above was used). To avoid calling setState multiple times. You can use a state management technique like Bloc or Provider and maybe decide to make use of a service locator for injection.

You can use this library https://pub.dev/packages/simpleprogressdialog
Create an object of ProgressDialog
ProgressDialog progressDialog = ProgressDialog(context: context, barrierDismissible: false);
Then invoke showMaterial before your async task
progressDialog.showMaterial(layout: MaterialProgressDialogLayout.overlayWithCircularProgressIndicator);
Then after the async task, dismiss the dialog.
progressDialog.dismiss();

Related

Clicking the button does not show the loader in Flutter

I need to add a loader to the button. That is, when you click on the Save button, you need to show the CircularProgressIndicator. I wanted to implement this through Bloc, because if I do this through setState (() {}), then I will have to rebuild the widget and the new added data on the page will disappear. Therefore, I want to do it through Bloc. I created a variable there and assign values ​​to it, the values ​​change but the loader does not update, tell me how can I make the loader show?
return BlocBuilder<EditPhotoCubit, EditPhotoState>(
builder: (context, state) {
return SizedBox(
height: size.height,
width: size.width,
child: _child(size, topPadding, context, cubitEditPhoto,
mainState.charging),
);
});
}
return const SizedBox();
});
...
SizedBox(
width: 148,
child: cubitEditPhoto.isLoading
? const CircularProgressIndicator(
color: constants.Colors.purpleMain)
: DefaultButtonGlow(
text: 'Save',
color:Colors.purpleMain,
shadowColor: Colors.purpleMain,
textStyle: Styles.buttonTextStyle,
onPressed: () async {
cubitEditPhoto.isLoading = true;
await cubitEditPhoto
.uploadImage(widget.chargingId)
.then((value) {
cubitEditPhoto.isLoading = false;
});
},
),
),
cubit
class EditPhotoCubit extends Cubit<EditPhotoState> {
EditPhotoCubit() : super(EditPhotoInitial());
bool isLoading = false;
}

Showing a button to the user based on the data entered in the TextFormField

final TextEditingController _weight = TextEditingController();
if (_weight.text.contains(RegExp(r'[0-9]')))
Padding(
padding: const EdgeInsets.only(bottom: 20),
child: BMIButton(
onpressed: () {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.rightToLeft,
child: BMIHeight(),
inheritTheme: true,
ctx: context),
);
},
))
I'm trying to show an OutlinedButton when the user enters some data into the textFormField. When I enter a value in the TextFormField and confirm, it doesn't show a button, but when I hot reload it, it sees that value and shows the button.
Try to add listener and call setState to update ui
late final TextEditingController _weight = TextEditingController()
..addListener(() {
setState(() {});
});
And override the dispose method and depose this controller.
This means you need just to update the state of your widget, first make sure this inside a StatefulWidget, then add a SetState(() {}) on the end of that method:
if (_weight.text.contains(RegExp(r'[0-9]')))
Padding(
padding: const EdgeInsets.only(bottom: 20),
child: BMIButton(
onpressed: () {
Navigator.push(
context,
PageTransition(
type: PageTransitionType.rightToLeft,
child: BMIHeight(),
inheritTheme: true,
ctx: context),
);
},
))
//...
setState(() {}) // add this
TextEditingController is implementing Listenable. You can exploit that to build part of your UI conditionally.
Moreover, TextEditingController must be correctly initialized and disposed of. You can do that smoothly with flutter_hooks.
You'd obtain this concise result:
class MyWidget extends HookWidget {
const MyWidget({super.key});
#override
Widget build(BuildContext context) {
final textController = useTextEditingController();
return Column( // example, I'm not sure of what you've got there
children: [
TextField(controller: textController), // Your Text Field
ValueListenableBuilder(
valueListenable: textController,
builder: (context, value, child) {
return value.text.contains(...)
? OutlinedButton( // Your Button
onPressed: () {
// .. do stuff
},
child: const Text('I am ready to be pressed'),
)
: const SizedBox.shrink(); // This is empty, but you can render whatever
},
)
],
);
}
}
Your code that checks if the TextFormField is populated needs to be inside the build function of a stateful widget.
To trigger the state update, listen to changes on the TextFormField, inside the function set the state of some variable that you can check.
Add this to the initState method:
_weight.addListener(() {
setState(() {
_inputText = _weight.text; // Create this variable inside the state class
});
});
And change your if statement as follows:
if (_inputText.contains(RegExp(r'[0-9]')))
Method 2
You could also Wrap your Widget with a Visibility Widget:
Visibility(
visible: RegExp(r'[0-9]').hasMatch(_inputText),
child: [YOUR WIDGET],
)
This still needs the listener with the setState call.

Flutter update refresh previous page when page has been pushed via a stateless widget

So here is the problem.
TabScreen() with 3 pages and one fabcontainer button (Stateless widget).
When pressed the fabcontainer will give you the chances of make one upload, after the upload i would like to refresh one of the page of the tabscreen.
return Container(
height: 45.0,
width: 45.0,
// ignore: missing_required_param
child: FabContainer(
icon: Ionicons.add_outline,
mini: true,
),
);
}
OnTap of the fabcontainer:
Navigator.pop(context);
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => CreatePost(),
),
);
},
Cannot add a .then(){setState... } because it is a stateless widget and i need to set the state of a precise page, not of the fabcontainer.
Any idea?
Thanks!
Define a updateUi method inside your TabScreen (which defines the pages)
TabScreen:
void updateUi(){
// here your logic to change the ui
// call setState after you made your changes
setState(() => {});
}
Pass this function as a constructor param to your FabContainer button
FabContainer(
icon: Ionicons.add_outline,
mini: true,
callback: updateUi,
),
Define it in your FabContainer class
final Function() callback;
Call it to update the ui
callback.call();
So what Ozan suggested was a very good beginning but i could not access the stateful widget in order to set the state.
What i did on top of Ozan's suggestion was giving the state a globalkey:
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
Assigning it to the scaffold:
return Scaffold(
key: scaffoldKey,
Making the state public removing the _MyPizzasState -> MyPizzasState
Creating a method to refresh the data:
refreshData() {
pizzas = postService.getMyPizzas();
setState(() {
});
}
Assigning a key during the creation of the MyPizzaPage:
final myPizzasKey = GlobalKey<MyPizzasState>();
{
'title': 'My Pizza',
'icon': Ionicons.pizza_sharp,
'page': MyPizzas(key: myPizzasKey),
'index': 0,
},
And, how Ozan said once i received the callback :
buildFab() {
return Container(
height: 45.0,
width: 45.0,
// ignore: missing_required_param
child: FabContainer(
icon: Ionicons.add_outline,
mini: true,
callback: refreshMyPizzas,
),
);
}
void refreshMyPizzas() {
print("Refreshing");
myPizzasKey.currentState?.refreshData();
}

My flutter app, generate multiple register in DB when I press button once

I have an app that contains a form. First, you have to authenticate with your ID, a function checks the date of your last register (if you don't register today, you'll pass. If not you can not log into the form screen). Second, you register your symptoms once a day(restriction of the app), press "ENVIAR(send)" and a POST method storages your data in a DB. It's simple.
In theory, I should see one register per person per day. Now the number of devices with my app installed has increased I can see multiple registers per person in some cases. Curiously the problem happens in just some devices. I cannot detect the problem because with my phone I've never had these kinds of problems (HUAWEI P30 LITE).
I try to debug my code, but it all works perfectly. Could you help or advise me on how to solve this problem, please?
PDT: when I press the button "SEND", the function _submit() is executed, which allows POST the data in the DB. Additionally, to verify the systems works right, I use a developer account with credential "000000000". I had to eliminate many lines, but these are the most important.
Have a nice day.
Thanks
I attach the code:
class Tamizaje1Page extends StatefulWidget {
#override
_Tamizaje1PageState createState() => _Tamizaje1PageState();
}
class _Tamizaje1PageState extends State<Tamizaje1Page> {
final usuariosProvider = new UsuariosProvider();
final appProvider = new AppProvider();
final formKey = GlobalKey<FormState>();
final productoProvider = new ProductosProvider();
ProductoModel producto = new ProductoModel();
AppModel app = new AppModel();
#override
Widget build(BuildContext context) {
final bloc = Provider.of(context);
mostrarPosision();
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Center(child: Text('Cuestionario Diario')),
),
body: WillPopScope(
onWillPop: (){Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context) => LoginPage()), (route) => false);},
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(15.0),
child: Form(
key: formKey,
child: Column(
children: <Widget>[
_crearBoton1(context),
],
),
),
),
),
),
);
}
Widget _crearBoton1( BuildContext context) {
final size = MediaQuery.of(context).size;
return ButtonTheme(
minWidth: size.width*0.2,
height: size.height*0.07,
focusColor: colorApp,
child: RaisedButton(
child: Container(padding: EdgeInsets.symmetric( horizontal: 80.0, vertical: 15.0),child: Text('Enviar', style: TextStyle(color: Colors.white,fontSize: 16.0),)),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25.0),side: BorderSide(color: Colors.transparent,width: 2.0)),
elevation: 0.0,
color: colorApp,
textColor: Colors.white,
onPressed: ( _guardando ) ? null : _submit,
),
);
}
//THIS FUNCTION SENDS THE DATA WHEN PRESS THE BUTTON
_submit() async {
_noVisibleData();
if ( !formKey.currentState.validate() ) return;
formKey.currentState.save();
if(producto.nroDoc!="000000000" && _currentPosition != null && ((producto.meSiento=='1' && app.diagnostico!='-1') || (producto.meSiento=='0'))){
bool permtirenvioToDB= await productoProvider.crearProducto(producto); //envia los datos
if(permtirenvioToDB==true){
_alertaDeEnvio(context, envio1);
await enviarGMAIL();
appProvider.crearApp(app);
Navigator.push(context, MaterialPageRoute(builder: (context) => AlertPage()));
}else{
contarIntetentosEnvioDB++;
if(contarIntetentosEnvioDB<5)_soloMensaje(context,"Se ha producido un error al enviar el formulario. Por favor... ¡Inténtalo nuevamente!","assets/alerta0/a0i1.svg",80.0);
else _soloMensaje(context,"Por favor, ¡Comuníquese con el área de Tecnologías de la Información!","assets/alerta0/a0i3.svg",80.0);
}
}else{
_alertaDeEnvio(context, envio1);
print('Prueba de desarrollador');
Navigator.push(context, MaterialPageRoute(builder: (context) => AlertPage()));
}
}
}
Thanks, i debugged many option and realised the errors occurs because i have 3 future functions wich execute when i press the button (inside the butto widget). I created i new page with an additional #averride before Widget build exclusive to execute future functions, and it works good.
class _MailDBSendState extends State<MailDBSend> {
#override
void initState() {
super.initState();
_dataBase();
_sendMail();
}
.
.
.
.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(

In Dart/Flutter, how do I use a variable from a method so I can ouput it to a text field

Hope somebody can help - I hit this dead end a few weeks ago and think that I've tried everything within my limited knowledge.
I've set up a database that works OK - that is I can add data on one screen, review the data and edit the data on another screen. Now I want to sum one of the columns (beef) which I've been able to do as proven in the 'debugPrint' to the console. I want to access this variable 'beefTotal' from the 'sumBeef' method and print show this in a text field in the UI. I just can't manage it though. It just returns null.
Thanks in advance for any help.
import 'package:flutter/material.dart';
import 'package:take_note/utils/database_helper.dart';
class Info extends StatefulWidget {
#override
State<StatefulWidget> createState() => _InfoState();
}
DatabaseHelper helper = DatabaseHelper();
var database = DatabaseHelper();
class _InfoState extends State<Info> {
List beefTotal;
#override
Widget build (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Beef Info"),
backgroundColor: Colors.lightBlueAccent,
),
body: Container(
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: RaisedButton(
onPressed: (){
sumBeef();
},
),
),
),
Expanded(
child: Center(
child: Text("Total Beef is: £ $beefTotal", style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 30.0,
fontWeight: FontWeight.w400
),),
),
),
],
),
)
);
}
void sumBeef () async {
beefTotal = await database.addBeef();
debugPrint("Total beef: $beefTotal");
}
}
The code below is from a class called DatabaseHelper which the method sumBeef() uses
Future<List<Map<String, dynamic>>> addBeef()async{
Database db = await this.database;
var result = await db.rawQuery("SELECT SUM(beef) FROM $table");
return result;
}
```[enter image description here][1]
[1]: https://i.stack.imgur.com/L46Gj.png
Just call
setState({
});
void sumBeef () async {
beefTotal = await database.addBeef();
setState(() {});
debugPrint("Total beef: $beefTotal");
}
and your good! anytime you make a change you have to call setState method to update the ui (rebuild) in flutters case