0

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
}
Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
Marco Groot
  • 49
  • 1
  • 6

2 Answers2

3

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(),
      ...
  }
}
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
0

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 ...;
    });
}
Peter Koltai
  • 8,296
  • 2
  • 10
  • 20
  • 1
    Gives me the following error 'LateInitializationError: Field '_future@27067882' has not been initialized.' – Marco Groot Jan 23 '22 at 09:07
  • 1
    Does your `initState` run? Can you check it with printing `_future` at the end of `initState`? Does your `_downLoadFiles` return a `Future`? Anyway, instead of `late Future _future;` you can also try: `final _future = _downlloadFiles();` and remove `_future` from `initState`. – Peter Koltai Jan 23 '22 at 09:16