Giulia Santoiemma
Giulia Santoiemma

Reputation: 312

Show CircularProgressIndicator before showDialog

In my Flutter application I need to allow admin users to download a CSV file with all the data entered into Firebase.

To do this, admins must press a button. If they do it from the web, the file is downloaded via the browser, while if they are on another device, a dialog opens asking if they want to download or share the file.

Creating the file takes some time, so I would like to show a CircularProgressIndicator while waiting for the AlertDialog to open. I don't know how to do this though, because I typically show the CircularProgressIndicator when calling a Future function via FutureBuilder, but showDialog doesn't return a Widget.

So how can I show a CircularProgressIndicator before opening AlertDialog?

Here is my code, thanks in advance!

FilledButton.icon(
  onPressed: () async {
    // This function creates a CSV file by retrieving data from Firebase,
    // and takes some time to run
    final String fileData = await getCsv();

    if (kIsWeb) {
      // running on the web
      downloadFileOnWeb(fileData);
    } else {
      // NOT running on the web
      if (!context.mounted) return;
      return showDialog<void>(
        context: context,
        builder: (context) => AlertDialog(
          content: const Text("Get report"),
          actions: [
            IconButton(
              icon: const Icon(Icons.download),
              onPressed: () => downloadFile(fileData),
            ),
            IconButton(
              icon: const Icon(Icons.share),
              onPressed: () => shareFile(fileData),
            ),
          ],
        ),
      );
    }
  },
  icon: const Icon(Icons.file_open),
  label: const Text("Report"),
),

Upvotes: 1

Views: 119

Answers (1)

MendelG
MendelG

Reputation: 20018

Why don't you simply use a bool to keep track of the loading state?

For example:

In your state class, create:

class _CsvPageState extends State<CsvPage> {
  bool _isLoading = false;

and use it in your function:

  Future<void> _handlePress() async {
    setState(() {
      _isLoading = true; // set it to true to show the loading indicator
    });

    final String fileData = await getCsv(); // your task, i.e, fetching the CSV data.

    setState(() {
      _isLoading = false; // after the fetching is done, set it back to false
    });

    _showDownloadDialog();
  }

and in your build, show different widgets based on the state;:

FilledButton.icon(
      onPressed: _isLoading ? null : _handlePress,
      icon: _isLoading // If loading, show a loading indicator
          ? const SizedBox(child: CircularProgressIndicator())
          : const Icon(Icons.file_open),
      label: const Text("Report"),
    );

complete runnable snippet:

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(
      title: 'Stackoverflow Answers',
      home: const MyHomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: const Center(
        child: CsvPage(),
      ),
    );
  }
}

class CsvPage extends StatefulWidget {
  const CsvPage({Key? key}) : super(key: key);

  @override
  _CsvPageState createState() => _CsvPageState();
}

class _CsvPageState extends State<CsvPage> {
  bool _isLoading = false;

  Future<void> _handlePress() async {
    setState(() {
      _isLoading = true;
    });

    final String fileData = await getCsv(); // Fetch the CSV data

    setState(() {
      _isLoading = false;
    });

    _showDownloadDialog(fileData);
  }

  void _showDownloadDialog(String fileData) {
    showDialog<void>(
      context: context,
      builder: (context) => AlertDialog(
        content: const Text("Get report"),
        actions: [
          IconButton(
            icon: const Icon(Icons.download),
            onPressed: () => Navigator.of(context).pop(),
          ),
          IconButton(
            icon: const Icon(Icons.share),
            onPressed: () => Navigator.of(context).pop(),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return FilledButton.icon(
      onPressed: _isLoading ? null : _handlePress,
      icon: _isLoading // If loading, show a loading indicator
          ? const SizedBox(child: CircularProgressIndicator())
          : const Icon(Icons.file_open),
      label: const Text("Report"),
    );
  }
}

Future<String> getCsv() async {
  // Simulate a delay to fetch the CSV data
  await Future.delayed(const Duration(seconds: 3));
  return 'Bla bla bla, do your CSV data fetching';
}

See also

Upvotes: 1

Related Questions