How to implement a loading dialog in Flutter

Updated: January 8, 2023 By: A Goodman 3 comments

This article shows you how to create a loading dialog from scratch in Flutter.

Overview

There are many scenarios where you have to perform an asynchronous computation (future), such as making HTTP requests, processing files, fetching data from a local database, etc. Because a future cannot provide a result immediately, the user has to wait for a while. In this case, we can display a loading dialog to let him or her know what is going on. Another benefit of the loading dialog is that it can prevent the user from interacting with the app (editing text fields, hitting buttons, or something like that) while the future is not done yet.

In Flutter, a normal dialog can be closed manually when the user taps somewhere outside it. However, a loading dialog should NOT be closed like that. It should only go away automatically when the future finishes., like so:

// show the loading dialog
showDialog(
        // The user CANNOT close this dialog  by pressing outsite it
        barrierDismissible: false,
        context: context,
        builder: (_) {
          return Dialog(
            /*
            put a CircularProgressIndicator() here
            */
          );
       }
);

// the future
await someFuture();

// close the dialog automatically
 Navigator.of(context).pop();

For more clarity, please see the complete example below.

Example

Preview

The tiny app we are going to build has a button in the center of the screen. When the button gets pressed, a loading dialog will show up. After 3 seconds, the loading dialog will automatically disappear.

The Code

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        // Remove the debug banner
        debugShowCheckedModeBanner: false,
        title: 'KindaCode.com',
        theme: ThemeData(
          primarySwatch: Colors.indigo,
        ),
        home: const HomePage());
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  void _fetchData(BuildContext context, [bool mounted = true]) async {
    // show the loading dialog
    showDialog(
        // The user CANNOT close this dialog  by pressing outsite it
        barrierDismissible: false,
        context: context,
        builder: (_) {
          return Dialog(
            // The background color
            backgroundColor: Colors.white,
            child: Padding(
              padding: const EdgeInsets.symmetric(vertical: 20),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: const [
                  // The loading indicator
                  CircularProgressIndicator(),
                  SizedBox(
                    height: 15,
                  ),
                  // Some text
                  Text('Loading...')
                ],
              ),
            ),
          );
        });

    // Your asynchronous computation here (fetching data from an API, processing files, inserting something to the database, etc)
    await Future.delayed(const Duration(seconds: 3));

    // Close the dialog programmatically
    // We use "mounted" variable to get rid of the "Do not use BuildContexts across async gaps" warning
    if (!mounted) return;
    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('KindaCode.com')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _fetchData(context),
          child: const Text('Load Data'),
        ),
      ),
    );
  }
}

Conclusion

You’ve learned how to make a loading dialog from native ingredients provided by Flutter. No third-party packages are required. If you’d like to explore more new and amazing things about modern Flutter development, take a look at the following articles:

You can also check out our Flutter category page or Dart category page for the latest tutorials and examples.

3 Comments
Inline Feedbacks
View all comments
Kostadin
Kostadin
1 year ago

This works for short progress duration.
I have to wait 60 seconds for operation to finish. And ehen try after that to call
Navigator.of(context).pop()
Flutter complains that context is pointing to not mounted widget.

shi
shi
2 years ago

i have tried but the dialog not close/pop, help

Frank
Frank
2 years ago

Thank you! Very helpful!

Related Articles