Search code examples
flutterasynchronousdart-null-safety

Initializing a future in flutter?


I would like to run a downloading Future function when opening a page in flutter, however it is being called multiple times.

I would like to implement a solution like the second in this article:

https://flutterigniter.com/future-async-called-multiple-times/

(memoizing the future after initialisation so that the init function is not called multiple times) however in his solution, he initialises the future like this

Future<String> _future;

this is no longer possible in the current version of dart and I was wondering if there was an equivalent, I have tried using the Late keyword and initializing it to null, neither of which work.

Here is the code currently and how I want it currently:

class _ARState extends State<AR> {
  
@override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((_) {
      _downloadFiles();
    });
  }

Future<dynamic> _downloadFiles() async {
// some downloading function that is getting run multiple times ....
}


Widget build(BuildContext context) {
    return FutureBuilder<dynamic>(
      future: _downloadFiles(),
      builder: /// stuff i want built
}

how I want it:

class _ARState extends State<AR> {
  
Future<dynamic> _future;

@override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addPostFrameCallback((_) {
      _downloadFiles();
    });
  }

Future<dynamic> _downloadFiles() async {
// some downloading function that is getting run multiple times ....
}


Widget build(BuildContext context) {
    return FutureBuilder<dynamic>(
      future: _future,
      builder: /// stuff i want built
}

Solution

  • One way is to make _future nullable and to make your asynchronous function idempotent by checking if _future is null. If it's null, do work; if it's not null, then just return the existing Future.

    class _ARState extends State<AR> {
      Future<dynamic>? _future;
    
      ...
    
      Future<dynamic> _downloadFiles() {
        Future<dynamic> helper() async {
          // Do actual downloading work here.
        }
    
        if (_future == null) {
          _future = helper();
        }
    
        return _future;
      }
    
      Widget build(BuildContext context) {
        return FutureBuilder<dynamic>(
          future: _downloadFiles(),
          ...
      }
    }