skip to Main Content

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 …
      },
    );
  }
}

2

Answers


  1. You can try StreamBuilder replace FutureBuilder, because Future can’t listen to a variable change. It’s a one-time response. Instead, you’ll need to use a Stream for refresh or event by user.

    This is code for your case :

          late StreamController<T> _conStream;
          @override
          void initState() {
            _conStream= StreamController<T>(); 
            super.initState();
          }
      @override
      void dispose() async {
        _conStream.onCancel;
       await  _conStream.close();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return StreamBuilder(
              stream: _conStream.stream,
    ...
    
    • function for refresh :

       _conStream.add(await Future.delayed(Duration(seconds: 5), () => 'Done') // as example);
      
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search