1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'package:flutter/material.dart';
6///
7/// @docImport 'animated_scroll_view.dart';
8/// @docImport 'container.dart';
9/// @docImport 'implicit_animations.dart';
10/// @docImport 'scroll_view.dart';
11/// @docImport 'sliver_fill.dart';
12/// @docImport 'sliver_persistent_header.dart';
13/// @docImport 'sliver_prototype_extent_list.dart';
14/// @docImport 'text.dart';
15/// @docImport 'two_dimensional_viewport.dart';
16/// @docImport 'viewport.dart';
17/// @docImport 'visibility.dart';
18library;
19
20import 'dart:collection' show HashMap, SplayTreeMap;
21import 'dart:math' as math;
22
23import 'package:flutter/foundation.dart';
24import 'package:flutter/rendering.dart';
25
26import 'automatic_keep_alive.dart';
27import 'basic.dart';
28import 'framework.dart';
29import 'scroll_delegate.dart';
30
31/// A base class for slivers that have [KeepAlive] children.
32///
33/// See also:
34///
35/// * [KeepAlive], which marks whether its child widget should be kept alive.
36/// * [SliverChildBuilderDelegate] and [SliverChildListDelegate], slivers
37/// which make use of the keep alive functionality through the
38/// `addAutomaticKeepAlives` property.
39/// * [SliverGrid] and [SliverList], two sliver widgets that are commonly
40/// wrapped with [KeepAlive] widgets to preserve their sliver child subtrees.
41abstract class SliverWithKeepAliveWidget extends RenderObjectWidget {
42 /// Initializes fields for subclasses.
43 const SliverWithKeepAliveWidget({super.key});
44
45 @override
46 RenderSliverWithKeepAliveMixin createRenderObject(BuildContext context);
47}
48
49/// A base class for slivers that have multiple box children.
50///
51/// Helps subclasses build their children lazily using a [SliverChildDelegate].
52///
53/// The widgets returned by the [delegate] are cached and the delegate is only
54/// consulted again if it changes and the new delegate's
55/// [SliverChildDelegate.shouldRebuild] method returns true.
56abstract class SliverMultiBoxAdaptorWidget extends SliverWithKeepAliveWidget {
57 /// Initializes fields for subclasses.
58 const SliverMultiBoxAdaptorWidget({super.key, required this.delegate});
59
60 /// {@template flutter.widgets.SliverMultiBoxAdaptorWidget.delegate}
61 /// The delegate that provides the children for this widget.
62 ///
63 /// The children are constructed lazily using this delegate to avoid creating
64 /// more children than are visible through the [Viewport].
65 ///
66 /// ## Using more than one delegate in a [Viewport]
67 ///
68 /// If multiple delegates are used in a single scroll view, the first child of
69 /// each delegate will always be laid out, even if it extends beyond the
70 /// currently viewable area. This is because at least one child is required in
71 /// order to estimate the max scroll offset for the whole scroll view, as it
72 /// uses the currently built children to estimate the remaining children's
73 /// extent.
74 ///
75 /// See also:
76 ///
77 /// * [SliverChildBuilderDelegate] and [SliverChildListDelegate], which are
78 /// commonly used subclasses of [SliverChildDelegate] that use a builder
79 /// callback and an explicit child list, respectively.
80 /// {@endtemplate}
81 final SliverChildDelegate delegate;
82
83 @override
84 SliverMultiBoxAdaptorElement createElement() => SliverMultiBoxAdaptorElement(this);
85
86 @override
87 RenderSliverMultiBoxAdaptor createRenderObject(BuildContext context);
88
89 /// Returns an estimate of the max scroll extent for all the children.
90 ///
91 /// Subclasses should override this function if they have additional
92 /// information about their max scroll extent.
93 ///
94 /// This is used by [SliverMultiBoxAdaptorElement] to implement part of the
95 /// [RenderSliverBoxChildManager] API.
96 ///
97 /// The default implementation defers to [delegate] via its
98 /// [SliverChildDelegate.estimateMaxScrollOffset] method.
99 double? estimateMaxScrollOffset(
100 SliverConstraints? constraints,
101 int firstIndex,
102 int lastIndex,
103 double leadingScrollOffset,
104 double trailingScrollOffset,
105 ) {
106 assert(lastIndex >= firstIndex);
107 return delegate.estimateMaxScrollOffset(
108 firstIndex,
109 lastIndex,
110 leadingScrollOffset,
111 trailingScrollOffset,
112 );
113 }
114
115 @override
116 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
117 super.debugFillProperties(properties);
118 properties.add(DiagnosticsProperty<SliverChildDelegate>('delegate', delegate));
119 }
120}
121
122/// A sliver that places multiple box children in a linear array along the main
123/// axis.
124///
125/// _To learn more about slivers, see [CustomScrollView.slivers]._
126///
127/// Each child is forced to have the [SliverConstraints.crossAxisExtent] in the
128/// cross axis but determines its own main axis extent.
129///
130/// [SliverList] determines its scroll offset by "dead reckoning" because
131/// children outside the visible part of the sliver are not materialized, which
132/// means [SliverList] cannot learn their main axis extent. Instead, newly
133/// materialized children are placed adjacent to existing children.
134///
135/// {@youtube 560 315 https://www.youtube.com/watch?v=ORiTTaVY6mM}
136///
137/// If the children have a fixed extent in the main axis, consider using
138/// [SliverFixedExtentList] rather than [SliverList] because
139/// [SliverFixedExtentList] does not need to perform layout on its children to
140/// obtain their extent in the main axis and is therefore more efficient.
141///
142/// {@macro flutter.widgets.SliverChildDelegate.lifecycle}
143///
144/// See also:
145///
146/// * <https://docs.flutter.dev/ui/layout/scrolling/slivers>, a description
147/// of what slivers are and how to use them.
148/// * [SliverFixedExtentList], which is more efficient for children with
149/// the same extent in the main axis.
150/// * [SliverPrototypeExtentList], which is similar to [SliverFixedExtentList]
151/// except that it uses a prototype list item instead of a pixel value to define
152/// the main axis extent of each item.
153/// * [SliverAnimatedList], which animates items added to or removed from a
154/// list.
155/// * [SliverGrid], which places multiple children in a two dimensional grid.
156/// * [SliverAnimatedGrid], a sliver which animates items when they are
157/// inserted into or removed from a grid.
158class SliverList extends SliverMultiBoxAdaptorWidget {
159 /// Creates a sliver that places box children in a linear array.
160 const SliverList({super.key, required super.delegate});
161
162 /// A sliver that places multiple box children in a linear array along the main
163 /// axis.
164 ///
165 /// This constructor is appropriate for sliver lists with a large (or
166 /// infinite) number of children because the builder is called only for those
167 /// children that are actually visible.
168 ///
169 /// Providing a non-null `itemCount` improves the ability of the [SliverList]
170 /// to estimate the maximum scroll extent.
171 ///
172 /// `itemBuilder` will be called only with indices greater than or equal to
173 /// zero and less than `itemCount`.
174 ///
175 /// {@macro flutter.widgets.ListView.builder.itemBuilder}
176 ///
177 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
178 ///
179 /// The `addAutomaticKeepAlives` argument corresponds to the
180 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
181 /// `addRepaintBoundaries` argument corresponds to the
182 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
183 /// `addSemanticIndexes` argument corresponds to the
184 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
185 ///
186 /// {@tool snippet}
187 /// This example, which would be provided in [CustomScrollView.slivers],
188 /// shows an infinite number of items in varying shades of blue:
189 ///
190 /// ```dart
191 /// SliverList.builder(
192 /// itemBuilder: (BuildContext context, int index) {
193 /// return Container(
194 /// alignment: Alignment.center,
195 /// color: Colors.lightBlue[100 * (index % 9)],
196 /// child: Text('list item $index'),
197 /// );
198 /// },
199 /// )
200 /// ```
201 /// {@end-tool}
202 SliverList.builder({
203 super.key,
204 required NullableIndexedWidgetBuilder itemBuilder,
205 ChildIndexGetter? findChildIndexCallback,
206 int? itemCount,
207 bool addAutomaticKeepAlives = true,
208 bool addRepaintBoundaries = true,
209 bool addSemanticIndexes = true,
210 }) : super(
211 delegate: SliverChildBuilderDelegate(
212 itemBuilder,
213 findChildIndexCallback: findChildIndexCallback,
214 childCount: itemCount,
215 addAutomaticKeepAlives: addAutomaticKeepAlives,
216 addRepaintBoundaries: addRepaintBoundaries,
217 addSemanticIndexes: addSemanticIndexes,
218 ),
219 );
220
221 /// A sliver that places multiple box children, separated by box widgets, in a
222 /// linear array along the main axis.
223 ///
224 /// This constructor is appropriate for sliver lists with a large (or
225 /// infinite) number of children because the builder is called only for those
226 /// children that are actually visible.
227 ///
228 /// Providing a non-null `itemCount` improves the ability of the [SliverList]
229 /// to estimate the maximum scroll extent.
230 ///
231 /// `itemBuilder` will be called only with indices greater than or equal to
232 /// zero and less than `itemCount`.
233 ///
234 /// {@macro flutter.widgets.ListView.builder.itemBuilder}
235 ///
236 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
237 ///
238 ///
239 /// The `separatorBuilder` is similar to `itemBuilder`, except it is the widget
240 /// that gets placed between itemBuilder(context, index) and itemBuilder(context, index + 1).
241 ///
242 /// The `addAutomaticKeepAlives` argument corresponds to the
243 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
244 /// `addRepaintBoundaries` argument corresponds to the
245 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
246 /// `addSemanticIndexes` argument corresponds to the
247 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
248 ///
249 /// {@tool snippet}
250 /// This example shows how to create a [SliverList] whose [Container] items
251 /// are separated by [Divider]s. The [SliverList] would be provided in
252 /// [CustomScrollView.slivers].
253 ///
254 /// ```dart
255 /// SliverList.separated(
256 /// itemBuilder: (BuildContext context, int index) {
257 /// return Container(
258 /// alignment: Alignment.center,
259 /// color: Colors.lightBlue[100 * (index % 9)],
260 /// child: Text('list item $index'),
261 /// );
262 /// },
263 /// separatorBuilder: (BuildContext context, int index) => const Divider(),
264 /// )
265 /// ```
266 /// {@end-tool}
267 SliverList.separated({
268 super.key,
269 required NullableIndexedWidgetBuilder itemBuilder,
270 ChildIndexGetter? findChildIndexCallback,
271 required NullableIndexedWidgetBuilder separatorBuilder,
272 int? itemCount,
273 bool addAutomaticKeepAlives = true,
274 bool addRepaintBoundaries = true,
275 bool addSemanticIndexes = true,
276 }) : super(
277 delegate: SliverChildBuilderDelegate(
278 (BuildContext context, int index) {
279 final int itemIndex = index ~/ 2;
280 final Widget? widget;
281 if (index.isEven) {
282 widget = itemBuilder(context, itemIndex);
283 } else {
284 widget = separatorBuilder(context, itemIndex);
285 assert(() {
286 if (widget == null) {
287 throw FlutterError('separatorBuilder cannot return null.');
288 }
289 return true;
290 }());
291 }
292 return widget;
293 },
294 findChildIndexCallback: findChildIndexCallback,
295 childCount: itemCount == null ? null : math.max(0, itemCount * 2 - 1),
296 addAutomaticKeepAlives: addAutomaticKeepAlives,
297 addRepaintBoundaries: addRepaintBoundaries,
298 addSemanticIndexes: addSemanticIndexes,
299 semanticIndexCallback: (Widget _, int index) {
300 return index.isEven ? index ~/ 2 : null;
301 },
302 ),
303 );
304
305 /// A sliver that places multiple box children in a linear array along the main
306 /// axis.
307 ///
308 /// This constructor uses a list of [Widget]s to build the sliver.
309 ///
310 /// The `addAutomaticKeepAlives` argument corresponds to the
311 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
312 /// `addRepaintBoundaries` argument corresponds to the
313 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
314 /// `addSemanticIndexes` argument corresponds to the
315 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
316 ///
317 /// {@tool snippet}
318 /// This example, which would be provided in [CustomScrollView.slivers],
319 /// shows a list containing two [Text] widgets:
320 ///
321 /// ```dart
322 /// SliverList.list(
323 /// children: const <Widget>[
324 /// Text('Hello'),
325 /// Text('World!'),
326 /// ],
327 /// );
328 /// ```
329 /// {@end-tool}
330 SliverList.list({
331 super.key,
332 required List<Widget> children,
333 bool addAutomaticKeepAlives = true,
334 bool addRepaintBoundaries = true,
335 bool addSemanticIndexes = true,
336 }) : super(
337 delegate: SliverChildListDelegate(
338 children,
339 addAutomaticKeepAlives: addAutomaticKeepAlives,
340 addRepaintBoundaries: addRepaintBoundaries,
341 addSemanticIndexes: addSemanticIndexes,
342 ),
343 );
344
345 @override
346 SliverMultiBoxAdaptorElement createElement() =>
347 SliverMultiBoxAdaptorElement(this, replaceMovedChildren: true);
348
349 @override
350 RenderSliverList createRenderObject(BuildContext context) {
351 final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
352 return RenderSliverList(childManager: element);
353 }
354}
355
356/// A sliver that places multiple box children with the same main axis extent in
357/// a linear array.
358///
359/// _To learn more about slivers, see [CustomScrollView.slivers]._
360///
361/// [SliverFixedExtentList] places its children in a linear array along the main
362/// axis starting at offset zero and without gaps. Each child is forced to have
363/// the [itemExtent] in the main axis and the
364/// [SliverConstraints.crossAxisExtent] in the cross axis.
365///
366/// [SliverFixedExtentList] is more efficient than [SliverList] because
367/// [SliverFixedExtentList] does not need to perform layout on its children to
368/// obtain their extent in the main axis.
369///
370/// {@tool snippet}
371///
372/// This example, which would be inserted into a [CustomScrollView.slivers]
373/// list, shows an infinite number of items in varying shades of blue:
374///
375/// ```dart
376/// SliverFixedExtentList(
377/// itemExtent: 50.0,
378/// delegate: SliverChildBuilderDelegate(
379/// (BuildContext context, int index) {
380/// return Container(
381/// alignment: Alignment.center,
382/// color: Colors.lightBlue[100 * (index % 9)],
383/// child: Text('list item $index'),
384/// );
385/// },
386/// ),
387/// )
388/// ```
389/// {@end-tool}
390///
391/// {@macro flutter.widgets.SliverChildDelegate.lifecycle}
392///
393/// See also:
394///
395/// * [SliverPrototypeExtentList], which is similar to [SliverFixedExtentList]
396/// except that it uses a prototype list item instead of a pixel value to define
397/// the main axis extent of each item.
398/// * [SliverVariedExtentList], which supports children with varying (but known
399/// upfront) extents.
400/// * [SliverFillViewport], which determines the [itemExtent] based on
401/// [SliverConstraints.viewportMainAxisExtent].
402/// * [SliverList], which does not require its children to have the same
403/// extent in the main axis.
404class SliverFixedExtentList extends SliverMultiBoxAdaptorWidget {
405 /// Creates a sliver that places box children with the same main axis extent
406 /// in a linear array.
407 const SliverFixedExtentList({super.key, required super.delegate, required this.itemExtent});
408
409 /// A sliver that places multiple box children in a linear array along the main
410 /// axis.
411 ///
412 /// [SliverFixedExtentList] places its children in a linear array along the main
413 /// axis starting at offset zero and without gaps. Each child is forced to have
414 /// the [itemExtent] in the main axis and the
415 /// [SliverConstraints.crossAxisExtent] in the cross axis.
416 ///
417 /// This constructor is appropriate for sliver lists with a large (or
418 /// infinite) number of children whose extent is already determined.
419 ///
420 /// Providing a non-null `itemCount` improves the ability of the [SliverFixedExtentList]
421 /// to estimate the maximum scroll extent.
422 ///
423 /// `itemBuilder` will be called only with indices greater than or equal to
424 /// zero and less than `itemCount`.
425 ///
426 /// {@macro flutter.widgets.ListView.builder.itemBuilder}
427 ///
428 /// The `itemExtent` argument is the extent of each item.
429 ///
430 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
431 ///
432 /// The `addAutomaticKeepAlives` argument corresponds to the
433 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
434 /// `addRepaintBoundaries` argument corresponds to the
435 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
436 /// `addSemanticIndexes` argument corresponds to the
437 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
438 /// {@tool snippet}
439 ///
440 /// This example, which would be inserted into a [CustomScrollView.slivers]
441 /// list, shows an infinite number of items in varying shades of blue:
442 ///
443 /// ```dart
444 /// SliverFixedExtentList.builder(
445 /// itemExtent: 50.0,
446 /// itemBuilder: (BuildContext context, int index) {
447 /// return Container(
448 /// alignment: Alignment.center,
449 /// color: Colors.lightBlue[100 * (index % 9)],
450 /// child: Text('list item $index'),
451 /// );
452 /// },
453 /// )
454 /// ```
455 /// {@end-tool}
456 SliverFixedExtentList.builder({
457 super.key,
458 required NullableIndexedWidgetBuilder itemBuilder,
459 required this.itemExtent,
460 ChildIndexGetter? findChildIndexCallback,
461 int? itemCount,
462 bool addAutomaticKeepAlives = true,
463 bool addRepaintBoundaries = true,
464 bool addSemanticIndexes = true,
465 }) : super(
466 delegate: SliverChildBuilderDelegate(
467 itemBuilder,
468 findChildIndexCallback: findChildIndexCallback,
469 childCount: itemCount,
470 addAutomaticKeepAlives: addAutomaticKeepAlives,
471 addRepaintBoundaries: addRepaintBoundaries,
472 addSemanticIndexes: addSemanticIndexes,
473 ),
474 );
475
476 /// A sliver that places multiple box children in a linear array along the main
477 /// axis.
478 ///
479 /// [SliverFixedExtentList] places its children in a linear array along the main
480 /// axis starting at offset zero and without gaps. Each child is forced to have
481 /// the [itemExtent] in the main axis and the
482 /// [SliverConstraints.crossAxisExtent] in the cross axis.
483 ///
484 /// This constructor uses a list of [Widget]s to build the sliver.
485 ///
486 /// The `addAutomaticKeepAlives` argument corresponds to the
487 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
488 /// `addRepaintBoundaries` argument corresponds to the
489 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
490 /// `addSemanticIndexes` argument corresponds to the
491 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
492 ///
493 /// {@tool snippet}
494 /// This example, which would be inserted into a [CustomScrollView.slivers]
495 /// list, shows an infinite number of items in varying shades of blue:
496 ///
497 /// ```dart
498 /// SliverFixedExtentList.list(
499 /// itemExtent: 50.0,
500 /// children: const <Widget>[
501 /// Text('Hello'),
502 /// Text('World!'),
503 /// ],
504 /// );
505 /// ```
506 /// {@end-tool}
507 SliverFixedExtentList.list({
508 super.key,
509 required List<Widget> children,
510 required this.itemExtent,
511 bool addAutomaticKeepAlives = true,
512 bool addRepaintBoundaries = true,
513 bool addSemanticIndexes = true,
514 }) : super(
515 delegate: SliverChildListDelegate(
516 children,
517 addAutomaticKeepAlives: addAutomaticKeepAlives,
518 addRepaintBoundaries: addRepaintBoundaries,
519 addSemanticIndexes: addSemanticIndexes,
520 ),
521 );
522
523 /// The extent the children are forced to have in the main axis.
524 final double itemExtent;
525
526 @override
527 RenderSliverFixedExtentList createRenderObject(BuildContext context) {
528 final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
529 return RenderSliverFixedExtentList(childManager: element, itemExtent: itemExtent);
530 }
531
532 @override
533 void updateRenderObject(BuildContext context, RenderSliverFixedExtentList renderObject) {
534 renderObject.itemExtent = itemExtent;
535 }
536}
537
538/// A sliver that places its box children in a linear array and constrains them
539/// to have the corresponding extent returned by [itemExtentBuilder].
540///
541/// _To learn more about slivers, see [CustomScrollView.slivers]._
542///
543/// [SliverVariedExtentList] arranges its children in a line along
544/// the main axis starting at offset zero and without gaps. Each child is
545/// constrained to the corresponding extent along the main axis
546/// and the [SliverConstraints.crossAxisExtent] along the cross axis.
547///
548/// [SliverVariedExtentList] is more efficient than [SliverList] because
549/// [SliverVariedExtentList] does not need to lay out its children to obtain
550/// their extent along the main axis. It's a little more flexible than
551/// [SliverFixedExtentList] because this allow the children to have different extents.
552///
553/// See also:
554///
555/// * [SliverFixedExtentList], whose children are forced to a given pixel
556/// extent.
557/// * [SliverPrototypeExtentList], which is similar to [SliverFixedExtentList]
558/// except that it uses a prototype list item instead of a pixel value to define
559/// the main axis extent of each item.
560/// * [SliverList], which does not require its children to have the same
561/// extent in the main axis.
562/// * [SliverFillViewport], which sizes its children based on the
563/// size of the viewport, regardless of what else is in the scroll view.
564class SliverVariedExtentList extends SliverMultiBoxAdaptorWidget {
565 /// Creates a sliver that places box children with the same main axis extent
566 /// in a linear array.
567 const SliverVariedExtentList({
568 super.key,
569 required super.delegate,
570 required this.itemExtentBuilder,
571 });
572
573 /// A sliver that places multiple box children in a linear array along the main
574 /// axis.
575 ///
576 /// [SliverVariedExtentList] places its children in a linear array along the main
577 /// axis starting at offset zero and without gaps. Each child is forced to have
578 /// the returned extent of [itemExtentBuilder] in the main axis and the
579 /// [SliverConstraints.crossAxisExtent] in the cross axis.
580 ///
581 /// This constructor is appropriate for sliver lists with a large (or
582 /// infinite) number of children whose extent is already determined.
583 ///
584 /// Providing a non-null `itemCount` improves the ability of the [SliverVariedExtentList]
585 /// to estimate the maximum scroll extent.
586 SliverVariedExtentList.builder({
587 super.key,
588 required NullableIndexedWidgetBuilder itemBuilder,
589 required this.itemExtentBuilder,
590 ChildIndexGetter? findChildIndexCallback,
591 int? itemCount,
592 bool addAutomaticKeepAlives = true,
593 bool addRepaintBoundaries = true,
594 bool addSemanticIndexes = true,
595 }) : super(
596 delegate: SliverChildBuilderDelegate(
597 itemBuilder,
598 findChildIndexCallback: findChildIndexCallback,
599 childCount: itemCount,
600 addAutomaticKeepAlives: addAutomaticKeepAlives,
601 addRepaintBoundaries: addRepaintBoundaries,
602 addSemanticIndexes: addSemanticIndexes,
603 ),
604 );
605
606 /// A sliver that places multiple box children in a linear array along the main
607 /// axis.
608 ///
609 /// [SliverVariedExtentList] places its children in a linear array along the main
610 /// axis starting at offset zero and without gaps. Each child is forced to have
611 /// the returned extent of [itemExtentBuilder] in the main axis and the
612 /// [SliverConstraints.crossAxisExtent] in the cross axis.
613 ///
614 /// This constructor uses a list of [Widget]s to build the sliver.
615 SliverVariedExtentList.list({
616 super.key,
617 required List<Widget> children,
618 required this.itemExtentBuilder,
619 bool addAutomaticKeepAlives = true,
620 bool addRepaintBoundaries = true,
621 bool addSemanticIndexes = true,
622 }) : super(
623 delegate: SliverChildListDelegate(
624 children,
625 addAutomaticKeepAlives: addAutomaticKeepAlives,
626 addRepaintBoundaries: addRepaintBoundaries,
627 addSemanticIndexes: addSemanticIndexes,
628 ),
629 );
630
631 /// The children extent builder.
632 ///
633 /// Should return null if asked to build an item extent with a greater index than
634 /// exists.
635 final ItemExtentBuilder itemExtentBuilder;
636
637 @override
638 RenderSliverVariedExtentList createRenderObject(BuildContext context) {
639 final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
640 return RenderSliverVariedExtentList(
641 childManager: element,
642 itemExtentBuilder: itemExtentBuilder,
643 );
644 }
645
646 @override
647 void updateRenderObject(BuildContext context, RenderSliverVariedExtentList renderObject) {
648 renderObject.itemExtentBuilder = itemExtentBuilder;
649 }
650}
651
652/// A sliver that places multiple box children in a two dimensional arrangement.
653///
654/// _To learn more about slivers, see [CustomScrollView.slivers]._
655///
656/// [SliverGrid] places its children in arbitrary positions determined by
657/// [gridDelegate]. Each child is forced to have the size specified by the
658/// [gridDelegate].
659///
660/// The main axis direction of a grid is the direction in which it scrolls; the
661/// cross axis direction is the orthogonal direction.
662///
663/// {@youtube 560 315 https://www.youtube.com/watch?v=ORiTTaVY6mM}
664///
665/// {@tool snippet}
666///
667/// This example, which would be inserted into a [CustomScrollView.slivers]
668/// list, shows twenty boxes in a pretty teal grid:
669///
670/// ```dart
671/// SliverGrid(
672/// gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
673/// maxCrossAxisExtent: 200.0,
674/// mainAxisSpacing: 10.0,
675/// crossAxisSpacing: 10.0,
676/// childAspectRatio: 4.0,
677/// ),
678/// delegate: SliverChildBuilderDelegate(
679/// (BuildContext context, int index) {
680/// return Container(
681/// alignment: Alignment.center,
682/// color: Colors.teal[100 * (index % 9)],
683/// child: Text('grid item $index'),
684/// );
685/// },
686/// childCount: 20,
687/// ),
688/// )
689/// ```
690/// {@end-tool}
691///
692/// {@macro flutter.widgets.SliverChildDelegate.lifecycle}
693///
694/// See also:
695///
696/// * [SliverList], which places its children in a linear array.
697/// * [SliverFixedExtentList], which places its children in a linear
698/// array with a fixed extent in the main axis.
699/// * [SliverPrototypeExtentList], which is similar to [SliverFixedExtentList]
700/// except that it uses a prototype list item instead of a pixel value to define
701/// the main axis extent of each item.
702class SliverGrid extends SliverMultiBoxAdaptorWidget {
703 /// Creates a sliver that places multiple box children in a two dimensional
704 /// arrangement.
705 const SliverGrid({super.key, required super.delegate, required this.gridDelegate});
706
707 /// A sliver that creates a 2D array of widgets that are created on demand.
708 ///
709 /// This constructor is appropriate for sliver grids with a large (or
710 /// infinite) number of children because the builder is called only for those
711 /// children that are actually visible.
712 ///
713 /// Providing a non-null `itemCount` improves the ability of the [SliverGrid]
714 /// to estimate the maximum scroll extent.
715 ///
716 /// `itemBuilder` will be called only with indices greater than or equal to
717 /// zero and less than `itemCount`.
718 ///
719 /// {@macro flutter.widgets.ListView.builder.itemBuilder}
720 ///
721 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
722 ///
723 /// The [gridDelegate] argument is required.
724 ///
725 /// The `addAutomaticKeepAlives` argument corresponds to the
726 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
727 /// `addRepaintBoundaries` argument corresponds to the
728 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
729 /// `addSemanticIndexes` argument corresponds to the
730 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
731 SliverGrid.builder({
732 super.key,
733 required this.gridDelegate,
734 required NullableIndexedWidgetBuilder itemBuilder,
735 ChildIndexGetter? findChildIndexCallback,
736 int? itemCount,
737 bool addAutomaticKeepAlives = true,
738 bool addRepaintBoundaries = true,
739 bool addSemanticIndexes = true,
740 }) : super(
741 delegate: SliverChildBuilderDelegate(
742 itemBuilder,
743 findChildIndexCallback: findChildIndexCallback,
744 childCount: itemCount,
745 addAutomaticKeepAlives: addAutomaticKeepAlives,
746 addRepaintBoundaries: addRepaintBoundaries,
747 addSemanticIndexes: addSemanticIndexes,
748 ),
749 );
750
751 /// Creates a sliver that places multiple box children in a two dimensional
752 /// arrangement with a fixed number of tiles in the cross axis.
753 ///
754 /// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate],
755 /// and a [SliverChildListDelegate] as the [delegate].
756 ///
757 /// See also:
758 ///
759 /// * [GridView.count], the equivalent constructor for [GridView] widgets.
760 SliverGrid.count({
761 super.key,
762 required int crossAxisCount,
763 double mainAxisSpacing = 0.0,
764 double crossAxisSpacing = 0.0,
765 double childAspectRatio = 1.0,
766 List<Widget> children = const <Widget>[],
767 }) : gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
768 crossAxisCount: crossAxisCount,
769 mainAxisSpacing: mainAxisSpacing,
770 crossAxisSpacing: crossAxisSpacing,
771 childAspectRatio: childAspectRatio,
772 ),
773 super(delegate: SliverChildListDelegate(children));
774
775 /// Creates a sliver that places multiple box children in a two dimensional
776 /// arrangement with tiles that each have a maximum cross-axis extent.
777 ///
778 /// Uses a [SliverGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate],
779 /// and a [SliverChildListDelegate] as the [delegate].
780 ///
781 /// See also:
782 ///
783 /// * [GridView.extent], the equivalent constructor for [GridView] widgets.
784 SliverGrid.extent({
785 super.key,
786 required double maxCrossAxisExtent,
787 double mainAxisSpacing = 0.0,
788 double crossAxisSpacing = 0.0,
789 double childAspectRatio = 1.0,
790 List<Widget> children = const <Widget>[],
791 }) : gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
792 maxCrossAxisExtent: maxCrossAxisExtent,
793 mainAxisSpacing: mainAxisSpacing,
794 crossAxisSpacing: crossAxisSpacing,
795 childAspectRatio: childAspectRatio,
796 ),
797 super(delegate: SliverChildListDelegate(children));
798
799 /// The delegate that controls the size and position of the children.
800 final SliverGridDelegate gridDelegate;
801
802 @override
803 RenderSliverGrid createRenderObject(BuildContext context) {
804 final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
805 return RenderSliverGrid(childManager: element, gridDelegate: gridDelegate);
806 }
807
808 @override
809 void updateRenderObject(BuildContext context, RenderSliverGrid renderObject) {
810 renderObject.gridDelegate = gridDelegate;
811 }
812
813 @override
814 double estimateMaxScrollOffset(
815 SliverConstraints? constraints,
816 int firstIndex,
817 int lastIndex,
818 double leadingScrollOffset,
819 double trailingScrollOffset,
820 ) {
821 return super.estimateMaxScrollOffset(
822 constraints,
823 firstIndex,
824 lastIndex,
825 leadingScrollOffset,
826 trailingScrollOffset,
827 ) ??
828 gridDelegate.getLayout(constraints!).computeMaxScrollOffset(delegate.estimatedChildCount!);
829 }
830}
831
832/// An element that lazily builds children for a [SliverMultiBoxAdaptorWidget].
833///
834/// Implements [RenderSliverBoxChildManager], which lets this element manage
835/// the children of subclasses of [RenderSliverMultiBoxAdaptor].
836class SliverMultiBoxAdaptorElement extends RenderObjectElement
837 implements RenderSliverBoxChildManager {
838 /// Creates an element that lazily builds children for the given widget.
839 ///
840 /// If `replaceMovedChildren` is set to true, a new child is proactively
841 /// inflate for the index that was previously occupied by a child that moved
842 /// to a new index. The layout offset of the moved child is copied over to the
843 /// new child. RenderObjects, that depend on the layout offset of existing
844 /// children during [RenderObject.performLayout] should set this to true
845 /// (example: [RenderSliverList]). For RenderObjects that figure out the
846 /// layout offset of their children without looking at the layout offset of
847 /// existing children this should be set to false (example:
848 /// [RenderSliverFixedExtentList]) to avoid inflating unnecessary children.
849 SliverMultiBoxAdaptorElement(
850 SliverMultiBoxAdaptorWidget super.widget, {
851 bool replaceMovedChildren = false,
852 }) : _replaceMovedChildren = replaceMovedChildren;
853
854 final bool _replaceMovedChildren;
855
856 @override
857 RenderSliverMultiBoxAdaptor get renderObject => super.renderObject as RenderSliverMultiBoxAdaptor;
858
859 @override
860 void update(covariant SliverMultiBoxAdaptorWidget newWidget) {
861 final SliverMultiBoxAdaptorWidget oldWidget = widget as SliverMultiBoxAdaptorWidget;
862 super.update(newWidget);
863 final SliverChildDelegate newDelegate = newWidget.delegate;
864 final SliverChildDelegate oldDelegate = oldWidget.delegate;
865 if (newDelegate != oldDelegate &&
866 (newDelegate.runtimeType != oldDelegate.runtimeType ||
867 newDelegate.shouldRebuild(oldDelegate))) {
868 performRebuild();
869 }
870 }
871
872 final SplayTreeMap<int, Element?> _childElements = SplayTreeMap<int, Element?>();
873 RenderBox? _currentBeforeChild;
874
875 @override
876 void performRebuild() {
877 super.performRebuild();
878 _currentBeforeChild = null;
879 bool childrenUpdated = false;
880 assert(_currentlyUpdatingChildIndex == null);
881 try {
882 final SplayTreeMap<int, Element?> newChildren = SplayTreeMap<int, Element?>();
883 final Map<int, double> indexToLayoutOffset = HashMap<int, double>();
884 final SliverMultiBoxAdaptorWidget adaptorWidget = widget as SliverMultiBoxAdaptorWidget;
885 void processElement(int index) {
886 _currentlyUpdatingChildIndex = index;
887 if (_childElements[index] != null && _childElements[index] != newChildren[index]) {
888 // This index has an old child that isn't used anywhere and should be deactivated.
889 _childElements[index] = updateChild(_childElements[index], null, index);
890 childrenUpdated = true;
891 }
892 final Element? newChild = updateChild(
893 newChildren[index],
894 _build(index, adaptorWidget),
895 index,
896 );
897 if (newChild != null) {
898 childrenUpdated = childrenUpdated || _childElements[index] != newChild;
899 _childElements[index] = newChild;
900 final SliverMultiBoxAdaptorParentData parentData =
901 newChild.renderObject!.parentData! as SliverMultiBoxAdaptorParentData;
902 if (index == 0) {
903 parentData.layoutOffset = 0.0;
904 } else if (indexToLayoutOffset.containsKey(index)) {
905 parentData.layoutOffset = indexToLayoutOffset[index];
906 }
907 if (!parentData.keptAlive) {
908 _currentBeforeChild = newChild.renderObject as RenderBox?;
909 }
910 } else {
911 childrenUpdated = true;
912 _childElements.remove(index);
913 }
914 }
915
916 for (final int index in _childElements.keys.toList()) {
917 final Key? key = _childElements[index]!.widget.key;
918 final int? newIndex = key == null ? null : adaptorWidget.delegate.findIndexByKey(key);
919 final SliverMultiBoxAdaptorParentData? childParentData =
920 _childElements[index]!.renderObject?.parentData as SliverMultiBoxAdaptorParentData?;
921
922 if (childParentData != null && childParentData.layoutOffset != null) {
923 indexToLayoutOffset[index] = childParentData.layoutOffset!;
924 }
925
926 if (newIndex != null && newIndex != index) {
927 // The layout offset of the child being moved is no longer accurate.
928 if (childParentData != null) {
929 childParentData.layoutOffset = null;
930 }
931
932 newChildren[newIndex] = _childElements[index];
933 if (_replaceMovedChildren) {
934 // We need to make sure the original index gets processed.
935 newChildren.putIfAbsent(index, () => null);
936 }
937 // We do not want the remapped child to get deactivated during processElement.
938 _childElements.remove(index);
939 } else {
940 newChildren.putIfAbsent(index, () => _childElements[index]);
941 }
942 }
943
944 renderObject.debugChildIntegrityEnabled =
945 false; // Moving children will temporary violate the integrity.
946 newChildren.keys.forEach(processElement);
947 // An element rebuild only updates existing children. The underflow check
948 // is here to make sure we look ahead one more child if we were at the end
949 // of the child list before the update. By doing so, we can update the max
950 // scroll offset during the layout phase. Otherwise, the layout phase may
951 // be skipped, and the scroll view may be stuck at the previous max
952 // scroll offset.
953 //
954 // This logic is not needed if any existing children has been updated,
955 // because we will not skip the layout phase if that happens.
956 if (!childrenUpdated && _didUnderflow) {
957 final int lastKey = _childElements.lastKey() ?? -1;
958 final int rightBoundary = lastKey + 1;
959 newChildren[rightBoundary] = _childElements[rightBoundary];
960 processElement(rightBoundary);
961 }
962 } finally {
963 _currentlyUpdatingChildIndex = null;
964 renderObject.debugChildIntegrityEnabled = true;
965 }
966 }
967
968 Widget? _build(int index, SliverMultiBoxAdaptorWidget widget) {
969 return widget.delegate.build(this, index);
970 }
971
972 @override
973 void createChild(int index, {required RenderBox? after}) {
974 assert(_currentlyUpdatingChildIndex == null);
975 owner!.buildScope(this, () {
976 final bool insertFirst = after == null;
977 assert(insertFirst || _childElements[index - 1] != null);
978 _currentBeforeChild =
979 insertFirst ? null : (_childElements[index - 1]!.renderObject as RenderBox?);
980 Element? newChild;
981 try {
982 final SliverMultiBoxAdaptorWidget adaptorWidget = widget as SliverMultiBoxAdaptorWidget;
983 _currentlyUpdatingChildIndex = index;
984 newChild = updateChild(_childElements[index], _build(index, adaptorWidget), index);
985 } finally {
986 _currentlyUpdatingChildIndex = null;
987 }
988 if (newChild != null) {
989 _childElements[index] = newChild;
990 } else {
991 _childElements.remove(index);
992 }
993 });
994 }
995
996 @override
997 Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
998 final SliverMultiBoxAdaptorParentData? oldParentData =
999 child?.renderObject?.parentData as SliverMultiBoxAdaptorParentData?;
1000 final Element? newChild = super.updateChild(child, newWidget, newSlot);
1001 final SliverMultiBoxAdaptorParentData? newParentData =
1002 newChild?.renderObject?.parentData as SliverMultiBoxAdaptorParentData?;
1003
1004 // Preserve the old layoutOffset if the renderObject was swapped out.
1005 if (oldParentData != newParentData && oldParentData != null && newParentData != null) {
1006 newParentData.layoutOffset = oldParentData.layoutOffset;
1007 }
1008 return newChild;
1009 }
1010
1011 @override
1012 void forgetChild(Element child) {
1013 assert(child.slot != null);
1014 assert(_childElements.containsKey(child.slot));
1015 _childElements.remove(child.slot);
1016 super.forgetChild(child);
1017 }
1018
1019 @override
1020 void removeChild(RenderBox child) {
1021 final int index = renderObject.indexOf(child);
1022 assert(_currentlyUpdatingChildIndex == null);
1023 assert(index >= 0);
1024 owner!.buildScope(this, () {
1025 assert(_childElements.containsKey(index));
1026 try {
1027 _currentlyUpdatingChildIndex = index;
1028 final Element? result = updateChild(_childElements[index], null, index);
1029 assert(result == null);
1030 } finally {
1031 _currentlyUpdatingChildIndex = null;
1032 }
1033 _childElements.remove(index);
1034 assert(!_childElements.containsKey(index));
1035 });
1036 }
1037
1038 static double _extrapolateMaxScrollOffset(
1039 int firstIndex,
1040 int lastIndex,
1041 double leadingScrollOffset,
1042 double trailingScrollOffset,
1043 int childCount,
1044 ) {
1045 if (lastIndex == childCount - 1) {
1046 return trailingScrollOffset;
1047 }
1048 final int reifiedCount = lastIndex - firstIndex + 1;
1049 final double averageExtent = (trailingScrollOffset - leadingScrollOffset) / reifiedCount;
1050 final int remainingCount = childCount - lastIndex - 1;
1051 return trailingScrollOffset + averageExtent * remainingCount;
1052 }
1053
1054 @override
1055 double estimateMaxScrollOffset(
1056 SliverConstraints? constraints, {
1057 int? firstIndex,
1058 int? lastIndex,
1059 double? leadingScrollOffset,
1060 double? trailingScrollOffset,
1061 }) {
1062 final int? childCount = estimatedChildCount;
1063 if (childCount == null) {
1064 return double.infinity;
1065 }
1066 return (widget as SliverMultiBoxAdaptorWidget).estimateMaxScrollOffset(
1067 constraints,
1068 firstIndex!,
1069 lastIndex!,
1070 leadingScrollOffset!,
1071 trailingScrollOffset!,
1072 ) ??
1073 _extrapolateMaxScrollOffset(
1074 firstIndex,
1075 lastIndex,
1076 leadingScrollOffset,
1077 trailingScrollOffset,
1078 childCount,
1079 );
1080 }
1081
1082 @override
1083 int? get estimatedChildCount =>
1084 (widget as SliverMultiBoxAdaptorWidget).delegate.estimatedChildCount;
1085
1086 @override
1087 int get childCount {
1088 int? result = estimatedChildCount;
1089 if (result == null) {
1090 // Since childCount was called, we know that we reached the end of
1091 // the list (as in, _build return null once), so we know that the
1092 // list is finite.
1093 // Let's do an open-ended binary search to find the end of the list
1094 // manually.
1095 int lo = 0;
1096 int hi = 1;
1097 final SliverMultiBoxAdaptorWidget adaptorWidget = widget as SliverMultiBoxAdaptorWidget;
1098 const int max =
1099 kIsWeb
1100 ? 9007199254740992 // max safe integer on JS (from 0 to this number x != x+1)
1101 : ((1 << 63) - 1);
1102 while (_build(hi - 1, adaptorWidget) != null) {
1103 lo = hi - 1;
1104 if (hi < max ~/ 2) {
1105 hi *= 2;
1106 } else if (hi < max) {
1107 hi = max;
1108 } else {
1109 throw FlutterError(
1110 'Could not find the number of children in ${adaptorWidget.delegate}.\n'
1111 "The childCount getter was called (implying that the delegate's builder returned null "
1112 'for a positive index), but even building the child with index $hi (the maximum '
1113 'possible integer) did not return null. Consider implementing childCount to avoid '
1114 'the cost of searching for the final child.',
1115 );
1116 }
1117 }
1118 while (hi - lo > 1) {
1119 final int mid = (hi - lo) ~/ 2 + lo;
1120 if (_build(mid - 1, adaptorWidget) == null) {
1121 hi = mid;
1122 } else {
1123 lo = mid;
1124 }
1125 }
1126 result = lo;
1127 }
1128 return result;
1129 }
1130
1131 @override
1132 void didStartLayout() {
1133 assert(debugAssertChildListLocked());
1134 }
1135
1136 @override
1137 void didFinishLayout() {
1138 assert(debugAssertChildListLocked());
1139 final int firstIndex = _childElements.firstKey() ?? 0;
1140 final int lastIndex = _childElements.lastKey() ?? 0;
1141 (widget as SliverMultiBoxAdaptorWidget).delegate.didFinishLayout(firstIndex, lastIndex);
1142 }
1143
1144 int? _currentlyUpdatingChildIndex;
1145
1146 @override
1147 bool debugAssertChildListLocked() {
1148 assert(_currentlyUpdatingChildIndex == null);
1149 return true;
1150 }
1151
1152 @override
1153 void didAdoptChild(RenderBox child) {
1154 assert(_currentlyUpdatingChildIndex != null);
1155 final SliverMultiBoxAdaptorParentData childParentData =
1156 child.parentData! as SliverMultiBoxAdaptorParentData;
1157 childParentData.index = _currentlyUpdatingChildIndex;
1158 }
1159
1160 bool _didUnderflow = false;
1161
1162 @override
1163 void setDidUnderflow(bool value) {
1164 _didUnderflow = value;
1165 }
1166
1167 @override
1168 void insertRenderObjectChild(covariant RenderObject child, int slot) {
1169 assert(_currentlyUpdatingChildIndex == slot);
1170 assert(renderObject.debugValidateChild(child));
1171 renderObject.insert(child as RenderBox, after: _currentBeforeChild);
1172 assert(() {
1173 final SliverMultiBoxAdaptorParentData childParentData =
1174 child.parentData! as SliverMultiBoxAdaptorParentData;
1175 assert(slot == childParentData.index);
1176 return true;
1177 }());
1178 }
1179
1180 @override
1181 void moveRenderObjectChild(covariant RenderObject child, int oldSlot, int newSlot) {
1182 assert(_currentlyUpdatingChildIndex == newSlot);
1183 renderObject.move(child as RenderBox, after: _currentBeforeChild);
1184 }
1185
1186 @override
1187 void removeRenderObjectChild(covariant RenderObject child, int slot) {
1188 assert(_currentlyUpdatingChildIndex != null);
1189 renderObject.remove(child as RenderBox);
1190 }
1191
1192 @override
1193 void visitChildren(ElementVisitor visitor) {
1194 // The toList() is to make a copy so that the underlying list can be modified by
1195 // the visitor:
1196 assert(!_childElements.values.any((Element? child) => child == null));
1197 _childElements.values.cast<Element>().toList().forEach(visitor);
1198 }
1199
1200 @override
1201 void debugVisitOnstageChildren(ElementVisitor visitor) {
1202 _childElements.values
1203 .cast<Element>()
1204 .where((Element child) {
1205 final SliverMultiBoxAdaptorParentData parentData =
1206 child.renderObject!.parentData! as SliverMultiBoxAdaptorParentData;
1207 final double itemExtent = switch (renderObject.constraints.axis) {
1208 Axis.horizontal => child.renderObject!.paintBounds.width,
1209 Axis.vertical => child.renderObject!.paintBounds.height,
1210 };
1211
1212 return parentData.layoutOffset != null &&
1213 parentData.layoutOffset! <
1214 renderObject.constraints.scrollOffset +
1215 renderObject.constraints.remainingPaintExtent &&
1216 parentData.layoutOffset! + itemExtent > renderObject.constraints.scrollOffset;
1217 })
1218 .forEach(visitor);
1219 }
1220}
1221
1222/// A sliver widget that makes its sliver child partially transparent.
1223///
1224/// This class paints its sliver child into an intermediate buffer and then
1225/// blends the sliver back into the scene partially transparent.
1226///
1227/// For values of opacity other than 0.0 and 1.0, this class is relatively
1228/// expensive because it requires painting the sliver child into an intermediate
1229/// buffer. For the value 0.0, the sliver child is not painted at all.
1230/// For the value 1.0, the sliver child is painted immediately without an
1231/// intermediate buffer.
1232///
1233/// {@tool dartpad}
1234///
1235/// This example shows a [SliverList] when the `_visible` member field is true,
1236/// and hides it when it is false.
1237///
1238/// This is more efficient than adding and removing the sliver child widget from
1239/// the tree on demand, but it does not affect how much the list scrolls (the
1240/// [SliverList] is still present, merely invisible).
1241///
1242/// ** See code in examples/api/lib/widgets/sliver/sliver_opacity.1.dart **
1243/// {@end-tool}
1244///
1245/// See also:
1246///
1247/// * [Opacity], which can apply a uniform alpha effect to its child using the
1248/// [RenderBox] layout protocol.
1249/// * [AnimatedOpacity], which uses an animation internally to efficiently
1250/// animate [Opacity].
1251/// * [SliverVisibility], which can hide a child more efficiently (albeit less
1252/// subtly, because it is either visible or hidden, rather than allowing
1253/// fractional opacity values). Specifically, the [SliverVisibility.maintain]
1254/// constructor is equivalent to using a sliver opacity widget with values of
1255/// `0.0` or `1.0`.
1256class SliverOpacity extends SingleChildRenderObjectWidget {
1257 /// Creates a sliver that makes its sliver child partially transparent.
1258 ///
1259 /// The [opacity] argument must be between zero and one, inclusive.
1260 const SliverOpacity({
1261 super.key,
1262 required this.opacity,
1263 this.alwaysIncludeSemantics = false,
1264 Widget? sliver,
1265 }) : assert(opacity >= 0.0 && opacity <= 1.0),
1266 super(child: sliver);
1267
1268 /// The fraction to scale the sliver child's alpha value.
1269 ///
1270 /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
1271 /// (i.e. invisible).
1272 ///
1273 /// Values 1.0 and 0.0 are painted with a fast path. Other values
1274 /// require painting the sliver child into an intermediate buffer, which is
1275 /// expensive.
1276 final double opacity;
1277
1278 /// Whether the semantic information of the sliver child is always included.
1279 ///
1280 /// Defaults to false.
1281 ///
1282 /// When true, regardless of the opacity settings, the sliver child semantic
1283 /// information is exposed as if the widget were fully visible. This is
1284 /// useful in cases where labels may be hidden during animations that
1285 /// would otherwise contribute relevant semantics.
1286 final bool alwaysIncludeSemantics;
1287
1288 @override
1289 RenderSliverOpacity createRenderObject(BuildContext context) {
1290 return RenderSliverOpacity(opacity: opacity, alwaysIncludeSemantics: alwaysIncludeSemantics);
1291 }
1292
1293 @override
1294 void updateRenderObject(BuildContext context, RenderSliverOpacity renderObject) {
1295 renderObject
1296 ..opacity = opacity
1297 ..alwaysIncludeSemantics = alwaysIncludeSemantics;
1298 }
1299
1300 @override
1301 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1302 super.debugFillProperties(properties);
1303 properties.add(DiagnosticsProperty<double>('opacity', opacity));
1304 properties.add(
1305 FlagProperty(
1306 'alwaysIncludeSemantics',
1307 value: alwaysIncludeSemantics,
1308 ifTrue: 'alwaysIncludeSemantics',
1309 ),
1310 );
1311 }
1312}
1313
1314/// A sliver widget that is invisible during hit testing.
1315///
1316/// When [ignoring] is true, this widget (and its subtree) is invisible
1317/// to hit testing. It still consumes space during layout and paints its sliver
1318/// child as usual. It just cannot be the target of located events, because it
1319/// returns false from [RenderSliver.hitTest].
1320///
1321/// ## Semantics
1322///
1323/// Using this class may also affect how the semantics subtree underneath is
1324/// collected.
1325///
1326/// {@macro flutter.widgets.IgnorePointer.semantics}
1327///
1328/// {@macro flutter.widgets.IgnorePointer.ignoringSemantics}
1329///
1330/// See also:
1331///
1332/// * [IgnorePointer], the equivalent widget for boxes.
1333class SliverIgnorePointer extends SingleChildRenderObjectWidget {
1334 /// Creates a sliver widget that is invisible to hit testing.
1335 const SliverIgnorePointer({
1336 super.key,
1337 this.ignoring = true,
1338 @Deprecated(
1339 'Create a custom sliver ignore pointer widget instead. '
1340 'This feature was deprecated after v3.8.0-12.0.pre.',
1341 )
1342 this.ignoringSemantics,
1343 Widget? sliver,
1344 }) : super(child: sliver);
1345
1346 /// Whether this sliver is ignored during hit testing.
1347 ///
1348 /// Regardless of whether this sliver is ignored during hit testing, it will
1349 /// still consume space during layout and be visible during painting.
1350 ///
1351 /// {@macro flutter.widgets.IgnorePointer.semantics}
1352 final bool ignoring;
1353
1354 /// Whether the semantics of this sliver is ignored when compiling the
1355 /// semantics tree.
1356 ///
1357 /// {@macro flutter.widgets.IgnorePointer.ignoringSemantics}
1358 @Deprecated(
1359 'Create a custom sliver ignore pointer widget instead. '
1360 'This feature was deprecated after v3.8.0-12.0.pre.',
1361 )
1362 final bool? ignoringSemantics;
1363
1364 @override
1365 RenderSliverIgnorePointer createRenderObject(BuildContext context) {
1366 return RenderSliverIgnorePointer(ignoring: ignoring, ignoringSemantics: ignoringSemantics);
1367 }
1368
1369 @override
1370 void updateRenderObject(BuildContext context, RenderSliverIgnorePointer renderObject) {
1371 renderObject
1372 ..ignoring = ignoring
1373 ..ignoringSemantics = ignoringSemantics;
1374 }
1375
1376 @override
1377 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1378 super.debugFillProperties(properties);
1379 properties.add(DiagnosticsProperty<bool>('ignoring', ignoring));
1380 properties.add(
1381 DiagnosticsProperty<bool>('ignoringSemantics', ignoringSemantics, defaultValue: null),
1382 );
1383 }
1384}
1385
1386/// A sliver that lays its sliver child out as if it was in the tree, but
1387/// without painting anything, without making the sliver child available for hit
1388/// testing, and without taking any room in the parent.
1389///
1390/// Animations continue to run in offstage sliver children, and therefore use
1391/// battery and CPU time, regardless of whether the animations end up being
1392/// visible.
1393///
1394/// To hide a sliver widget from view while it is
1395/// not needed, prefer removing the widget from the tree entirely rather than
1396/// keeping it alive in an [Offstage] subtree.
1397///
1398/// See also:
1399///
1400/// * [Offstage], the equivalent widget for boxes.
1401class SliverOffstage extends SingleChildRenderObjectWidget {
1402 /// Creates a sliver that visually hides its sliver child.
1403 const SliverOffstage({super.key, this.offstage = true, Widget? sliver}) : super(child: sliver);
1404
1405 /// Whether the sliver child is hidden from the rest of the tree.
1406 ///
1407 /// If true, the sliver child is laid out as if it was in the tree, but
1408 /// without painting anything, without making the child available for hit
1409 /// testing, and without taking any room in the parent.
1410 ///
1411 /// If false, the sliver child is included in the tree as normal.
1412 final bool offstage;
1413
1414 @override
1415 RenderSliverOffstage createRenderObject(BuildContext context) =>
1416 RenderSliverOffstage(offstage: offstage);
1417
1418 @override
1419 void updateRenderObject(BuildContext context, RenderSliverOffstage renderObject) {
1420 renderObject.offstage = offstage;
1421 }
1422
1423 @override
1424 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1425 super.debugFillProperties(properties);
1426 properties.add(DiagnosticsProperty<bool>('offstage', offstage));
1427 }
1428
1429 @override
1430 SingleChildRenderObjectElement createElement() => _SliverOffstageElement(this);
1431}
1432
1433class _SliverOffstageElement extends SingleChildRenderObjectElement {
1434 _SliverOffstageElement(SliverOffstage super.widget);
1435
1436 @override
1437 void debugVisitOnstageChildren(ElementVisitor visitor) {
1438 if (!(widget as SliverOffstage).offstage) {
1439 super.debugVisitOnstageChildren(visitor);
1440 }
1441 }
1442}
1443
1444/// Mark a child as needing to stay alive even when it's in a lazy list that
1445/// would otherwise remove it.
1446///
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
1459/// [TwoDimensionalChildBuilderDelegate] and [TwoDimensionalChildListDelegate].
1460///
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.
1467class KeepAlive extends ParentDataWidget<KeepAliveParentDataMixin> {
1468 /// Marks a child as needing to remain alive.
1469 const KeepAlive({super.key, required this.keepAlive, required super.child});
1470
1471 /// Whether to keep the child alive.
1472 ///
1473 /// If this is false, it is as if this widget was omitted.
1474 final bool keepAlive;
1475
1476 @override
1477 void applyParentData(RenderObject renderObject) {
1478 assert(renderObject.parentData is KeepAliveParentDataMixin);
1479 final KeepAliveParentDataMixin parentData =
1480 renderObject.parentData! as KeepAliveParentDataMixin;
1481 if (parentData.keepAlive != keepAlive) {
1482 // No need to redo layout if it became true.
1483 parentData.keepAlive = keepAlive;
1484 if (!keepAlive) {
1485 renderObject.parent?.markNeedsLayout();
1486 }
1487 }
1488 }
1489
1490 // We only return true if [keepAlive] is true, because turning _off_ keep
1491 // alive requires a layout to do the garbage collection (but turning it on
1492 // requires nothing, since by definition the widget is already alive and won't
1493 // go away _unless_ we do a layout).
1494 @override
1495 bool debugCanApplyOutOfTurn() => keepAlive;
1496
1497 @override
1498 Type get debugTypicalAncestorWidgetClass =>
1499 throw FlutterError(
1500 'Multiple Types are supported, use debugTypicalAncestorWidgetDescription.',
1501 );
1502
1503 @override
1504 String get debugTypicalAncestorWidgetDescription =>
1505 'SliverWithKeepAliveWidget or TwoDimensionalViewport';
1506
1507 @override
1508 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1509 super.debugFillProperties(properties);
1510 properties.add(DiagnosticsProperty<bool>('keepAlive', keepAlive));
1511 }
1512}
1513
1514/// A sliver that constrains the cross axis extent of its sliver child.
1515///
1516/// The [SliverConstrainedCrossAxis] takes a [maxExtent] parameter and uses it as
1517/// the cross axis extent of the [SliverConstraints] passed to the sliver child.
1518/// The widget ensures that the [maxExtent] is a nonnegative value.
1519///
1520/// This is useful when you want to apply a custom cross-axis extent constraint
1521/// to a sliver child, as slivers typically consume the full cross axis extent.
1522///
1523/// This widget also sets its parent data's [SliverPhysicalParentData.crossAxisFlex]
1524/// to 0, so that it informs [SliverCrossAxisGroup] that it should not flex
1525/// in the cross axis direction.
1526///
1527/// {@tool dartpad}
1528/// In this sample the [SliverConstrainedCrossAxis] sizes its child so that the
1529/// cross axis extent takes up less space than the actual viewport.
1530///
1531/// ** See code in examples/api/lib/widgets/sliver/sliver_constrained_cross_axis.0.dart **
1532/// {@end-tool}
1533///
1534/// See also:
1535///
1536/// * [SliverCrossAxisGroup], the widget which makes use of 0 flex factor set by
1537/// this widget.
1538class SliverConstrainedCrossAxis extends StatelessWidget {
1539 /// Creates a sliver that constrains the cross axis extent of its sliver child.
1540 ///
1541 /// The [maxExtent] parameter is required and must be nonnegative.
1542 const SliverConstrainedCrossAxis({super.key, required this.maxExtent, required this.sliver});
1543
1544 /// The cross axis extent to apply to the sliver child.
1545 ///
1546 /// This value must be nonnegative.
1547 final double maxExtent;
1548
1549 /// The widget below this widget in the tree.
1550 ///
1551 /// Must be a sliver.
1552 final Widget sliver;
1553
1554 @override
1555 Widget build(BuildContext context) {
1556 return _SliverZeroFlexParentDataWidget(
1557 sliver: _SliverConstrainedCrossAxis(maxExtent: maxExtent, sliver: sliver),
1558 );
1559 }
1560}
1561
1562class _SliverZeroFlexParentDataWidget extends ParentDataWidget<SliverPhysicalParentData> {
1563 const _SliverZeroFlexParentDataWidget({required Widget sliver}) : super(child: sliver);
1564
1565 @override
1566 void applyParentData(RenderObject renderObject) {
1567 assert(renderObject.parentData is SliverPhysicalParentData);
1568 final SliverPhysicalParentData parentData =
1569 renderObject.parentData! as SliverPhysicalParentData;
1570 bool needsLayout = false;
1571 if (parentData.crossAxisFlex != 0) {
1572 parentData.crossAxisFlex = 0;
1573 needsLayout = true;
1574 }
1575
1576 if (needsLayout) {
1577 renderObject.parent?.markNeedsLayout();
1578 }
1579 }
1580
1581 @override
1582 Type get debugTypicalAncestorWidgetClass => SliverCrossAxisGroup;
1583}
1584
1585class _SliverConstrainedCrossAxis extends SingleChildRenderObjectWidget {
1586 const _SliverConstrainedCrossAxis({required this.maxExtent, required Widget sliver})
1587 : assert(maxExtent >= 0.0),
1588 super(child: sliver);
1589
1590 /// The cross axis extent to apply to the sliver child.
1591 ///
1592 /// This value must be nonnegative.
1593 final double maxExtent;
1594
1595 @override
1596 RenderSliverConstrainedCrossAxis createRenderObject(BuildContext context) {
1597 return RenderSliverConstrainedCrossAxis(maxExtent: maxExtent);
1598 }
1599
1600 @override
1601 void updateRenderObject(BuildContext context, RenderSliverConstrainedCrossAxis renderObject) {
1602 renderObject.maxExtent = maxExtent;
1603 }
1604}
1605
1606/// Set a flex factor for allocating space in the cross axis direction.
1607///
1608/// This is a [ParentDataWidget] to be used in [SliverCrossAxisGroup].
1609/// After all slivers with null or zero flex (e.g. [SliverConstrainedCrossAxis])
1610/// are laid out (which should determine their own [SliverGeometry.crossAxisExtent]),
1611/// the remaining space is laid out among the slivers with nonzero flex
1612/// proportionally to their flex value.
1613class SliverCrossAxisExpanded extends ParentDataWidget<SliverPhysicalContainerParentData> {
1614 /// Creates an object that assigns a [flex] value to the child sliver.
1615 ///
1616 /// The provided [flex] value must be greater than 0.
1617 const SliverCrossAxisExpanded({super.key, required this.flex, required Widget sliver})
1618 : assert(flex > 0 && flex < double.infinity),
1619 super(child: sliver);
1620
1621 /// Flex value for allocating cross axis extent left after laying out the children with
1622 /// constrained cross axis. The children with flex values will have the remaining extent
1623 /// allocated proportionally to their flex value. This must an integer between
1624 /// 0 and infinity, exclusive.
1625 final int flex;
1626
1627 @override
1628 void applyParentData(RenderObject renderObject) {
1629 assert(renderObject.parentData is SliverPhysicalContainerParentData);
1630 assert(renderObject.parent is RenderSliverCrossAxisGroup);
1631 final SliverPhysicalParentData parentData =
1632 renderObject.parentData! as SliverPhysicalParentData;
1633 bool needsLayout = false;
1634
1635 if (parentData.crossAxisFlex != flex) {
1636 parentData.crossAxisFlex = flex;
1637 needsLayout = true;
1638 }
1639
1640 if (needsLayout) {
1641 renderObject.parent?.markNeedsLayout();
1642 }
1643 }
1644
1645 @override
1646 Type get debugTypicalAncestorWidgetClass => SliverCrossAxisGroup;
1647}
1648
1649/// A sliver that places multiple sliver children in a linear array along
1650/// the cross axis.
1651///
1652/// ## Layout algorithm
1653///
1654/// _This section describes how the framework causes [RenderSliverCrossAxisGroup]
1655/// to position its children._
1656///
1657/// Layout for a [RenderSliverCrossAxisGroup] has four steps:
1658///
1659/// 1. Layout each child with a null or zero flex factor with cross axis constraint
1660/// being whatever cross axis space is remaining after laying out any previous
1661/// sliver. Slivers with null or zero flex factor should determine their own
1662/// [SliverGeometry.crossAxisExtent]. For example, the [SliverConstrainedCrossAxis]
1663/// widget uses either [SliverConstrainedCrossAxis.maxExtent] or
1664/// [SliverConstraints.crossAxisExtent], deciding between whichever is smaller.
1665/// 2. Divide up the remaining cross axis space among the children with non-zero flex
1666/// factors according to their flex factor. For example, a child with a flex
1667/// factor of 2.0 will receive twice the amount of cross axis space as a child
1668/// with a flex factor 1.0.
1669/// 3. Layout each of the remaining children with the cross axis constraint
1670/// allocated in the previous step.
1671/// 4. Set the geometry to that of whichever child has the longest
1672/// [SliverGeometry.scrollExtent] with the [SliverGeometry.crossAxisExtent] adjusted
1673/// to [SliverConstraints.crossAxisExtent].
1674///
1675/// {@tool dartpad}
1676/// In this sample the [SliverCrossAxisGroup] sizes its three [children] so that
1677/// the first normal [SliverList] has a flex factor of 1, the second [SliverConstrainedCrossAxis]
1678/// has a flex factor of 0 and a maximum cross axis extent of 200.0, and the third
1679/// [SliverCrossAxisExpanded] has a flex factor of 2.
1680///
1681/// ** See code in examples/api/lib/widgets/sliver/sliver_cross_axis_group.0.dart **
1682/// {@end-tool}
1683///
1684/// See also:
1685///
1686/// * [SliverCrossAxisExpanded], which is the [ParentDataWidget] for setting a flex
1687/// value to a widget.
1688/// * [SliverConstrainedCrossAxis], which is a [RenderObjectWidget] for setting
1689/// an extent to constrain the widget to.
1690/// * [SliverMainAxisGroup], which is the [RenderObjectWidget] for laying out
1691/// multiple slivers along the main axis.
1692class SliverCrossAxisGroup extends MultiChildRenderObjectWidget {
1693 /// Creates a sliver that places sliver children in a linear array along
1694 /// the cross axis.
1695 const SliverCrossAxisGroup({super.key, required List<Widget> slivers}) : super(children: slivers);
1696
1697 @override
1698 RenderSliverCrossAxisGroup createRenderObject(BuildContext context) {
1699 return RenderSliverCrossAxisGroup();
1700 }
1701}
1702
1703/// A sliver that places multiple sliver children in a linear array along
1704/// the main axis, one after another.
1705///
1706/// ## Layout algorithm
1707///
1708/// _This section describes how the framework causes [RenderSliverMainAxisGroup]
1709/// to position its children._
1710///
1711/// Layout for a [RenderSliverMainAxisGroup] has four steps:
1712///
1713/// 1. Keep track of an offset variable which is the total [SliverGeometry.scrollExtent]
1714/// of the slivers laid out so far.
1715/// 2. To determine the constraints for the next sliver child to layout, calculate the
1716/// amount of paint extent occupied from 0.0 to the offset variable and subtract this from
1717/// [SliverConstraints.remainingPaintExtent] minus to use as the child's
1718/// [SliverConstraints.remainingPaintExtent]. For the [SliverConstraints.scrollOffset],
1719/// take the provided constraint's value and subtract out the offset variable, using
1720/// 0.0 if negative.
1721/// 3. Once we finish laying out all the slivers, this offset variable represents
1722/// the total [SliverGeometry.scrollExtent] of the sliver group. Since it is possible
1723/// for specialized slivers to try to paint itself outside of the bounds of the
1724/// sliver group's scroll extent (see [SliverPersistentHeader]), we must do a
1725/// second pass to set a [SliverPhysicalParentData.paintOffset] to make sure it
1726/// is within the bounds of the sliver group.
1727/// 4. Finally, set the [RenderSliverMainAxisGroup.geometry] with the total
1728/// [SliverGeometry.scrollExtent], [SliverGeometry.paintExtent] calculated from
1729/// the constraints and [SliverGeometry.scrollExtent], and [SliverGeometry.maxPaintExtent].
1730///
1731/// {@tool dartpad}
1732/// In this sample the [CustomScrollView] renders a [SliverMainAxisGroup] and a
1733/// [SliverToBoxAdapter] with some content. The [SliverMainAxisGroup] renders a
1734/// [SliverAppBar], [SliverList], and [SliverToBoxAdapter]. Notice that when the
1735/// [SliverMainAxisGroup] goes out of view, so does the pinned [SliverAppBar].
1736///
1737/// ** See code in examples/api/lib/widgets/sliver/sliver_main_axis_group.0.dart **
1738/// {@end-tool}
1739///
1740/// See also:
1741///
1742/// * [SliverPersistentHeader], which is a [RenderObjectWidget] which may require
1743/// adjustment to its [SliverPhysicalParentData.paintOffset] to make it fit
1744/// within the computed [SliverGeometry.scrollExtent] of the [SliverMainAxisGroup].
1745/// * [SliverCrossAxisGroup], which is the [RenderObjectWidget] for laying out
1746/// multiple slivers along the cross axis.
1747class SliverMainAxisGroup extends MultiChildRenderObjectWidget {
1748 /// Creates a sliver that places sliver children in a linear array along
1749 /// the main axis.
1750 const SliverMainAxisGroup({super.key, required List<Widget> slivers}) : super(children: slivers);
1751
1752 @override
1753 MultiChildRenderObjectElement createElement() => _SliverMainAxisGroupElement(this);
1754
1755 @override
1756 RenderSliverMainAxisGroup createRenderObject(BuildContext context) {
1757 return RenderSliverMainAxisGroup();
1758 }
1759}
1760
1761class _SliverMainAxisGroupElement extends MultiChildRenderObjectElement {
1762 _SliverMainAxisGroupElement(SliverMainAxisGroup super.widget);
1763
1764 @override
1765 void debugVisitOnstageChildren(ElementVisitor visitor) {
1766 children
1767 .where((Element e) {
1768 final RenderSliver renderSliver = e.renderObject! as RenderSliver;
1769 return renderSliver.geometry!.visible;
1770 })
1771 .forEach(visitor);
1772 }
1773}
1774
1775/// A sliver that ensures its sliver child is included in the semantics tree.
1776///
1777/// This sliver ensures that its child sliver is still visited by the [RenderViewport]
1778/// when constructing the semantics tree, and is not clipped out of the semantics tree by
1779/// the [RenderViewport] when it is outside the current viewport and outside the cache extent.
1780///
1781/// The child sliver may still be excluded from the semantics tree if its [RenderSliver] does
1782/// not provide a valid [RenderSliver.semanticBounds]. This sliver does not guarantee its
1783/// child sliver is laid out.
1784///
1785/// Be mindful when positioning [SliverEnsureSemantics] in a [CustomScrollView] after slivers that build
1786/// their children lazily, like [SliverList]. Lazy slivers might underestimate the total scrollable size (scroll
1787/// extent) before the [SliverEnsureSemantics] widget. This inaccuracy can cause problems for assistive
1788/// technologies (e.g., screen readers), which rely on a correct scroll extent to navigate properly; they
1789/// might fail to scroll accurately to the content wrapped by [SliverEnsureSemantics].
1790///
1791/// To avoid this potential issue and ensure the scroll extent is calculated accurately up to this sliver,
1792/// it's recommended to use slivers that can determine their extent precisely beforehand. Instead of
1793/// [SliverList], consider using [SliverFixedExtentList], [SliverVariedExtentList], or
1794/// [SliverPrototypeExtentList]. If using [SliverGrid], ensure it employs a delegate such as
1795/// [SliverGridDelegateWithFixedCrossAxisCount] or [SliverGridDelegateWithMaxCrossAxisExtent].
1796/// Using these alternatives guarantees that the scrollable area's size is known accurately, allowing
1797/// assistive technologies to function correctly with [SliverEnsureSemantics].
1798///
1799/// {@tool dartpad}
1800/// This example shows how to use [SliverEnsureSemantics] to keep certain headers and lists
1801/// available to assistive technologies while they are outside the current viewport and cache extent.
1802///
1803/// ** See code in examples/api/lib/widgets/sliver/sliver_ensure_semantics.0.dart **
1804/// {@end-tool}
1805// TODO(Renzo-Olivares): Investigate potential solutions for revealing off screen items, https://github.com/flutter/flutter/issues/166703.
1806class SliverEnsureSemantics extends SingleChildRenderObjectWidget {
1807 /// Creates a sliver that ensures its sliver child is included in the semantics tree.
1808 const SliverEnsureSemantics({super.key, required Widget sliver}) : super(child: sliver);
1809
1810 @override
1811 RenderObject createRenderObject(BuildContext context) => _RenderSliverEnsureSemantics();
1812}
1813
1814/// Ensures its sliver child is included in the semantics tree.
1815class _RenderSliverEnsureSemantics extends RenderProxySliver {
1816 @override
1817 bool get ensureSemantics => true;
1818}
1819

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com