Search code examples
flutterdartfutureflutter-futurebuilder

How to clone Future in Dart/Flutter


I have a custom FLutter widget that receives a future as a parameter to it's constructor and has FutureBuilder inside.

I want to add the ability to rerun this future in case of connection error, but the problem is that future after first usage is "exhausted". So I need some way to create a copy of the future passed as a parameter to use the copy inside FutureBuilder every time user press the Reload button.

import 'package:flutter/material.dart';

class CustomFutureBuilder<T> extends StatefulWidget {
  final Future<T> future;

  const CustomFutureBuilder({super.key, required this.future});

  @override
  State<CustomFutureBuilder<T>> createState() => _CustomFutureBuilderState<T>();
}

class _CustomFutureBuilderState<T> extends State<CustomFutureBuilder<T>> {
  late Future<T> _future;

  @override
  void initState() {
    // I need to use initState to initialize _future for the first usage of this widget
    _future = widget.future; // ← here I want to create a future clone and use it instead of original
    super.initState();
  }

  @override
  void didUpdateWidget(covariant CommonFutureBuilder<T> oldWidget) {
    _future = widget.future; // ← and here too
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _future,
      builder: (context, snapshot) {
        if (snapshot.connectionState != ConnectionState.done) {
          return const LinearProgressIndicator();
        }
        if (snapshot.hasError) {
          debugPrint('Loading error: ${snapshot.error}');
          return Column(
            children: [
              const Text(
                  'Loading error is occurred. Check the internet connection or try again later.'),
              OutlinedButton(
                onPressed: () {
                  setState(() {
                    // ← and maybe here too, I am not sure
                  });
                },
                child: const Text('Reload'),
              ),
            ],
          );
        }

        final data = snapshot.data as T;
        return …
      },
    );
  }
}


Solution

  • So I need some way to create a copy of the future

    You can't. One way to solve this is instead of injecting a Future<T> you inject a factory producing a Future<T>.

    So instead of _future getting passed into your constructor, pass a Function(Future<T>) futureFactory.

    In your InitState you need to call it once:

    _future = futureFactory();
    

    And then when someone wants to run the future again, you do the same thing:

    _future = futureFactory();
    

    You cannot rerun a future, but if you have a way to create those futures, you are good to go.