futureBuilder<F> method

Widget futureBuilder<F>({
  1. Future<F>? future(
    1. T? data,
    2. Future<T> asyncState
    )?,
  2. required Widget onWaiting()?,
  3. required Widget onError(
    1. dynamic
    )?,
  4. required Widget onData(
    1. F? data
    ),
  5. void dispose()?,
  6. On<void>? onSetState,
  7. Key? key,
})

Listen to a future from the injected model and rebuild this widget when it resolves.

After the future ends (with data or error), it will mutate the state of the injected model, but only ///rebuilds this widget.

  • Required parameters:
    • onWaiting : callback to be executed when the future is in the waiting state.
    • onError : callback to be executed when the future ends with error.
    • onData : callback to be executed when the future ends data.
  • Optional parameters:
    • future : Callback that takes the current state and async state of the injected model. If not defined and if the injected model is of type (InjectedFuture), the async state is used by default
    • dispose : called when the widget is removed from the widget tree.

If onWaiting or onError is set to null, the onData callback will be execute instead.

ex: In the following code the onData will be invoked when the future is waiting, hasError, or hasData

injectedModel.futureBuilder(
future : (s, asyncS) => someMethod(),
onWaiting : null, //onData is called instead
onError: null, // onData is called instead
onData: (data)=>SomeWidget(),
)

Performance: When this futureBuilder is removed from the widget tree, the future is canceled if not resolved yet.

Implementation

Widget futureBuilder<F>({
  Future<F>? Function(T? data, Future<T> asyncState)? future,
  required Widget Function()? onWaiting,
  required Widget Function(dynamic)? onError,
  required Widget Function(F? data) onData,
  void Function()? dispose,
  On<void>? onSetState,
  Key? key,
}) {
  return StateBuilderBase<_OnAsyncWidget<F>>(
    (widget, setState) {
      final inj = this as InjectedImp<T>;
      inj.initialize();

      StreamSubscription? subscription;
      bool isWaiting = true;
      F? data = _getPrimitiveNullState<F>();
      dynamic error;
      Future<F?>? f;
      SnapState<F?> snap = SnapState._nothing(null, '', '');
      VoidCallback? disposer;
      if (future != null) {
        f = future(inj._nullableState, inj.stateAsync);
      } else {
        f = stateAsync as Future<F?>;
      }
      disposer = inj._reactiveModelState.listeners.addListenerForRebuild(
        (_) {},
        clean: inj.autoDisposeWhenNotUsed
            ? () {
                inj.dispose();
              }
            : null,
      );

      return LifeCycleHooks(
        mountedState: (_) {
          subscription = f?.asStream().listen.call((d) {
            isWaiting = false;
            setState();
            onSetState?.call(snap._copyToHasData(d));
            if (d is T) {
              inj._reactiveModelState._snapState = SnapState<T>._withData(
                ConnectionState.done,
                d as T,
              );
              if (onSetState?._onData == null) {
                inj.onDataForSideEffect?.call(inj._state);
              }
              disposer?.call();
              disposer = null;
            }
            data = d;
          }, onError: (e, s) {
            isWaiting = false;
            setState();
            onSetState?.call(snap._copyToHasError(e, () {}, stackTrace: s));
            if (e != inj.error) {
              if (onSetState?._onError == null) {
                inj.onError?.call(e, s);
              }
            }
            error = e;
          });
          onSetState?.call(snap._copyToIsWaiting());
        },
        dispose: (_) {
          disposer?.call();
          dispose?.call();
          subscription?.cancel();
          // if (!inj._reactiveModelState._listeners.hasListeners) {
          //   inj.dispose();
          // }
        },
        builder: (ctx, widget) {
          if (isWaiting) {
            return widget.onWaiting?.call() ?? widget.onData(data);
          }
          if (error != null) {
            return widget.onError?.call(error) ?? widget.onData(data);
          }
          return widget.onData(data);
        },
      );
    },
    widget: _OnAsyncWidget<F>(
      onWaiting: onWaiting,
      onError: onError,
      onData: onData,
    ),
    key: key,
  );
}