Add more documentation around keep alive (#168311)

Should
- fix https://github.com/flutter/flutter/issues/153860
- fix https://github.com/flutter/flutter/issues/146612
- fix https://github.com/flutter/flutter/issues/20112


## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Victor Sanni <victorsanniay@gmail.com>
This commit is contained in:
Valentin Vignal 2025-06-03 07:47:01 +08:00 committed by GitHub
parent a844dd2d2a
commit b6100b7c09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 121 additions and 21 deletions

View File

@ -27,13 +27,23 @@ import 'sliver.dart';
///
/// To send these notifications, consider using [AutomaticKeepAliveClientMixin].
///
/// The [SliverChildBuilderDelegate] and [SliverChildListDelegate] delegates,
/// used with [SliverList] and [SliverGrid], as well as the scroll view
/// counterparts [ListView] and [GridView], have an `addAutomaticKeepAlives`
/// feature, which is enabled by default. This feature inserts
/// [AutomaticKeepAlive] widgets around each child, which in turn configure
/// [KeepAlive] widgets in response to [KeepAliveNotification]s.
///
/// The same `addAutomaticKeepAlives` feature is supported by
/// [TwoDimensionalChildBuilderDelegate] and [TwoDimensionalChildListDelegate].
///
/// {@tool dartpad}
/// This sample demonstrates how to use the [AutomaticKeepAlive] widget in
/// combination with the [AutomaticKeepAliveClientMixin] to selectively preserve
/// the state of individual items in a scrollable list.
///
/// Normally, widgets in a lazily built list like [ListView.builder] are
/// disposed of when they leave the visible area to save resources. This means
/// disposed of when they leave the visible area to maintain performance. This means
/// that any state inside a [StatefulWidget] would be lost unless explicitly
/// preserved.
///
@ -50,6 +60,13 @@ import 'sliver.dart';
/// ** See code in examples/api/lib/widgets/keep_alive/automatic_keep_alive.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [AutomaticKeepAliveClientMixin], which is a mixin with convenience
/// methods for clients of [AutomaticKeepAlive]. Used with [State]
/// subclasses.
/// * [KeepAlive] which marks a child as needing to stay alive even when it's
/// in a lazy list that would otherwise remove it.
class AutomaticKeepAlive extends StatefulWidget {
/// Creates a widget that listens to [KeepAliveNotification]s and maintains a
/// [KeepAlive] widget appropriately.
@ -354,8 +371,19 @@ class KeepAliveHandle extends ChangeNotifier {
}
}
/// A mixin with convenience methods for clients of [AutomaticKeepAlive]. Used
/// with [State] subclasses.
/// A mixin with convenience methods for clients of [AutomaticKeepAlive]. It is used
/// with [State] subclasses to manage keep-alive behavior in lazily built lists.
///
/// This mixin simplifies interaction with [AutomaticKeepAlive] by automatically
/// sending [KeepAliveNotification]s when necessary. Subclasses must implement
/// [wantKeepAlive] to indicate whether the widget should be kept alive and call
/// [updateKeepAlive] whenever its value changes.
///
/// The mixin internally manages a [KeepAliveHandle], which is used to notify
/// the nearest [AutomaticKeepAlive] ancestor of changes in keep-alive
/// requirements. [AutomaticKeepAlive] listens for [KeepAliveNotification]s sent
/// by this mixin and dynamically wraps the subtree in a [KeepAlive] widget to
/// preserve its state when it is no longer visible in the viewport.
///
/// Subclasses must implement [wantKeepAlive], and their [build] methods must
/// call `super.build` (though the return value should be ignored).
@ -366,9 +394,20 @@ class KeepAliveHandle extends ChangeNotifier {
/// The type argument `T` is the type of the [StatefulWidget] subclass of the
/// [State] into which this class is being mixed.
///
/// The [SliverChildBuilderDelegate] and [SliverChildListDelegate] delegates,
/// used with [SliverList] and [SliverGrid], as well as the scroll view
/// counterparts [ListView] and [GridView], have an `addAutomaticKeepAlives`
/// feature, which is enabled by default. This feature inserts
/// [AutomaticKeepAlive] widgets around each child, which in turn configure
/// [KeepAlive] widgets in response to [KeepAliveNotification]s.
///
/// The same `addAutomaticKeepAlives` feature is supported by
/// [TwoDimensionalChildBuilderDelegate] and [TwoDimensionalChildListDelegate].
///
/// {@tool dartpad}
/// This example demonstrates how to use the [AutomaticKeepAliveClientMixin]
/// to keep the state of a widget alive even when it is scrolled out of view.
/// This example demonstrates how to use the
/// [AutomaticKeepAliveClientMixin] to keep the state of a widget alive even
/// when it is scrolled out of view.
///
/// ** See code in examples/api/lib/widgets/keep_alive/automatic_keep_alive_client_mixin.0.dart **
/// {@end-tool}
@ -377,6 +416,8 @@ class KeepAliveHandle extends ChangeNotifier {
///
/// * [AutomaticKeepAlive], which listens to messages from this mixin.
/// * [KeepAliveNotification], the notifications sent by this mixin.
/// * [KeepAlive] which marks a child as needing to stay alive even when it's
/// in a lazy list that would otherwise remove it.
@optionalTypeArgs
mixin AutomaticKeepAliveClientMixin<T extends StatefulWidget> on State<T> {
KeepAliveHandle? _keepAliveHandle;

View File

@ -413,6 +413,60 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
/// none of the children will ever try to keep themselves alive.
///
/// Defaults to true.
///
/// {@tool dartpad}
/// This sample demonstrates how to use the [AutomaticKeepAlive] widget in
/// combination with the [AutomaticKeepAliveClientMixin] to selectively preserve
/// the state of individual items in a scrollable list.
///
/// Normally, widgets in a lazily built list like [ListView.builder] are
/// disposed of when they leave the visible area to maintain performance. This means
/// that any state inside a [StatefulWidget] would be lost unless explicitly
/// preserved.
///
/// In this example, each list item is a [StatefulWidget] that includes a
/// counter and an increment button. To preserve the state of selected items
/// (based on their index), the [AutomaticKeepAlive] widget and
/// [AutomaticKeepAliveClientMixin] are used:
///
/// - The `wantKeepAlive` getter in the items state class returns true for
/// even-indexed items, indicating that their state should be preserved.
/// - For odd-indexed items, `wantKeepAlive` returns false, so their state is
/// not preserved when scrolled out of view.
///
/// ** See code in examples/api/lib/widgets/keep_alive/automatic_keep_alive.0.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This sample demonstrates how to use the [KeepAlive] widget
/// to preserve the state of individual list items in a [ListView] when they are
/// scrolled out of view.
///
/// By default, [ListView.builder] only keeps the widgets currently visible in
/// the viewport alive. When an item scrolls out of view, it may be disposed to
/// free up resources. This can cause the state of [StatefulWidget]s to be lost
/// if not explicitly preserved.
///
/// In this example, each item in the list is a [StatefulWidget] that maintains
/// a counter. Tapping the "+" button increments the counter. To selectively
/// preserve the state, each item is wrapped in a [KeepAlive] widget, with the
/// keepAlive parameter set based on the items index:
///
/// - For even-indexed items, `keepAlive: true`, so their state is preserved
/// even when scrolled off-screen.
/// - For odd-indexed items, `keepAlive: false`, so their state is discarded
/// when they are no longer visible.
///
/// ** See code in examples/api/lib/widgets/keep_alive/keep_alive.0.dart **
/// {@end-tool}
///
/// * [AutomaticKeepAlive], which allows subtrees to request to be kept alive
/// in lazy lists.
/// * [AutomaticKeepAliveClientMixin], which is a mixin with convenience
/// methods for clients of [AutomaticKeepAlive]. Used with [State]
/// subclasses.
/// * [KeepAlive] which marks a child as needing to stay alive even when it's
/// in a lazy list that would otherwise remove it.
/// {@endtemplate}
final bool addAutomaticKeepAlives;

View File

@ -1444,26 +1444,24 @@ class _SliverOffstageElement extends SingleChildRenderObjectElement {
/// Mark a child as needing to stay alive even when it's in a lazy list that
/// would otherwise remove it.
///
/// This widget is for use in a [RenderAbstractViewport]s, such as
/// [Viewport] or [TwoDimensionalViewport].
/// This widget is used in [RenderAbstractViewport]s, such as [Viewport] or
/// [TwoDimensionalViewport], to manage the lifecycle of widgets that need to
/// remain alive even when scrolled out of view.
///
/// This widget is rarely used directly. The [SliverChildBuilderDelegate] and
/// [SliverChildListDelegate] delegates, used with [SliverList] and
/// [SliverGrid], as well as the scroll view counterparts [ListView] and
/// [GridView], have an `addAutomaticKeepAlives` feature, which is enabled by
/// default, and which causes [AutomaticKeepAlive] widgets to be inserted around
/// each child, causing [KeepAlive] widgets to be automatically added and
/// configured in response to [KeepAliveNotification]s.
/// The [SliverChildBuilderDelegate] and [SliverChildListDelegate] delegates,
/// used with [SliverList] and [SliverGrid], as well as the scroll view
/// counterparts [ListView] and [GridView], have an `addAutomaticKeepAlives`
/// feature, which is enabled by default. This feature inserts
/// [AutomaticKeepAlive] widgets around each child, which in turn configure
/// [KeepAlive] widgets in response to [KeepAliveNotification]s.
///
/// The same `addAutomaticKeepAlives` feature is supported by the
/// The same `addAutomaticKeepAlives` feature is supported by
/// [TwoDimensionalChildBuilderDelegate] and [TwoDimensionalChildListDelegate].
///
/// Therefore, to keep a widget alive, it is more common to use those
/// notifications than to directly deal with [KeepAlive] widgets.
///
/// In practice, the simplest way to deal with these notifications is to mix
/// [AutomaticKeepAliveClientMixin] into one's [State]. See the documentation
/// for that mixin class for details.
/// Keep-alive behavior can be managed by using [KeepAlive] directly or by
/// relying on notifications. For convenience, [AutomaticKeepAliveClientMixin]
/// may be mixed into a [State] subclass. Further details are available in the
/// documentation for [AutomaticKeepAliveClientMixin].
///
/// {@tool dartpad}
/// This sample demonstrates how to use the [KeepAlive] widget
@ -1488,6 +1486,13 @@ class _SliverOffstageElement extends SingleChildRenderObjectElement {
/// ** See code in examples/api/lib/widgets/keep_alive/keep_alive.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [AutomaticKeepAlive], which allows subtrees to request to be kept alive
/// in lazy lists.
/// * [AutomaticKeepAliveClientMixin], which is a mixin with convenience
/// methods for clients of [AutomaticKeepAlive]. Used with [State]
/// subclasses.
class KeepAlive extends ParentDataWidget<KeepAliveParentDataMixin> {
/// Marks a child as needing to remain alive.
const KeepAlive({super.key, required this.keepAlive, required super.child});