Marco Groot
Marco Groot

Reputation: 105

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
}

Upvotes: 0

Views: 6078

Answers (2)

jamesdlin
jamesdlin

Reputation: 90135

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(),
      ...
  }
}

Upvotes: 4

Peter Koltai
Peter Koltai

Reputation: 9754

Try the code below, using late keyword, but there are other options for that. I think you don't need the addPostFrameCallBack:

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

@override
void initState() {
  super.initState();
  _future = _downloadFiles();
}

Future<dynamic> _downloadFiles() async {

}

Widget build(BuildContext context) {
  return FutureBuilder<dynamic>(
    future: _future,
    builder: (context, snapshot) {
      if (snapshot.hasData || snapshot.data != null) {
        // build your widget
      }
      // progress indicator or something while future is running
      return ...;
    });
}

Upvotes: 0

Related Questions