diff --git a/packages/flutter/lib/src/widgets/automatic_keep_alive.dart b/packages/flutter/lib/src/widgets/automatic_keep_alive.dart index 4965e1b5d93..0c93135a4ba 100644 --- a/packages/flutter/lib/src/widgets/automatic_keep_alive.dart +++ b/packages/flutter/lib/src/widgets/automatic_keep_alive.dart @@ -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 on State { KeepAliveHandle? _keepAliveHandle; diff --git a/packages/flutter/lib/src/widgets/scroll_delegate.dart b/packages/flutter/lib/src/widgets/scroll_delegate.dart index f3de6fd386f..b5902065c31 100644 --- a/packages/flutter/lib/src/widgets/scroll_delegate.dart +++ b/packages/flutter/lib/src/widgets/scroll_delegate.dart @@ -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 item’s 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 item’s 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; diff --git a/packages/flutter/lib/src/widgets/sliver.dart b/packages/flutter/lib/src/widgets/sliver.dart index b89cdaa03d4..ef06fda8cd1 100644 --- a/packages/flutter/lib/src/widgets/sliver.dart +++ b/packages/flutter/lib/src/widgets/sliver.dart @@ -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 { /// Marks a child as needing to remain alive. const KeepAlive({super.key, required this.keepAlive, required super.child});