Showing a Progress Indicator easily in Flutter

Rafael Perez
Nerd For Tech
Published in
3 min readJun 10, 2021
Photo by Mike van den Bos on Unsplash

Is a fact that asynchronous operations are necessary for almost every app we code. Sometimes, the user must wait until the operation finish to be able to interact with the app again, and that is exactly the purpose of the progress indicators, to give feedback to the user so they can know that something is being processed.

Is on us to prevent the user to interact with the app while the asynchronous operation is loading, and the way to do this is by using dialogs. The easiest way to show a Dialog in Flutter is by calling the function showDialog(), which receives a context and a builder:

show(BuildContext context, {String text = 'Loading...'}) {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
_context = context;
isDisplayed = true;
return WillPopScope(
onWillPop: () async => false,
child: SimpleDialog(
backgroundColor: Colors.white,
children: [
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 16, top: 16, right: 16),
child: CircularProgressIndicator(),
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(text),
)
],
),
)
] ,
),
);
}
);
}

The context is necessary to tell flutter from which widget is being called the dialog, and the builder is where we define the UI for the dialog. We also have the parameter barrierDismissible, when we define this parameter as false we are preventing the user to dismiss the dialog by touching the screen area outside the dialog. But this parameter does not prevent the user to dismiss the dialog by tapping the hardware back button in an android device, in order to avoid this, we wrap the SimpleDialog with a WillPopScope which returns false when the callback onWillPop is called.

Okay, we have defined our showDialog function to display our progress dialog, now we just need an easy way to reuse it from anywhere in our code. To achieve this we will create a class to encapsulate our function and we will use the singleton pattern to avoid the creation of a new object each time we want to call the function. Our class will look like this:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class LoadingIndicatorDialog {
static final LoadingIndicatorDialog _singleton = LoadingIndicatorDialog._internal();
late BuildContext _context;
bool isDisplayed = false;

factory LoadingIndicatorDialog() {
return _singleton;
}

LoadingIndicatorDialog._internal();

show(BuildContext context, {String text = 'Loading...'}) {
if(isDisplayed) {
return;
}
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
_context = context;
isDisplayed = true;
return WillPopScope(
onWillPop: () async => false,
child: SimpleDialog(
backgroundColor: Colors.white,
children: [
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 16, top: 16, right: 16),
child: CircularProgressIndicator(),
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(text),
)
],
),
)
] ,
),
);
}
);
}

dismiss() {
if(isDisplayed) {
Navigator.of(_context).pop();
isDisplayed = false;
}
}
}

If you read the code, you will find that in the show() method we are assigning the value of the dialog builder context to a class member private variable _context. The purpose of this variable is to save the context from where our dialog is being called. Then, in the method dismiss() we can use that context to pop() the dialog. Also, we have the variable isDisplayed. This variable will prevent us to display the dialog twice and will help us to avoid trying to dismiss an inexistent dialog.

Now we are ready to show the progress indicator from anywhere in our code like this:

LoadingIndicatorDialog().show(context);
await asynchronousOperation();
LoadingIndicatorDialog().dismiss();

And that is it! Now we can easily display our progress indicator from any place in our code. Hope this information has been helpful to you. See you in another story.

--

--

Rafael Perez
Nerd For Tech

Mobile developer. I’m interested on everything related to the life cycle of an app, design, development, and management.