Skip to content

Commit b6100b7

Browse files
Add more documentation around keep alive (flutter#168311)
Should - fix flutter#153860 - fix flutter#146612 - fix flutter#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 <[email protected]>
1 parent a844dd2 commit b6100b7

File tree

3 files changed

+123
-23
lines changed

3 files changed

+123
-23
lines changed

packages/flutter/lib/src/widgets/automatic_keep_alive.dart

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,23 @@ import 'sliver.dart';
2727
///
2828
/// To send these notifications, consider using [AutomaticKeepAliveClientMixin].
2929
///
30+
/// The [SliverChildBuilderDelegate] and [SliverChildListDelegate] delegates,
31+
/// used with [SliverList] and [SliverGrid], as well as the scroll view
32+
/// counterparts [ListView] and [GridView], have an `addAutomaticKeepAlives`
33+
/// feature, which is enabled by default. This feature inserts
34+
/// [AutomaticKeepAlive] widgets around each child, which in turn configure
35+
/// [KeepAlive] widgets in response to [KeepAliveNotification]s.
36+
///
37+
/// The same `addAutomaticKeepAlives` feature is supported by
38+
/// [TwoDimensionalChildBuilderDelegate] and [TwoDimensionalChildListDelegate].
39+
///
3040
/// {@tool dartpad}
3141
/// This sample demonstrates how to use the [AutomaticKeepAlive] widget in
3242
/// combination with the [AutomaticKeepAliveClientMixin] to selectively preserve
3343
/// the state of individual items in a scrollable list.
3444
///
3545
/// Normally, widgets in a lazily built list like [ListView.builder] are
36-
/// disposed of when they leave the visible area to save resources. This means
46+
/// disposed of when they leave the visible area to maintain performance. This means
3747
/// that any state inside a [StatefulWidget] would be lost unless explicitly
3848
/// preserved.
3949
///
@@ -50,6 +60,13 @@ import 'sliver.dart';
5060
/// ** See code in examples/api/lib/widgets/keep_alive/automatic_keep_alive.0.dart **
5161
/// {@end-tool}
5262
///
63+
/// See also:
64+
///
65+
/// * [AutomaticKeepAliveClientMixin], which is a mixin with convenience
66+
/// methods for clients of [AutomaticKeepAlive]. Used with [State]
67+
/// subclasses.
68+
/// * [KeepAlive] which marks a child as needing to stay alive even when it's
69+
/// in a lazy list that would otherwise remove it.
5370
class AutomaticKeepAlive extends StatefulWidget {
5471
/// Creates a widget that listens to [KeepAliveNotification]s and maintains a
5572
/// [KeepAlive] widget appropriately.
@@ -354,8 +371,19 @@ class KeepAliveHandle extends ChangeNotifier {
354371
}
355372
}
356373

357-
/// A mixin with convenience methods for clients of [AutomaticKeepAlive]. Used
358-
/// with [State] subclasses.
374+
/// A mixin with convenience methods for clients of [AutomaticKeepAlive]. It is used
375+
/// with [State] subclasses to manage keep-alive behavior in lazily built lists.
376+
///
377+
/// This mixin simplifies interaction with [AutomaticKeepAlive] by automatically
378+
/// sending [KeepAliveNotification]s when necessary. Subclasses must implement
379+
/// [wantKeepAlive] to indicate whether the widget should be kept alive and call
380+
/// [updateKeepAlive] whenever its value changes.
381+
///
382+
/// The mixin internally manages a [KeepAliveHandle], which is used to notify
383+
/// the nearest [AutomaticKeepAlive] ancestor of changes in keep-alive
384+
/// requirements. [AutomaticKeepAlive] listens for [KeepAliveNotification]s sent
385+
/// by this mixin and dynamically wraps the subtree in a [KeepAlive] widget to
386+
/// preserve its state when it is no longer visible in the viewport.
359387
///
360388
/// Subclasses must implement [wantKeepAlive], and their [build] methods must
361389
/// call `super.build` (though the return value should be ignored).
@@ -366,9 +394,20 @@ class KeepAliveHandle extends ChangeNotifier {
366394
/// The type argument `T` is the type of the [StatefulWidget] subclass of the
367395
/// [State] into which this class is being mixed.
368396
///
397+
/// The [SliverChildBuilderDelegate] and [SliverChildListDelegate] delegates,
398+
/// used with [SliverList] and [SliverGrid], as well as the scroll view
399+
/// counterparts [ListView] and [GridView], have an `addAutomaticKeepAlives`
400+
/// feature, which is enabled by default. This feature inserts
401+
/// [AutomaticKeepAlive] widgets around each child, which in turn configure
402+
/// [KeepAlive] widgets in response to [KeepAliveNotification]s.
403+
///
404+
/// The same `addAutomaticKeepAlives` feature is supported by
405+
/// [TwoDimensionalChildBuilderDelegate] and [TwoDimensionalChildListDelegate].
406+
///
369407
/// {@tool dartpad}
370-
/// This example demonstrates how to use the [AutomaticKeepAliveClientMixin]
371-
/// to keep the state of a widget alive even when it is scrolled out of view.
408+
/// This example demonstrates how to use the
409+
/// [AutomaticKeepAliveClientMixin] to keep the state of a widget alive even
410+
/// when it is scrolled out of view.
372411
///
373412
/// ** See code in examples/api/lib/widgets/keep_alive/automatic_keep_alive_client_mixin.0.dart **
374413
/// {@end-tool}
@@ -377,6 +416,8 @@ class KeepAliveHandle extends ChangeNotifier {
377416
///
378417
/// * [AutomaticKeepAlive], which listens to messages from this mixin.
379418
/// * [KeepAliveNotification], the notifications sent by this mixin.
419+
/// * [KeepAlive] which marks a child as needing to stay alive even when it's
420+
/// in a lazy list that would otherwise remove it.
380421
@optionalTypeArgs
381422
mixin AutomaticKeepAliveClientMixin<T extends StatefulWidget> on State<T> {
382423
KeepAliveHandle? _keepAliveHandle;

packages/flutter/lib/src/widgets/scroll_delegate.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,60 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
413413
/// none of the children will ever try to keep themselves alive.
414414
///
415415
/// Defaults to true.
416+
///
417+
/// {@tool dartpad}
418+
/// This sample demonstrates how to use the [AutomaticKeepAlive] widget in
419+
/// combination with the [AutomaticKeepAliveClientMixin] to selectively preserve
420+
/// the state of individual items in a scrollable list.
421+
///
422+
/// Normally, widgets in a lazily built list like [ListView.builder] are
423+
/// disposed of when they leave the visible area to maintain performance. This means
424+
/// that any state inside a [StatefulWidget] would be lost unless explicitly
425+
/// preserved.
426+
///
427+
/// In this example, each list item is a [StatefulWidget] that includes a
428+
/// counter and an increment button. To preserve the state of selected items
429+
/// (based on their index), the [AutomaticKeepAlive] widget and
430+
/// [AutomaticKeepAliveClientMixin] are used:
431+
///
432+
/// - The `wantKeepAlive` getter in the item’s state class returns true for
433+
/// even-indexed items, indicating that their state should be preserved.
434+
/// - For odd-indexed items, `wantKeepAlive` returns false, so their state is
435+
/// not preserved when scrolled out of view.
436+
///
437+
/// ** See code in examples/api/lib/widgets/keep_alive/automatic_keep_alive.0.dart **
438+
/// {@end-tool}
439+
///
440+
/// {@tool dartpad}
441+
/// This sample demonstrates how to use the [KeepAlive] widget
442+
/// to preserve the state of individual list items in a [ListView] when they are
443+
/// scrolled out of view.
444+
///
445+
/// By default, [ListView.builder] only keeps the widgets currently visible in
446+
/// the viewport alive. When an item scrolls out of view, it may be disposed to
447+
/// free up resources. This can cause the state of [StatefulWidget]s to be lost
448+
/// if not explicitly preserved.
449+
///
450+
/// In this example, each item in the list is a [StatefulWidget] that maintains
451+
/// a counter. Tapping the "+" button increments the counter. To selectively
452+
/// preserve the state, each item is wrapped in a [KeepAlive] widget, with the
453+
/// keepAlive parameter set based on the item’s index:
454+
///
455+
/// - For even-indexed items, `keepAlive: true`, so their state is preserved
456+
/// even when scrolled off-screen.
457+
/// - For odd-indexed items, `keepAlive: false`, so their state is discarded
458+
/// when they are no longer visible.
459+
///
460+
/// ** See code in examples/api/lib/widgets/keep_alive/keep_alive.0.dart **
461+
/// {@end-tool}
462+
///
463+
/// * [AutomaticKeepAlive], which allows subtrees to request to be kept alive
464+
/// in lazy lists.
465+
/// * [AutomaticKeepAliveClientMixin], which is a mixin with convenience
466+
/// methods for clients of [AutomaticKeepAlive]. Used with [State]
467+
/// subclasses.
468+
/// * [KeepAlive] which marks a child as needing to stay alive even when it's
469+
/// in a lazy list that would otherwise remove it.
416470
/// {@endtemplate}
417471
final bool addAutomaticKeepAlives;
418472

packages/flutter/lib/src/widgets/sliver.dart

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,26 +1444,24 @@ class _SliverOffstageElement extends SingleChildRenderObjectElement {
14441444
/// Mark a child as needing to stay alive even when it's in a lazy list that
14451445
/// would otherwise remove it.
14461446
///
1447-
/// This widget is for use in a [RenderAbstractViewport]s, such as
1448-
/// [Viewport] or [TwoDimensionalViewport].
1449-
///
1450-
/// This widget is rarely used directly. The [SliverChildBuilderDelegate] and
1451-
/// [SliverChildListDelegate] delegates, used with [SliverList] and
1452-
/// [SliverGrid], as well as the scroll view counterparts [ListView] and
1453-
/// [GridView], have an `addAutomaticKeepAlives` feature, which is enabled by
1454-
/// default, and which causes [AutomaticKeepAlive] widgets to be inserted around
1455-
/// each child, causing [KeepAlive] widgets to be automatically added and
1456-
/// configured in response to [KeepAliveNotification]s.
1457-
///
1458-
/// The same `addAutomaticKeepAlives` feature is supported by the
1447+
/// This widget is used in [RenderAbstractViewport]s, such as [Viewport] or
1448+
/// [TwoDimensionalViewport], to manage the lifecycle of widgets that need to
1449+
/// remain alive even when scrolled out of view.
1450+
///
1451+
/// The [SliverChildBuilderDelegate] and [SliverChildListDelegate] delegates,
1452+
/// used with [SliverList] and [SliverGrid], as well as the scroll view
1453+
/// counterparts [ListView] and [GridView], have an `addAutomaticKeepAlives`
1454+
/// feature, which is enabled by default. This feature inserts
1455+
/// [AutomaticKeepAlive] widgets around each child, which in turn configure
1456+
/// [KeepAlive] widgets in response to [KeepAliveNotification]s.
1457+
///
1458+
/// The same `addAutomaticKeepAlives` feature is supported by
14591459
/// [TwoDimensionalChildBuilderDelegate] and [TwoDimensionalChildListDelegate].
14601460
///
1461-
/// Therefore, to keep a widget alive, it is more common to use those
1462-
/// notifications than to directly deal with [KeepAlive] widgets.
1463-
///
1464-
/// In practice, the simplest way to deal with these notifications is to mix
1465-
/// [AutomaticKeepAliveClientMixin] into one's [State]. See the documentation
1466-
/// for that mixin class for details.
1461+
/// Keep-alive behavior can be managed by using [KeepAlive] directly or by
1462+
/// relying on notifications. For convenience, [AutomaticKeepAliveClientMixin]
1463+
/// may be mixed into a [State] subclass. Further details are available in the
1464+
/// documentation for [AutomaticKeepAliveClientMixin].
14671465
///
14681466
/// {@tool dartpad}
14691467
/// This sample demonstrates how to use the [KeepAlive] widget
@@ -1488,6 +1486,13 @@ class _SliverOffstageElement extends SingleChildRenderObjectElement {
14881486
/// ** See code in examples/api/lib/widgets/keep_alive/keep_alive.0.dart **
14891487
/// {@end-tool}
14901488
///
1489+
/// See also:
1490+
///
1491+
/// * [AutomaticKeepAlive], which allows subtrees to request to be kept alive
1492+
/// in lazy lists.
1493+
/// * [AutomaticKeepAliveClientMixin], which is a mixin with convenience
1494+
/// methods for clients of [AutomaticKeepAlive]. Used with [State]
1495+
/// subclasses.
14911496
class KeepAlive extends ParentDataWidget<KeepAliveParentDataMixin> {
14921497
/// Marks a child as needing to remain alive.
14931498
const KeepAlive({super.key, required this.keepAlive, required super.child});

0 commit comments

Comments
 (0)