Logo

Infinite Scrolling

Infinite scrolling is actually really easy to do in ReArch! ReArch gives you the flexibility to do it in almost any way you choose, but here are the two standard ways:

  1. Use a factory and let Flutter handle the disposal of out-of-view widgets (preferred). You can easily customize how long widgets are kept alive with use.automaticKeepAlive!
  2. Keep your own cache and grow it as the user scrolls (not as recommended). Whenever new widgets come into view, they add elements to a capsule of type List<Future<T>>.

Both approaches described above utilize a more advanced technique described in Reducing Builds.

Example#

Here is a quick example that showcases the first option above.

/// A factory capsule that returns the input data after a brief delay.
/// This also doubles as an example of a generic capsule.
Future<T> Function(T) delayedEchoFactory<T>(CapsuleHandle _) {
  return (data) => Future.delayed(const Duration(seconds: 1), () => data);
}

@rearchWidget
Widget infiniteList() {
  return ListView.builder(
    itemBuilder: (context, index) => InfiniteScrollItem(index: index),
  );
}

@rearchWidget
Widget infiniteScrollItem(WidgetHandle use, {required int index}) {
  final (keepAlive, setKeepAlive) = use.state(false);
  use.automaticKeepAlive(keepAlive: keepAlive);

  final factory = use(delayedEchoFactory<int>);
  final echoFuture = use.memo(() => factory(index), [factory, index]);
  final echoState = use.future(echoFuture);

  return ListTile(
    selected: keepAlive,
    onTap: () => setKeepAlive(!keepAlive),
    leading: Icon(
      keepAlive
          ? Icons.task_alt_rounded
          : Icons.radio_button_unchecked_rounded,
    ),
    title: switch (echoState) {
      AsyncData(:final data) => Text('$data'),
      AsyncLoading() => const Align(
          alignment: Alignment.centerLeft,
          child: CircularProgressIndicator.adaptive(),
        ),
      AsyncError(:final error) => Text('$error'),
    },
  );
}

And here's a complete, runnable version of the above.