main
  1import 'package:blood_pressure_app/l10n/app_localizations.dart';
  2import 'package:flutter/material.dart';
  3
  4/// A future builder with app defaults.
  5///
  6/// This allows to have the same loading style everywhere in the app.
  7class ConsistentFutureBuilder<T> extends StatefulWidget {
  8  /// Create a future builder with app defaults.
  9  const ConsistentFutureBuilder({
 10    super.key,
 11    required this.future,
 12    this.onNotStarted,
 13    this.onWaiting,
 14    required this.onData,
 15    this.cacheFuture = false,
 16    this.lastChildWhileWaiting = false,
 17  });
 18
 19  /// Future that gets evaluated.
 20  final Future<T> future;
 21
 22  /// The build strategy once the future loaded.
 23  final Widget Function(BuildContext context, T result) onData;
 24
 25  /// The text displayed when no future is connected.
 26  ///
 27  /// This case should generally be avoided and is protected by assertions in
 28  /// debug builds.
 29  ///
 30  /// Is a 'not started' text by default.
 31  final Widget? onNotStarted;
 32
 33  /// The future loading indicator.
 34  ///
 35  /// Shown while the element is loading. Defaults to 'loading... text'.
 36  final Widget? onWaiting;
 37
 38  /// Internally save the future and avoid rebuilds.
 39  ///
 40  /// Caching will allow the future builder not to load again in some cases
 41  /// where a rebuild is triggered. But it comes at the cost that onData will
 42  /// not be called again, even if data changed.
 43  ///
 44  /// The parameter is false by default and should only be set to true when
 45  /// rebuilds are disruptive to the user and it is certain that the data will
 46  /// not change over the lifetime of the screen.
 47  final bool cacheFuture;
 48
 49  /// When loading the next result the child that got build the last time will
 50  /// be returned.
 51  ///
 52  /// During the first build, [onWaiting] os respected instead.
 53  final bool lastChildWhileWaiting;
 54
 55  @override
 56  State<ConsistentFutureBuilder<T>> createState() =>
 57      _ConsistentFutureBuilderState<T>();
 58}
 59
 60class _ConsistentFutureBuilderState<T>
 61    extends State<ConsistentFutureBuilder<T>> {
 62  Future<T>? _future; // avoid rebuilds
 63  /// Used for returning the last child during load when rebuilding.
 64  Widget? _lastChild;
 65
 66  @override
 67  void initState() {
 68    super.initState();
 69    if (widget.cacheFuture) {
 70      _future = widget.future;
 71    }
 72  }
 73
 74  @override
 75  Widget build(BuildContext context) => FutureBuilder<T>(
 76    future: _future ?? widget.future,
 77    builder: (BuildContext context, AsyncSnapshot<T> snapshot) {
 78      // Might get called before localizations initialize.
 79      final localizations = AppLocalizations.of(context);
 80      if (snapshot.hasError) {
 81        return Directionality(
 82          textDirection: TextDirection.ltr,
 83          child: Text(localizations?.error(snapshot.error.toString())
 84              ?? snapshot.error.toString(),),
 85        );
 86      }
 87      switch (snapshot.connectionState) {
 88        case ConnectionState.none:
 89          assert(false);
 90          return widget.onNotStarted ?? Text(localizations?.errNotStarted
 91              ?? 'NO_LOC_NO_START: please report this error.',);
 92        case ConnectionState.waiting:
 93        case ConnectionState.active:
 94          if (widget.lastChildWhileWaiting && _lastChild != null) {
 95            return _lastChild!;
 96          }
 97          return widget.onWaiting ?? Text(localizations?.loading
 98              ?? 'loading...',);
 99        case ConnectionState.done:
100          _lastChild = widget.onData(context, snapshot.data!);
101          return _lastChild!;
102      }
103    },
104  );
105}