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/cupertino.dart';
6/// @docImport 'package:flutter/material.dart';
7/// @docImport 'package:flutter/widgets.dart';
8/// @docImport 'package:flutter_test/flutter_test.dart';
9library;
10
11import 'dart:math' as math;
12
13import 'package:flutter/gestures.dart';
14import 'package:flutter/rendering.dart';
15
16import 'basic.dart';
17import 'debug.dart';
18import 'focus_manager.dart';
19import 'focus_scope.dart';
20import 'framework.dart';
21import 'media_query.dart';
22import 'notification_listener.dart';
23import 'primary_scroll_controller.dart';
24import 'scroll_configuration.dart';
25import 'scroll_controller.dart';
26import 'scroll_delegate.dart';
27import 'scroll_notification.dart';
28import 'scroll_physics.dart';
29import 'scrollable.dart';
30import 'scrollable_helpers.dart';
31import 'sliver.dart';
32import 'sliver_prototype_extent_list.dart';
33import 'viewport.dart';
34
35// Examples can assume:
36// late int itemCount;
37
38/// A representation of how a [ScrollView] should dismiss the on-screen
39/// keyboard.
40enum ScrollViewKeyboardDismissBehavior {
41 /// `manual` means there is no automatic dismissal of the on-screen keyboard.
42 /// It is up to the client to dismiss the keyboard.
43 manual,
44
45 /// `onDrag` means that the [ScrollView] will dismiss an on-screen keyboard
46 /// when a drag begins.
47 onDrag,
48}
49
50/// A widget that combines a [Scrollable] and a [Viewport] to create an
51/// interactive scrolling pane of content in one dimension.
52///
53/// Scrollable widgets consist of three pieces:
54///
55/// 1. A [Scrollable] widget, which listens for various user gestures and
56/// implements the interaction design for scrolling.
57/// 2. A viewport widget, such as [Viewport] or [ShrinkWrappingViewport], which
58/// implements the visual design for scrolling by displaying only a portion
59/// of the widgets inside the scroll view.
60/// 3. One or more slivers, which are widgets that can be composed to created
61/// various scrolling effects, such as lists, grids, and expanding headers.
62///
63/// [ScrollView] helps orchestrate these pieces by creating the [Scrollable] and
64/// the viewport and deferring to its subclass to create the slivers.
65///
66/// To learn more about slivers, see [CustomScrollView.slivers].
67///
68/// To control the initial scroll offset of the scroll view, provide a
69/// [controller] with its [ScrollController.initialScrollOffset] property set.
70///
71/// {@template flutter.widgets.ScrollView.PageStorage}
72/// ## Persisting the scroll position during a session
73///
74/// Scroll views attempt to persist their scroll position using [PageStorage].
75/// This can be disabled by setting [ScrollController.keepScrollOffset] to false
76/// on the [controller]. If it is enabled, using a [PageStorageKey] for the
77/// [key] of this widget is recommended to help disambiguate different scroll
78/// views from each other.
79/// {@endtemplate}
80///
81/// See also:
82///
83/// * [ListView], which is a commonly used [ScrollView] that displays a
84/// scrolling, linear list of child widgets.
85/// * [PageView], which is a scrolling list of child widgets that are each the
86/// size of the viewport.
87/// * [GridView], which is a [ScrollView] that displays a scrolling, 2D array
88/// of child widgets.
89/// * [CustomScrollView], which is a [ScrollView] that creates custom scroll
90/// effects using slivers.
91/// * [ScrollNotification] and [NotificationListener], which can be used to watch
92/// the scroll position without using a [ScrollController].
93/// * [TwoDimensionalScrollView], which is a similar widget [ScrollView] that
94/// scrolls in two dimensions.
95abstract class ScrollView extends StatelessWidget {
96 /// Creates a widget that scrolls.
97 ///
98 /// The [ScrollView.primary] argument defaults to true for vertical
99 /// scroll views if no [controller] has been provided. The [controller] argument
100 /// must be null if [primary] is explicitly set to true. If [primary] is true,
101 /// the nearest [PrimaryScrollController] surrounding the widget is attached
102 /// to this scroll view.
103 ///
104 /// If the [shrinkWrap] argument is true, the [center] argument must be null.
105 ///
106 /// The [anchor] argument must be in the range zero to one, inclusive.
107 const ScrollView({
108 super.key,
109 this.scrollDirection = Axis.vertical,
110 this.reverse = false,
111 this.controller,
112 this.primary,
113 ScrollPhysics? physics,
114 this.scrollBehavior,
115 this.shrinkWrap = false,
116 this.center,
117 this.anchor = 0.0,
118 this.cacheExtent,
119 this.semanticChildCount,
120 this.dragStartBehavior = DragStartBehavior.start,
121 this.keyboardDismissBehavior,
122 this.restorationId,
123 this.clipBehavior = Clip.hardEdge,
124 this.hitTestBehavior = HitTestBehavior.opaque,
125 }) : assert(
126 !(controller != null && (primary ?? false)),
127 'Primary ScrollViews obtain their ScrollController via inheritance '
128 'from a PrimaryScrollController widget. You cannot both set primary to '
129 'true and pass an explicit controller.',
130 ),
131 assert(!shrinkWrap || center == null),
132 assert(anchor >= 0.0 && anchor <= 1.0),
133 assert(semanticChildCount == null || semanticChildCount >= 0),
134 physics =
135 physics ??
136 ((primary ?? false) ||
137 (primary == null &&
138 controller == null &&
139 identical(scrollDirection, Axis.vertical))
140 ? const AlwaysScrollableScrollPhysics()
141 : null);
142
143 /// {@template flutter.widgets.scroll_view.scrollDirection}
144 /// The [Axis] along which the scroll view's offset increases.
145 ///
146 /// For the direction in which active scrolling may be occurring, see
147 /// [ScrollDirection].
148 ///
149 /// Defaults to [Axis.vertical].
150 /// {@endtemplate}
151 final Axis scrollDirection;
152
153 /// {@template flutter.widgets.scroll_view.reverse}
154 /// Whether the scroll view scrolls in the reading direction.
155 ///
156 /// For example, if the reading direction is left-to-right and
157 /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from
158 /// left to right when [reverse] is false and from right to left when
159 /// [reverse] is true.
160 ///
161 /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view
162 /// scrolls from top to bottom when [reverse] is false and from bottom to top
163 /// when [reverse] is true.
164 ///
165 /// Defaults to false.
166 /// {@endtemplate}
167 final bool reverse;
168
169 /// {@template flutter.widgets.scroll_view.controller}
170 /// An object that can be used to control the position to which this scroll
171 /// view is scrolled.
172 ///
173 /// Must be null if [primary] is true.
174 ///
175 /// A [ScrollController] serves several purposes. It can be used to control
176 /// the initial scroll position (see [ScrollController.initialScrollOffset]).
177 /// It can be used to control whether the scroll view should automatically
178 /// save and restore its scroll position in the [PageStorage] (see
179 /// [ScrollController.keepScrollOffset]). It can be used to read the current
180 /// scroll position (see [ScrollController.offset]), or change it (see
181 /// [ScrollController.animateTo]).
182 /// {@endtemplate}
183 final ScrollController? controller;
184
185 /// {@template flutter.widgets.scroll_view.primary}
186 /// Whether this is the primary scroll view associated with the parent
187 /// [PrimaryScrollController].
188 ///
189 /// When this is true, the scroll view is scrollable even if it does not have
190 /// sufficient content to actually scroll. Otherwise, by default the user can
191 /// only scroll the view if it has sufficient content. See [physics].
192 ///
193 /// Also when true, the scroll view is used for default [ScrollAction]s. If a
194 /// ScrollAction is not handled by an otherwise focused part of the application,
195 /// the ScrollAction will be evaluated using this scroll view, for example,
196 /// when executing [Shortcuts] key events like page up and down.
197 ///
198 /// On iOS, this also identifies the scroll view that will scroll to top in
199 /// response to a tap in the status bar.
200 ///
201 /// Cannot be true while a [ScrollController] is provided to `controller`,
202 /// only one ScrollController can be associated with a ScrollView.
203 ///
204 /// Setting to false will explicitly prevent inheriting any
205 /// [PrimaryScrollController].
206 ///
207 /// Defaults to null. When null, and a controller is not provided,
208 /// [PrimaryScrollController.shouldInherit] is used to decide automatic
209 /// inheritance.
210 ///
211 /// By default, the [PrimaryScrollController] that is injected by each
212 /// [ModalRoute] is configured to automatically be inherited on
213 /// [TargetPlatformVariant.mobile] for ScrollViews in the [Axis.vertical]
214 /// scroll direction. Adding another to your app will override the
215 /// PrimaryScrollController above it.
216 ///
217 /// The following video contains more information about scroll controllers,
218 /// the PrimaryScrollController widget, and their impact on your apps:
219 ///
220 /// {@youtube 560 315 https://www.youtube.com/watch?v=33_0ABjFJUU}
221 ///
222 /// {@endtemplate}
223 final bool? primary;
224
225 /// {@template flutter.widgets.scroll_view.physics}
226 /// How the scroll view should respond to user input.
227 ///
228 /// For example, determines how the scroll view continues to animate after the
229 /// user stops dragging the scroll view.
230 ///
231 /// Defaults to matching platform conventions. Furthermore, if [primary] is
232 /// false, then the user cannot scroll if there is insufficient content to
233 /// scroll, while if [primary] is true, they can always attempt to scroll.
234 ///
235 /// To force the scroll view to always be scrollable even if there is
236 /// insufficient content, as if [primary] was true but without necessarily
237 /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics
238 /// object, as in:
239 ///
240 /// ```dart
241 /// physics: const AlwaysScrollableScrollPhysics(),
242 /// ```
243 ///
244 /// To force the scroll view to use the default platform conventions and not
245 /// be scrollable if there is insufficient content, regardless of the value of
246 /// [primary], provide an explicit [ScrollPhysics] object, as in:
247 ///
248 /// ```dart
249 /// physics: const ScrollPhysics(),
250 /// ```
251 ///
252 /// The physics can be changed dynamically (by providing a new object in a
253 /// subsequent build), but new physics will only take effect if the _class_ of
254 /// the provided object changes. Merely constructing a new instance with a
255 /// different configuration is insufficient to cause the physics to be
256 /// reapplied. (This is because the final object used is generated
257 /// dynamically, which can be relatively expensive, and it would be
258 /// inefficient to speculatively create this object each frame to see if the
259 /// physics should be updated.)
260 /// {@endtemplate}
261 ///
262 /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the
263 /// [ScrollPhysics] provided by that behavior will take precedence after
264 /// [physics].
265 final ScrollPhysics? physics;
266
267 /// {@macro flutter.widgets.scrollable.scrollBehavior}
268 final ScrollBehavior? scrollBehavior;
269
270 /// {@template flutter.widgets.scroll_view.shrinkWrap}
271 /// Whether the extent of the scroll view in the [scrollDirection] should be
272 /// determined by the contents being viewed.
273 ///
274 /// If the scroll view does not shrink wrap, then the scroll view will expand
275 /// to the maximum allowed size in the [scrollDirection]. If the scroll view
276 /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must
277 /// be true.
278 ///
279 /// Shrink wrapping the content of the scroll view is significantly more
280 /// expensive than expanding to the maximum allowed size because the content
281 /// can expand and contract during scrolling, which means the size of the
282 /// scroll view needs to be recomputed whenever the scroll position changes.
283 ///
284 /// Defaults to false.
285 ///
286 /// {@youtube 560 315 https://www.youtube.com/watch?v=LUqDNnv_dh0}
287 /// {@endtemplate}
288 final bool shrinkWrap;
289
290 /// The first child in the [GrowthDirection.forward] growth direction.
291 ///
292 /// Children after [center] will be placed in the [AxisDirection] determined
293 /// by [scrollDirection] and [reverse] relative to the [center]. Children
294 /// before [center] will be placed in the opposite of the axis direction
295 /// relative to the [center]. This makes the [center] the inflection point of
296 /// the growth direction.
297 ///
298 /// The [center] must be the key of one of the slivers built by [buildSlivers].
299 ///
300 /// Of the built-in subclasses of [ScrollView], only [CustomScrollView]
301 /// supports [center]; for that class, the given key must be the key of one of
302 /// the slivers in the [CustomScrollView.slivers] list.
303 ///
304 /// Most scroll views by default are ordered [GrowthDirection.forward].
305 /// Changing the default values of [ScrollView.anchor],
306 /// [ScrollView.center], or both, can configure a scroll view for
307 /// [GrowthDirection.reverse].
308 ///
309 /// {@tool dartpad}
310 /// This sample shows a [CustomScrollView], with [Radio] buttons in the
311 /// [AppBar.bottom] that change the [AxisDirection] to illustrate different
312 /// configurations. The [CustomScrollView.anchor] and [CustomScrollView.center]
313 /// properties are also set to have the 0 scroll offset positioned in the middle
314 /// of the viewport, with [GrowthDirection.forward] and [GrowthDirection.reverse]
315 /// illustrated on either side. The sliver that shares the
316 /// [CustomScrollView.center] key is positioned at the [CustomScrollView.anchor].
317 ///
318 /// ** See code in examples/api/lib/rendering/growth_direction/growth_direction.0.dart **
319 /// {@end-tool}
320 ///
321 /// See also:
322 ///
323 /// * [anchor], which controls where the [center] as aligned in the viewport.
324 final Key? center;
325
326 /// {@template flutter.widgets.scroll_view.anchor}
327 /// The relative position of the zero scroll offset.
328 ///
329 /// For example, if [anchor] is 0.5 and the [AxisDirection] determined by
330 /// [scrollDirection] and [reverse] is [AxisDirection.down] or
331 /// [AxisDirection.up], then the zero scroll offset is vertically centered
332 /// within the viewport. If the [anchor] is 1.0, and the axis direction is
333 /// [AxisDirection.right], then the zero scroll offset is on the left edge of
334 /// the viewport.
335 ///
336 /// Most scroll views by default are ordered [GrowthDirection.forward].
337 /// Changing the default values of [ScrollView.anchor],
338 /// [ScrollView.center], or both, can configure a scroll view for
339 /// [GrowthDirection.reverse].
340 ///
341 /// {@tool dartpad}
342 /// This sample shows a [CustomScrollView], with [Radio] buttons in the
343 /// [AppBar.bottom] that change the [AxisDirection] to illustrate different
344 /// configurations. The [CustomScrollView.anchor] and [CustomScrollView.center]
345 /// properties are also set to have the 0 scroll offset positioned in the middle
346 /// of the viewport, with [GrowthDirection.forward] and [GrowthDirection.reverse]
347 /// illustrated on either side. The sliver that shares the
348 /// [CustomScrollView.center] key is positioned at the [CustomScrollView.anchor].
349 ///
350 /// ** See code in examples/api/lib/rendering/growth_direction/growth_direction.0.dart **
351 /// {@end-tool}
352 /// {@endtemplate}
353 final double anchor;
354
355 /// {@macro flutter.rendering.RenderViewportBase.cacheExtent}
356 final double? cacheExtent;
357
358 /// The number of children that will contribute semantic information.
359 ///
360 /// Some subtypes of [ScrollView] can infer this value automatically. For
361 /// example [ListView] will use the number of widgets in the child list,
362 /// while the [ListView.separated] constructor will use half that amount.
363 ///
364 /// For [CustomScrollView] and other types which do not receive a builder
365 /// or list of widgets, the child count must be explicitly provided. If the
366 /// number is unknown or unbounded this should be left unset or set to null.
367 ///
368 /// See also:
369 ///
370 /// * [SemanticsConfiguration.scrollChildCount], the corresponding semantics property.
371 final int? semanticChildCount;
372
373 /// {@macro flutter.widgets.scrollable.dragStartBehavior}
374 final DragStartBehavior dragStartBehavior;
375
376 /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior}
377 /// The [ScrollViewKeyboardDismissBehavior] defines how this [ScrollView] will
378 /// dismiss the keyboard automatically.
379 /// {@endtemplate}
380 ///
381 /// If [keyboardDismissBehavior] is null then it will fallback to
382 /// [scrollBehavior]. If that is also null, the inherited
383 /// [ScrollBehavior.getKeyboardDismissBehavior] will be used.
384 final ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
385
386 /// {@macro flutter.widgets.scrollable.restorationId}
387 final String? restorationId;
388
389 /// {@macro flutter.material.Material.clipBehavior}
390 ///
391 /// Defaults to [Clip.hardEdge].
392 final Clip clipBehavior;
393
394 /// {@macro flutter.widgets.scrollable.hitTestBehavior}
395 ///
396 /// Defaults to [HitTestBehavior.opaque].
397 final HitTestBehavior hitTestBehavior;
398
399 /// Returns the [AxisDirection] in which the scroll view scrolls.
400 ///
401 /// Combines the [scrollDirection] with the [reverse] boolean to obtain the
402 /// concrete [AxisDirection].
403 ///
404 /// If the [scrollDirection] is [Axis.horizontal], the ambient
405 /// [Directionality] is also considered when selecting the concrete
406 /// [AxisDirection]. For example, if the ambient [Directionality] is
407 /// [TextDirection.rtl], then the non-reversed [AxisDirection] is
408 /// [AxisDirection.left] and the reversed [AxisDirection] is
409 /// [AxisDirection.right].
410 @protected
411 AxisDirection getDirection(BuildContext context) {
412 return getAxisDirectionFromAxisReverseAndDirectionality(context, scrollDirection, reverse);
413 }
414
415 /// Build the list of widgets to place inside the viewport.
416 ///
417 /// Subclasses should override this method to build the slivers for the inside
418 /// of the viewport.
419 ///
420 /// To learn more about slivers, see [CustomScrollView.slivers].
421 @protected
422 List<Widget> buildSlivers(BuildContext context);
423
424 /// Build the viewport.
425 ///
426 /// Subclasses may override this method to change how the viewport is built.
427 /// The default implementation uses a [ShrinkWrappingViewport] if [shrinkWrap]
428 /// is true, and a regular [Viewport] otherwise.
429 ///
430 /// The `offset` argument is the value obtained from
431 /// [Scrollable.viewportBuilder].
432 ///
433 /// The `axisDirection` argument is the value obtained from [getDirection],
434 /// which by default uses [scrollDirection] and [reverse].
435 ///
436 /// The `slivers` argument is the value obtained from [buildSlivers].
437 @protected
438 Widget buildViewport(
439 BuildContext context,
440 ViewportOffset offset,
441 AxisDirection axisDirection,
442 List<Widget> slivers,
443 ) {
444 assert(() {
445 switch (axisDirection) {
446 case AxisDirection.up:
447 case AxisDirection.down:
448 return debugCheckHasDirectionality(
449 context,
450 why: 'to determine the cross-axis direction of the scroll view',
451 hint:
452 'Vertical scroll views create Viewport widgets that try to determine their cross axis direction '
453 'from the ambient Directionality.',
454 );
455 case AxisDirection.left:
456 case AxisDirection.right:
457 return true;
458 }
459 }());
460 if (shrinkWrap) {
461 return ShrinkWrappingViewport(
462 axisDirection: axisDirection,
463 offset: offset,
464 slivers: slivers,
465 clipBehavior: clipBehavior,
466 );
467 }
468 return Viewport(
469 axisDirection: axisDirection,
470 offset: offset,
471 slivers: slivers,
472 cacheExtent: cacheExtent,
473 center: center,
474 anchor: anchor,
475 clipBehavior: clipBehavior,
476 );
477 }
478
479 @override
480 Widget build(BuildContext context) {
481 final List<Widget> slivers = buildSlivers(context);
482 final AxisDirection axisDirection = getDirection(context);
483
484 final bool effectivePrimary =
485 primary ??
486 controller == null && PrimaryScrollController.shouldInherit(context, scrollDirection);
487
488 final ScrollController? scrollController =
489 effectivePrimary ? PrimaryScrollController.maybeOf(context) : controller;
490
491 final Scrollable scrollable = Scrollable(
492 dragStartBehavior: dragStartBehavior,
493 axisDirection: axisDirection,
494 controller: scrollController,
495 physics: physics,
496 scrollBehavior: scrollBehavior,
497 semanticChildCount: semanticChildCount,
498 restorationId: restorationId,
499 hitTestBehavior: hitTestBehavior,
500 viewportBuilder: (BuildContext context, ViewportOffset offset) {
501 return buildViewport(context, offset, axisDirection, slivers);
502 },
503 clipBehavior: clipBehavior,
504 );
505
506 final Widget scrollableResult =
507 effectivePrimary && scrollController != null
508 // Further descendant ScrollViews will not inherit the same PrimaryScrollController
509 ? PrimaryScrollController.none(child: scrollable)
510 : scrollable;
511
512 final ScrollViewKeyboardDismissBehavior effectiveKeyboardDismissBehavior =
513 keyboardDismissBehavior ??
514 scrollBehavior?.getKeyboardDismissBehavior(context) ??
515 ScrollConfiguration.of(context).getKeyboardDismissBehavior(context);
516
517 if (effectiveKeyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) {
518 return NotificationListener<ScrollUpdateNotification>(
519 child: scrollableResult,
520 onNotification: (ScrollUpdateNotification notification) {
521 final FocusScopeNode currentScope = FocusScope.of(context);
522 if (notification.dragDetails != null &&
523 !currentScope.hasPrimaryFocus &&
524 currentScope.hasFocus) {
525 FocusManager.instance.primaryFocus?.unfocus();
526 }
527 return false;
528 },
529 );
530 } else {
531 return scrollableResult;
532 }
533 }
534
535 @override
536 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
537 super.debugFillProperties(properties);
538 properties.add(EnumProperty<Axis>('scrollDirection', scrollDirection));
539 properties.add(FlagProperty('reverse', value: reverse, ifTrue: 'reversed', showName: true));
540 properties.add(
541 DiagnosticsProperty<ScrollController>(
542 'controller',
543 controller,
544 showName: false,
545 defaultValue: null,
546 ),
547 );
548 properties.add(
549 FlagProperty('primary', value: primary, ifTrue: 'using primary controller', showName: true),
550 );
551 properties.add(
552 DiagnosticsProperty<ScrollPhysics>('physics', physics, showName: false, defaultValue: null),
553 );
554 properties.add(
555 FlagProperty('shrinkWrap', value: shrinkWrap, ifTrue: 'shrink-wrapping', showName: true),
556 );
557 }
558}
559
560/// A [ScrollView] that creates custom scroll effects using [slivers].
561///
562/// A [CustomScrollView] lets you supply [slivers] directly to create various
563/// scrolling effects, such as lists, grids, and expanding headers. For example,
564/// to create a scroll view that contains an expanding app bar followed by a
565/// list and a grid, use a list of three slivers: [SliverAppBar], [SliverList],
566/// and [SliverGrid].
567///
568/// [Widget]s in these [slivers] must produce [RenderSliver] objects.
569///
570/// To control the initial scroll offset of the scroll view, provide a
571/// [controller] with its [ScrollController.initialScrollOffset] property set.
572///
573/// {@animation 400 376 https://flutter.github.io/assets-for-api-docs/assets/widgets/custom_scroll_view.mp4}
574///
575/// {@tool snippet}
576///
577/// This sample code shows a scroll view that contains a flexible pinned app
578/// bar, a grid, and an infinite list.
579///
580/// ```dart
581/// CustomScrollView(
582/// slivers: <Widget>[
583/// const SliverAppBar(
584/// pinned: true,
585/// expandedHeight: 250.0,
586/// flexibleSpace: FlexibleSpaceBar(
587/// title: Text('Demo'),
588/// ),
589/// ),
590/// SliverGrid(
591/// gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
592/// maxCrossAxisExtent: 200.0,
593/// mainAxisSpacing: 10.0,
594/// crossAxisSpacing: 10.0,
595/// childAspectRatio: 4.0,
596/// ),
597/// delegate: SliverChildBuilderDelegate(
598/// (BuildContext context, int index) {
599/// return Container(
600/// alignment: Alignment.center,
601/// color: Colors.teal[100 * (index % 9)],
602/// child: Text('Grid Item $index'),
603/// );
604/// },
605/// childCount: 20,
606/// ),
607/// ),
608/// SliverFixedExtentList(
609/// itemExtent: 50.0,
610/// delegate: SliverChildBuilderDelegate(
611/// (BuildContext context, int index) {
612/// return Container(
613/// alignment: Alignment.center,
614/// color: Colors.lightBlue[100 * (index % 9)],
615/// child: Text('List Item $index'),
616/// );
617/// },
618/// ),
619/// ),
620/// ],
621/// )
622/// ```
623/// {@end-tool}
624///
625/// {@tool dartpad}
626/// By default, if items are inserted at the "top" of a scrolling container like
627/// [ListView] or [CustomScrollView], the top item and all of the items below it
628/// are scrolled downwards. In some applications, it's preferable to have the
629/// top of the list just grow upwards, without changing the scroll position.
630/// This example demonstrates how to do that with a [CustomScrollView] with
631/// two [SliverList] children, and the [CustomScrollView.center] set to the key
632/// of the bottom SliverList. The top one SliverList will grow upwards, and the
633/// bottom SliverList will grow downwards.
634///
635/// ** See code in examples/api/lib/widgets/scroll_view/custom_scroll_view.1.dart **
636/// {@end-tool}
637///
638/// ## Accessibility
639///
640/// A [CustomScrollView] can allow Talkback/VoiceOver to make announcements
641/// to the user when the scroll state changes. For example, on Android an
642/// announcement might be read as "showing items 1 to 10 of 23". To produce
643/// this announcement, the scroll view needs three pieces of information:
644///
645/// * The first visible child index.
646/// * The total number of children.
647/// * The total number of visible children.
648///
649/// The last value can be computed exactly by the framework, however the first
650/// two must be provided. Most of the higher-level scrollable widgets provide
651/// this information automatically. For example, [ListView] provides each child
652/// widget with a semantic index automatically and sets the semantic child
653/// count to the length of the list.
654///
655/// To determine visible indexes, the scroll view needs a way to associate the
656/// generated semantics of each scrollable item with a semantic index. This can
657/// be done by wrapping the child widgets in an [IndexedSemantics].
658///
659/// This semantic index is not necessarily the same as the index of the widget in
660/// the scrollable, because some widgets may not contribute semantic
661/// information. Consider a [ListView.separated]: every other widget is a
662/// divider with no semantic information. In this case, only odd numbered
663/// widgets have a semantic index (equal to the index ~/ 2). Furthermore, the
664/// total number of children in this example would be half the number of
665/// widgets. (The [ListView.separated] constructor handles this
666/// automatically; this is only used here as an example.)
667///
668/// The total number of visible children can be provided by the constructor
669/// parameter `semanticChildCount`. This should always be the same as the
670/// number of widgets wrapped in [IndexedSemantics].
671///
672/// {@macro flutter.widgets.ScrollView.PageStorage}
673///
674/// See also:
675///
676/// * [SliverList], which is a sliver that displays linear list of children.
677/// * [SliverFixedExtentList], which is a more efficient sliver that displays
678/// linear list of children that have the same extent along the scroll axis.
679/// * [SliverGrid], which is a sliver that displays a 2D array of children.
680/// * [SliverPadding], which is a sliver that adds blank space around another
681/// sliver.
682/// * [SliverAppBar], which is a sliver that displays a header that can expand
683/// and float as the scroll view scrolls.
684/// * [ScrollNotification] and [NotificationListener], which can be used to watch
685/// the scroll position without using a [ScrollController].
686/// * [IndexedSemantics], which allows annotating child lists with an index
687/// for scroll announcements.
688class CustomScrollView extends ScrollView {
689 /// Creates a [ScrollView] that creates custom scroll effects using slivers.
690 ///
691 /// See the [ScrollView] constructor for more details on these arguments.
692 const CustomScrollView({
693 super.key,
694 super.scrollDirection,
695 super.reverse,
696 super.controller,
697 super.primary,
698 super.physics,
699 super.scrollBehavior,
700 super.shrinkWrap,
701 super.center,
702 super.anchor,
703 super.cacheExtent,
704 this.slivers = const <Widget>[],
705 super.semanticChildCount,
706 super.dragStartBehavior,
707 super.keyboardDismissBehavior,
708 super.restorationId,
709 super.clipBehavior,
710 super.hitTestBehavior,
711 });
712
713 /// The slivers to place inside the viewport.
714 ///
715 /// ## What is a sliver?
716 ///
717 /// > _**sliver** (noun): a small, thin piece of something._
718 ///
719 /// A _sliver_ is a widget backed by a [RenderSliver] subclass, i.e. one that
720 /// implements the constraint/geometry protocol that uses [SliverConstraints]
721 /// and [SliverGeometry].
722 ///
723 /// This is as distinct from those widgets that are backed by [RenderBox]
724 /// subclasses, which use [BoxConstraints] and [Size] respectively, and are
725 /// known as box widgets. (Widgets like [Container], [Row], and [SizedBox] are
726 /// box widgets.)
727 ///
728 /// While boxes are much more straightforward (implementing a simple
729 /// two-dimensional Cartesian layout system), slivers are much more powerful,
730 /// and are optimized for one-axis scrolling environments.
731 ///
732 /// Slivers are hosted in viewports, also known as scroll views, most notably
733 /// [CustomScrollView].
734 ///
735 /// ## Examples of slivers
736 ///
737 /// The Flutter framework has many built-in sliver widgets, and custom widgets
738 /// can be created in the same manner. By convention, sliver widgets always
739 /// start with the prefix `Sliver` and are always used in properties called
740 /// `sliver` or `slivers` (as opposed to `child` and `children` which are used
741 /// for box widgets).
742 ///
743 /// Examples of widgets unique to the sliver world include:
744 ///
745 /// * [SliverList], a lazily-loading list of variably-sized box widgets.
746 /// * [SliverFixedExtentList], a lazily-loading list of box widgets that are
747 /// all forced to the same height.
748 /// * [SliverPrototypeExtentList], a lazily-loading list of box widgets that
749 /// are all forced to the same height as a given prototype widget.
750 /// * [SliverGrid], a lazily-loading grid of box widgets.
751 /// * [SliverAnimatedList] and [SliverAnimatedGrid], animated variants of
752 /// [SliverList] and [SliverGrid].
753 /// * [SliverFillRemaining], a widget that fills all remaining space in a
754 /// scroll view, and lays a box widget out inside that space.
755 /// * [SliverFillViewport], a widget that lays a list of boxes out, each
756 /// being sized to fit the whole viewport.
757 /// * [SliverPersistentHeader], a sliver that implements pinned and floating
758 /// headers, e.g. used to implement [SliverAppBar].
759 /// * [SliverToBoxAdapter], a sliver that wraps a box widget.
760 ///
761 /// Examples of sliver variants of common box widgets include:
762 ///
763 /// * [SliverOpacity], [SliverAnimatedOpacity], and [SliverFadeTransition],
764 /// sliver versions of [Opacity], [AnimatedOpacity], and [FadeTransition].
765 /// * [SliverIgnorePointer], a sliver version of [IgnorePointer].
766 /// * [SliverLayoutBuilder], a sliver version of [LayoutBuilder].
767 /// * [SliverOffstage], a sliver version of [Offstage].
768 /// * [SliverPadding], a sliver version of [Padding].
769 /// * [SliverReorderableList], a sliver version of [ReorderableList]
770 /// * [SliverSafeArea], a sliver version of [SafeArea].
771 /// * [SliverVisibility], a sliver version of [Visibility].
772 ///
773 /// ## Benefits of slivers over boxes
774 ///
775 /// The sliver protocol ([SliverConstraints] and [SliverGeometry]) enables
776 /// _scroll effects_, such as floating app bars, widgets that expand and
777 /// shrink during scroll, section headers that are pinned only while the
778 /// section's children are visible, etc.
779 ///
780 /// {@youtube 560 315 https://www.youtube.com/watch?v=Mz3kHQxBjGg}
781 ///
782 /// ## Mixing slivers and boxes
783 ///
784 /// In general, slivers always wrap box widgets to actually render anything
785 /// (for example, there is no sliver equivalent of [Text] or [Container]);
786 /// the sliver part of the equation is mostly about how these boxes should
787 /// be laid out in a viewport (i.e. when scrolling).
788 ///
789 /// Typically, the simplest way to combine boxes into a sliver environment is
790 /// to use a [SliverList] (maybe using a [ListView], which is a convenient
791 /// combination of a [CustomScrollView] and a [SliverList]). In rare cases,
792 /// e.g. if a single [Divider] widget is needed between two [SliverGrid]s,
793 /// a [SliverToBoxAdapter] can be used to wrap the box widgets.
794 ///
795 /// ## Performance considerations
796 ///
797 /// Because the purpose of scroll views is to, well, scroll, it is common
798 /// for scroll views to contain more contents than are rendered on the screen
799 /// at any particular time.
800 ///
801 /// To improve the performance of scroll views, the content can be rendered in
802 /// _lazy_ widgets, notably [SliverList] and [SliverGrid] (and their variants,
803 /// such as [SliverFixedExtentList] and [SliverAnimatedGrid]). These widgets
804 /// ensure that only the portion of their child lists that are actually
805 /// visible get built, laid out, and painted.
806 ///
807 /// The [ListView] and [GridView] widgets provide a convenient way to combine
808 /// a [CustomScrollView] and a [SliverList] or [SliverGrid] (respectively).
809 final List<Widget> slivers;
810
811 @override
812 List<Widget> buildSlivers(BuildContext context) => slivers;
813}
814
815/// A [ScrollView] that uses a single child layout model.
816///
817/// {@template flutter.widgets.BoxScroll.scrollBehaviour}
818/// [ScrollView]s are often decorated with [Scrollbar]s and overscroll indicators,
819/// which are managed by the inherited [ScrollBehavior]. Placing a
820/// [ScrollConfiguration] above a ScrollView can modify these behaviors for that
821/// ScrollView, or can be managed app-wide by providing a ScrollBehavior to
822/// [MaterialApp.scrollBehavior] or [CupertinoApp.scrollBehavior].
823/// {@endtemplate}
824///
825/// See also:
826///
827/// * [ListView], which is a [BoxScrollView] that uses a linear layout model.
828/// * [GridView], which is a [BoxScrollView] that uses a 2D layout model.
829/// * [CustomScrollView], which can combine multiple child layout models into a
830/// single scroll view.
831abstract class BoxScrollView extends ScrollView {
832 /// Creates a [ScrollView] uses a single child layout model.
833 ///
834 /// If the [primary] argument is true, the [controller] must be null.
835 const BoxScrollView({
836 super.key,
837 super.scrollDirection,
838 super.reverse,
839 super.controller,
840 super.primary,
841 super.physics,
842 super.shrinkWrap,
843 this.padding,
844 super.cacheExtent,
845 super.semanticChildCount,
846 super.dragStartBehavior,
847 super.keyboardDismissBehavior,
848 super.restorationId,
849 super.clipBehavior,
850 super.hitTestBehavior,
851 });
852
853 /// The amount of space by which to inset the children.
854 final EdgeInsetsGeometry? padding;
855
856 @override
857 List<Widget> buildSlivers(BuildContext context) {
858 Widget sliver = buildChildLayout(context);
859 EdgeInsetsGeometry? effectivePadding = padding;
860 if (padding == null) {
861 final MediaQueryData? mediaQuery = MediaQuery.maybeOf(context);
862 if (mediaQuery != null) {
863 // Automatically pad sliver with padding from MediaQuery.
864 final EdgeInsets mediaQueryHorizontalPadding = mediaQuery.padding.copyWith(
865 top: 0.0,
866 bottom: 0.0,
867 );
868 final EdgeInsets mediaQueryVerticalPadding = mediaQuery.padding.copyWith(
869 left: 0.0,
870 right: 0.0,
871 );
872 // Consume the main axis padding with SliverPadding.
873 effectivePadding =
874 scrollDirection == Axis.vertical
875 ? mediaQueryVerticalPadding
876 : mediaQueryHorizontalPadding;
877 // Leave behind the cross axis padding.
878 sliver = MediaQuery(
879 data: mediaQuery.copyWith(
880 padding:
881 scrollDirection == Axis.vertical
882 ? mediaQueryHorizontalPadding
883 : mediaQueryVerticalPadding,
884 ),
885 child: sliver,
886 );
887 }
888 }
889
890 if (effectivePadding != null) {
891 sliver = SliverPadding(padding: effectivePadding, sliver: sliver);
892 }
893 return <Widget>[sliver];
894 }
895
896 /// Subclasses should override this method to build the layout model.
897 @protected
898 Widget buildChildLayout(BuildContext context);
899
900 @override
901 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
902 super.debugFillProperties(properties);
903 properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
904 }
905}
906
907/// A scrollable list of widgets arranged linearly.
908///
909/// {@youtube 560 315 https://www.youtube.com/watch?v=KJpkjHGiI5A}
910///
911/// [ListView] is the most commonly used scrolling widget. It displays its
912/// children one after another in the scroll direction. In the cross axis, the
913/// children are required to fill the [ListView].
914///
915/// If non-null, the [itemExtent] forces the children to have the given extent
916/// in the scroll direction.
917///
918/// If non-null, the [prototypeItem] forces the children to have the same extent
919/// as the given widget in the scroll direction.
920///
921/// Specifying an [itemExtent] or an [prototypeItem] is more efficient than
922/// letting the children determine their own extent because the scrolling
923/// machinery can make use of the foreknowledge of the children's extent to save
924/// work, for example when the scroll position changes drastically.
925///
926/// You can't specify both [itemExtent] and [prototypeItem], only one or none of
927/// them.
928///
929/// There are four options for constructing a [ListView]:
930///
931/// 1. The default constructor takes an explicit [List<Widget>] of children. This
932/// constructor is appropriate for list views with a small number of
933/// children because constructing the [List] requires doing work for every
934/// child that could possibly be displayed in the list view instead of just
935/// those children that are actually visible.
936///
937/// 2. The [ListView.builder] constructor takes an [IndexedWidgetBuilder], which
938/// builds the children on demand. This constructor is appropriate for list views
939/// with a large (or infinite) number of children because the builder is called
940/// only for those children that are actually visible.
941///
942/// 3. The [ListView.separated] constructor takes two [IndexedWidgetBuilder]s:
943/// `itemBuilder` builds child items on demand, and `separatorBuilder`
944/// similarly builds separator children which appear in between the child items.
945/// This constructor is appropriate for list views with a fixed number of children.
946///
947/// 4. The [ListView.custom] constructor takes a [SliverChildDelegate], which provides
948/// the ability to customize additional aspects of the child model. For example,
949/// a [SliverChildDelegate] can control the algorithm used to estimate the
950/// size of children that are not actually visible.
951///
952/// To control the initial scroll offset of the scroll view, provide a
953/// [controller] with its [ScrollController.initialScrollOffset] property set.
954///
955/// By default, [ListView] will automatically pad the list's scrollable
956/// extremities to avoid partial obstructions indicated by [MediaQuery]'s
957/// padding. To avoid this behavior, override with a zero [padding] property.
958///
959/// {@tool snippet}
960/// This example uses the default constructor for [ListView] which takes an
961/// explicit [List<Widget>] of children. This [ListView]'s children are made up
962/// of [Container]s with [Text].
963///
964/// ![A ListView of 3 amber colored containers with sample text.](https://flutter.github.io/assets-for-api-docs/assets/widgets/list_view.png)
965///
966/// ```dart
967/// ListView(
968/// padding: const EdgeInsets.all(8),
969/// children: <Widget>[
970/// Container(
971/// height: 50,
972/// color: Colors.amber[600],
973/// child: const Center(child: Text('Entry A')),
974/// ),
975/// Container(
976/// height: 50,
977/// color: Colors.amber[500],
978/// child: const Center(child: Text('Entry B')),
979/// ),
980/// Container(
981/// height: 50,
982/// color: Colors.amber[100],
983/// child: const Center(child: Text('Entry C')),
984/// ),
985/// ],
986/// )
987/// ```
988/// {@end-tool}
989///
990/// {@tool snippet}
991/// This example mirrors the previous one, creating the same list using the
992/// [ListView.builder] constructor. Using the [IndexedWidgetBuilder], children
993/// are built lazily and can be infinite in number.
994///
995/// ![A ListView of 3 amber colored containers with sample text.](https://flutter.github.io/assets-for-api-docs/assets/widgets/list_view_builder.png)
996///
997/// ```dart
998/// final List<String> entries = <String>['A', 'B', 'C'];
999/// final List<int> colorCodes = <int>[600, 500, 100];
1000///
1001/// Widget build(BuildContext context) {
1002/// return ListView.builder(
1003/// padding: const EdgeInsets.all(8),
1004/// itemCount: entries.length,
1005/// itemBuilder: (BuildContext context, int index) {
1006/// return Container(
1007/// height: 50,
1008/// color: Colors.amber[colorCodes[index]],
1009/// child: Center(child: Text('Entry ${entries[index]}')),
1010/// );
1011/// }
1012/// );
1013/// }
1014/// ```
1015/// {@end-tool}
1016///
1017/// {@tool snippet}
1018/// This example continues to build from our the previous ones, creating a
1019/// similar list using [ListView.separated]. Here, a [Divider] is used as a
1020/// separator.
1021///
1022/// ![A ListView of 3 amber colored containers with sample text and a Divider
1023/// between each of them.](https://flutter.github.io/assets-for-api-docs/assets/widgets/list_view_separated.png)
1024///
1025/// ```dart
1026/// final List<String> entries = <String>['A', 'B', 'C'];
1027/// final List<int> colorCodes = <int>[600, 500, 100];
1028///
1029/// Widget build(BuildContext context) {
1030/// return ListView.separated(
1031/// padding: const EdgeInsets.all(8),
1032/// itemCount: entries.length,
1033/// itemBuilder: (BuildContext context, int index) {
1034/// return Container(
1035/// height: 50,
1036/// color: Colors.amber[colorCodes[index]],
1037/// child: Center(child: Text('Entry ${entries[index]}')),
1038/// );
1039/// },
1040/// separatorBuilder: (BuildContext context, int index) => const Divider(),
1041/// );
1042/// }
1043/// ```
1044/// {@end-tool}
1045///
1046/// ## Child elements' lifecycle
1047///
1048/// ### Creation
1049///
1050/// While laying out the list, visible children's elements, states and render
1051/// objects will be created lazily based on existing widgets (such as when using
1052/// the default constructor) or lazily provided ones (such as when using the
1053/// [ListView.builder] constructor).
1054///
1055/// ### Destruction
1056///
1057/// When a child is scrolled out of view, the associated element subtree,
1058/// states and render objects are destroyed. A new child at the same position
1059/// in the list will be lazily recreated along with new elements, states and
1060/// render objects when it is scrolled back.
1061///
1062/// ### Destruction mitigation
1063///
1064/// In order to preserve state as child elements are scrolled in and out of
1065/// view, the following options are possible:
1066///
1067/// * Moving the ownership of non-trivial UI-state-driving business logic
1068/// out of the list child subtree. For instance, if a list contains posts
1069/// with their number of upvotes coming from a cached network response, store
1070/// the list of posts and upvote number in a data model outside the list. Let
1071/// the list child UI subtree be easily recreate-able from the
1072/// source-of-truth model object. Use [StatefulWidget]s in the child
1073/// widget subtree to store instantaneous UI state only.
1074///
1075/// * Letting [KeepAlive] be the root widget of the list child widget subtree
1076/// that needs to be preserved. The [KeepAlive] widget marks the child
1077/// subtree's top render object child for keepalive. When the associated top
1078/// render object is scrolled out of view, the list keeps the child's render
1079/// object (and by extension, its associated elements and states) in a cache
1080/// list instead of destroying them. When scrolled back into view, the render
1081/// object is repainted as-is (if it wasn't marked dirty in the interim).
1082///
1083/// This only works if `addAutomaticKeepAlives` and `addRepaintBoundaries`
1084/// are false since those parameters cause the [ListView] to wrap each child
1085/// widget subtree with other widgets.
1086///
1087/// * Using [AutomaticKeepAlive] widgets (inserted by default when
1088/// `addAutomaticKeepAlives` is true). [AutomaticKeepAlive] allows descendant
1089/// widgets to control whether the subtree is actually kept alive or not.
1090/// This behavior is in contrast with [KeepAlive], which will unconditionally keep
1091/// the subtree alive.
1092///
1093/// As an example, the [EditableText] widget signals its list child element
1094/// subtree to stay alive while its text field has input focus. If it doesn't
1095/// have focus and no other descendants signaled for keepalive via a
1096/// [KeepAliveNotification], the list child element subtree will be destroyed
1097/// when scrolled away.
1098///
1099/// [AutomaticKeepAlive] descendants typically signal it to be kept alive
1100/// by using the [AutomaticKeepAliveClientMixin], then implementing the
1101/// [AutomaticKeepAliveClientMixin.wantKeepAlive] getter and calling
1102/// [AutomaticKeepAliveClientMixin.updateKeepAlive].
1103///
1104/// ## Transitioning to [CustomScrollView]
1105///
1106/// A [ListView] is basically a [CustomScrollView] with a single [SliverList] in
1107/// its [CustomScrollView.slivers] property.
1108///
1109/// If [ListView] is no longer sufficient, for example because the scroll view
1110/// is to have both a list and a grid, or because the list is to be combined
1111/// with a [SliverAppBar], etc, it is straight-forward to port code from using
1112/// [ListView] to using [CustomScrollView] directly.
1113///
1114/// The [key], [scrollDirection], [reverse], [controller], [primary], [physics],
1115/// and [shrinkWrap] properties on [ListView] map directly to the identically
1116/// named properties on [CustomScrollView].
1117///
1118/// The [CustomScrollView.slivers] property should be a list containing either:
1119/// * a [SliverList] if both [itemExtent] and [prototypeItem] were null;
1120/// * a [SliverFixedExtentList] if [itemExtent] was not null; or
1121/// * a [SliverPrototypeExtentList] if [prototypeItem] was not null.
1122///
1123/// The [childrenDelegate] property on [ListView] corresponds to the
1124/// [SliverList.delegate] (or [SliverFixedExtentList.delegate]) property. The
1125/// [ListView] constructor's `children` argument corresponds to the
1126/// [childrenDelegate] being a [SliverChildListDelegate] with that same
1127/// argument. The [ListView.builder] constructor's `itemBuilder` and
1128/// `itemCount` arguments correspond to the [childrenDelegate] being a
1129/// [SliverChildBuilderDelegate] with the equivalent arguments.
1130///
1131/// The [padding] property corresponds to having a [SliverPadding] in the
1132/// [CustomScrollView.slivers] property instead of the list itself, and having
1133/// the [SliverList] instead be a child of the [SliverPadding].
1134///
1135/// [CustomScrollView]s don't automatically avoid obstructions from [MediaQuery]
1136/// like [ListView]s do. To reproduce the behavior, wrap the slivers in
1137/// [SliverSafeArea]s.
1138///
1139/// Once code has been ported to use [CustomScrollView], other slivers, such as
1140/// [SliverGrid] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
1141/// list.
1142///
1143/// {@tool snippet}
1144///
1145/// Here are two brief snippets showing a [ListView] and its equivalent using
1146/// [CustomScrollView]:
1147///
1148/// ```dart
1149/// ListView(
1150/// padding: const EdgeInsets.all(20.0),
1151/// children: const <Widget>[
1152/// Text("I'm dedicating every day to you"),
1153/// Text('Domestic life was never quite my style'),
1154/// Text('When you smile, you knock me out, I fall apart'),
1155/// Text('And I thought I was so smart'),
1156/// ],
1157/// )
1158/// ```
1159/// {@end-tool}
1160/// {@tool snippet}
1161///
1162/// ```dart
1163/// CustomScrollView(
1164/// slivers: <Widget>[
1165/// SliverPadding(
1166/// padding: const EdgeInsets.all(20.0),
1167/// sliver: SliverList(
1168/// delegate: SliverChildListDelegate(
1169/// <Widget>[
1170/// const Text("I'm dedicating every day to you"),
1171/// const Text('Domestic life was never quite my style'),
1172/// const Text('When you smile, you knock me out, I fall apart'),
1173/// const Text('And I thought I was so smart'),
1174/// ],
1175/// ),
1176/// ),
1177/// ),
1178/// ],
1179/// )
1180/// ```
1181/// {@end-tool}
1182///
1183/// ## Special handling for an empty list
1184///
1185/// A common design pattern is to have a custom UI for an empty list. The best
1186/// way to achieve this in Flutter is just conditionally replacing the
1187/// [ListView] at build time with whatever widgets you need to show for the
1188/// empty list state:
1189///
1190/// {@tool snippet}
1191///
1192/// Example of simple empty list interface:
1193///
1194/// ```dart
1195/// Widget build(BuildContext context) {
1196/// return Scaffold(
1197/// appBar: AppBar(title: const Text('Empty List Test')),
1198/// body: itemCount > 0
1199/// ? ListView.builder(
1200/// itemCount: itemCount,
1201/// itemBuilder: (BuildContext context, int index) {
1202/// return ListTile(
1203/// title: Text('Item ${index + 1}'),
1204/// );
1205/// },
1206/// )
1207/// : const Center(child: Text('No items')),
1208/// );
1209/// }
1210/// ```
1211/// {@end-tool}
1212///
1213/// ## Selection of list items
1214///
1215/// [ListView] has no built-in notion of a selected item or items. For a small
1216/// example of how a caller might wire up basic item selection, see
1217/// [ListTile.selected].
1218///
1219/// {@tool dartpad}
1220/// This example shows a custom implementation of [ListTile] selection in a [ListView] or [GridView].
1221/// Long press any [ListTile] to enable selection mode.
1222///
1223/// ** See code in examples/api/lib/widgets/scroll_view/list_view.0.dart **
1224/// {@end-tool}
1225///
1226/// {@macro flutter.widgets.BoxScroll.scrollBehaviour}
1227///
1228/// {@macro flutter.widgets.ScrollView.PageStorage}
1229///
1230/// See also:
1231///
1232/// * [SingleChildScrollView], which is a scrollable widget that has a single
1233/// child.
1234/// * [PageView], which is a scrolling list of child widgets that are each the
1235/// size of the viewport.
1236/// * [GridView], which is a scrollable, 2D array of widgets.
1237/// * [CustomScrollView], which is a scrollable widget that creates custom
1238/// scroll effects using slivers.
1239/// * [ListBody], which arranges its children in a similar manner, but without
1240/// scrolling.
1241/// * [ScrollNotification] and [NotificationListener], which can be used to watch
1242/// the scroll position without using a [ScrollController].
1243/// * The [catalog of layout widgets](https://docs.flutter.dev/ui/widgets/layout).
1244/// * Cookbook: [Use lists](https://docs.flutter.dev/cookbook/lists/basic-list)
1245/// * Cookbook: [Work with long lists](https://docs.flutter.dev/cookbook/lists/long-lists)
1246/// * Cookbook: [Create a horizontal list](https://docs.flutter.dev/cookbook/lists/horizontal-list)
1247/// * Cookbook: [Create lists with different types of items](https://docs.flutter.dev/cookbook/lists/mixed-list)
1248/// * Cookbook: [Implement swipe to dismiss](https://docs.flutter.dev/cookbook/gestures/dismissible)
1249class ListView extends BoxScrollView {
1250 /// Creates a scrollable, linear array of widgets from an explicit [List].
1251 ///
1252 /// This constructor is appropriate for list views with a small number of
1253 /// children because constructing the [List] requires doing work for every
1254 /// child that could possibly be displayed in the list view instead of just
1255 /// those children that are actually visible.
1256 ///
1257 /// Like other widgets in the framework, this widget expects that
1258 /// the [children] list will not be mutated after it has been passed in here.
1259 /// See the documentation at [SliverChildListDelegate.children] for more details.
1260 ///
1261 /// It is usually more efficient to create children on demand using
1262 /// [ListView.builder] because it will create the widget children lazily as necessary.
1263 ///
1264 /// The `addAutomaticKeepAlives` argument corresponds to the
1265 /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
1266 /// `addRepaintBoundaries` argument corresponds to the
1267 /// [SliverChildListDelegate.addRepaintBoundaries] property. The
1268 /// `addSemanticIndexes` argument corresponds to the
1269 /// [SliverChildListDelegate.addSemanticIndexes] property. None
1270 /// may be null.
1271 ListView({
1272 super.key,
1273 super.scrollDirection,
1274 super.reverse,
1275 super.controller,
1276 super.primary,
1277 super.physics,
1278 super.shrinkWrap,
1279 super.padding,
1280 this.itemExtent,
1281 this.itemExtentBuilder,
1282 this.prototypeItem,
1283 bool addAutomaticKeepAlives = true,
1284 bool addRepaintBoundaries = true,
1285 bool addSemanticIndexes = true,
1286 super.cacheExtent,
1287 List<Widget> children = const <Widget>[],
1288 int? semanticChildCount,
1289 super.dragStartBehavior,
1290 super.keyboardDismissBehavior,
1291 super.restorationId,
1292 super.clipBehavior,
1293 super.hitTestBehavior,
1294 }) : assert(
1295 (itemExtent == null && prototypeItem == null) ||
1296 (itemExtent == null && itemExtentBuilder == null) ||
1297 (prototypeItem == null && itemExtentBuilder == null),
1298 'You can only pass one of itemExtent, prototypeItem and itemExtentBuilder.',
1299 ),
1300 childrenDelegate = SliverChildListDelegate(
1301 children,
1302 addAutomaticKeepAlives: addAutomaticKeepAlives,
1303 addRepaintBoundaries: addRepaintBoundaries,
1304 addSemanticIndexes: addSemanticIndexes,
1305 ),
1306 super(semanticChildCount: semanticChildCount ?? children.length);
1307
1308 /// Creates a scrollable, linear array of widgets that are created on demand.
1309 ///
1310 /// This constructor is appropriate for list views with a large (or infinite)
1311 /// number of children because the builder is called only for those children
1312 /// that are actually visible.
1313 ///
1314 /// Providing a non-null `itemCount` improves the ability of the [ListView] to
1315 /// estimate the maximum scroll extent.
1316 ///
1317 /// The `itemBuilder` callback will be called only with indices greater than
1318 /// or equal to zero and less than `itemCount`.
1319 ///
1320 /// {@template flutter.widgets.ListView.builder.itemBuilder}
1321 /// It is legal for `itemBuilder` to return `null`. If it does, the scroll view
1322 /// will stop calling `itemBuilder`, even if it has yet to reach `itemCount`.
1323 /// By returning `null`, the [ScrollPosition.maxScrollExtent] will not be accurate
1324 /// unless the user has reached the end of the [ScrollView]. This can also cause the
1325 /// [Scrollbar] to grow as the user scrolls.
1326 ///
1327 /// For more accurate [ScrollMetrics], consider specifying `itemCount`.
1328 /// {@endtemplate}
1329 ///
1330 /// The `itemBuilder` should always create the widget instances when called.
1331 /// Avoid using a builder that returns a previously-constructed widget; if the
1332 /// list view's children are created in advance, or all at once when the
1333 /// [ListView] itself is created, it is more efficient to use the [ListView]
1334 /// constructor. Even more efficient, however, is to create the instances on
1335 /// demand using this constructor's `itemBuilder` callback.
1336 ///
1337 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
1338 ///
1339 /// The `addAutomaticKeepAlives` argument corresponds to the
1340 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
1341 /// `addRepaintBoundaries` argument corresponds to the
1342 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
1343 /// `addSemanticIndexes` argument corresponds to the
1344 /// [SliverChildBuilderDelegate.addSemanticIndexes] property. None may be
1345 /// null.
1346 ListView.builder({
1347 super.key,
1348 super.scrollDirection,
1349 super.reverse,
1350 super.controller,
1351 super.primary,
1352 super.physics,
1353 super.shrinkWrap,
1354 super.padding,
1355 this.itemExtent,
1356 this.itemExtentBuilder,
1357 this.prototypeItem,
1358 required NullableIndexedWidgetBuilder itemBuilder,
1359 ChildIndexGetter? findChildIndexCallback,
1360 int? itemCount,
1361 bool addAutomaticKeepAlives = true,
1362 bool addRepaintBoundaries = true,
1363 bool addSemanticIndexes = true,
1364 super.cacheExtent,
1365 int? semanticChildCount,
1366 super.dragStartBehavior,
1367 super.keyboardDismissBehavior,
1368 super.restorationId,
1369 super.clipBehavior,
1370 super.hitTestBehavior,
1371 }) : assert(itemCount == null || itemCount >= 0),
1372 assert(semanticChildCount == null || semanticChildCount <= itemCount!),
1373 assert(
1374 (itemExtent == null && prototypeItem == null) ||
1375 (itemExtent == null && itemExtentBuilder == null) ||
1376 (prototypeItem == null && itemExtentBuilder == null),
1377 'You can only pass one of itemExtent, prototypeItem and itemExtentBuilder.',
1378 ),
1379 childrenDelegate = SliverChildBuilderDelegate(
1380 itemBuilder,
1381 findChildIndexCallback: findChildIndexCallback,
1382 childCount: itemCount,
1383 addAutomaticKeepAlives: addAutomaticKeepAlives,
1384 addRepaintBoundaries: addRepaintBoundaries,
1385 addSemanticIndexes: addSemanticIndexes,
1386 ),
1387 super(semanticChildCount: semanticChildCount ?? itemCount);
1388
1389 /// Creates a fixed-length scrollable linear array of list "items" separated
1390 /// by list item "separators".
1391 ///
1392 /// This constructor is appropriate for list views with a large number of
1393 /// item and separator children because the builders are only called for
1394 /// the children that are actually visible.
1395 ///
1396 /// The `itemBuilder` callback will be called with indices greater than
1397 /// or equal to zero and less than `itemCount`.
1398 ///
1399 /// Separators only appear between list items: separator 0 appears after item
1400 /// 0 and the last separator appears before the last item.
1401 ///
1402 /// The `separatorBuilder` callback will be called with indices greater than
1403 /// or equal to zero and less than `itemCount - 1`.
1404 ///
1405 /// The `itemBuilder` and `separatorBuilder` callbacks should always
1406 /// actually create widget instances when called. Avoid using a builder that
1407 /// returns a previously-constructed widget; if the list view's children are
1408 /// created in advance, or all at once when the [ListView] itself is created,
1409 /// it is more efficient to use the [ListView] constructor.
1410 ///
1411 /// {@macro flutter.widgets.ListView.builder.itemBuilder}
1412 ///
1413 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
1414 ///
1415 /// {@tool snippet}
1416 ///
1417 /// This example shows how to create [ListView] whose [ListTile] list items
1418 /// are separated by [Divider]s.
1419 ///
1420 /// ```dart
1421 /// ListView.separated(
1422 /// itemCount: 25,
1423 /// separatorBuilder: (BuildContext context, int index) => const Divider(),
1424 /// itemBuilder: (BuildContext context, int index) {
1425 /// return ListTile(
1426 /// title: Text('item $index'),
1427 /// );
1428 /// },
1429 /// )
1430 /// ```
1431 /// {@end-tool}
1432 ///
1433 /// The `addAutomaticKeepAlives` argument corresponds to the
1434 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
1435 /// `addRepaintBoundaries` argument corresponds to the
1436 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
1437 /// `addSemanticIndexes` argument corresponds to the
1438 /// [SliverChildBuilderDelegate.addSemanticIndexes] property. None may be
1439 /// null.
1440 ListView.separated({
1441 super.key,
1442 super.scrollDirection,
1443 super.reverse,
1444 super.controller,
1445 super.primary,
1446 super.physics,
1447 super.shrinkWrap,
1448 super.padding,
1449 required NullableIndexedWidgetBuilder itemBuilder,
1450 ChildIndexGetter? findChildIndexCallback,
1451 required IndexedWidgetBuilder separatorBuilder,
1452 required int itemCount,
1453 bool addAutomaticKeepAlives = true,
1454 bool addRepaintBoundaries = true,
1455 bool addSemanticIndexes = true,
1456 super.cacheExtent,
1457 super.dragStartBehavior,
1458 super.keyboardDismissBehavior,
1459 super.restorationId,
1460 super.clipBehavior,
1461 super.hitTestBehavior,
1462 }) : assert(itemCount >= 0),
1463 itemExtent = null,
1464 itemExtentBuilder = null,
1465 prototypeItem = null,
1466 childrenDelegate = SliverChildBuilderDelegate(
1467 (BuildContext context, int index) {
1468 final int itemIndex = index ~/ 2;
1469 if (index.isEven) {
1470 return itemBuilder(context, itemIndex);
1471 }
1472 return separatorBuilder(context, itemIndex);
1473 },
1474 findChildIndexCallback: findChildIndexCallback,
1475 childCount: _computeActualChildCount(itemCount),
1476 addAutomaticKeepAlives: addAutomaticKeepAlives,
1477 addRepaintBoundaries: addRepaintBoundaries,
1478 addSemanticIndexes: addSemanticIndexes,
1479 semanticIndexCallback: (Widget widget, int index) {
1480 return index.isEven ? index ~/ 2 : null;
1481 },
1482 ),
1483 super(semanticChildCount: itemCount);
1484
1485 /// Creates a scrollable, linear array of widgets with a custom child model.
1486 ///
1487 /// For example, a custom child model can control the algorithm used to
1488 /// estimate the size of children that are not actually visible.
1489 ///
1490 /// {@tool dartpad}
1491 /// This example shows a [ListView] that uses a custom [SliverChildBuilderDelegate] to support child
1492 /// reordering.
1493 ///
1494 /// ** See code in examples/api/lib/widgets/scroll_view/list_view.1.dart **
1495 /// {@end-tool}
1496 const ListView.custom({
1497 super.key,
1498 super.scrollDirection,
1499 super.reverse,
1500 super.controller,
1501 super.primary,
1502 super.physics,
1503 super.shrinkWrap,
1504 super.padding,
1505 this.itemExtent,
1506 this.prototypeItem,
1507 this.itemExtentBuilder,
1508 required this.childrenDelegate,
1509 super.cacheExtent,
1510 super.semanticChildCount,
1511 super.dragStartBehavior,
1512 super.keyboardDismissBehavior,
1513 super.restorationId,
1514 super.clipBehavior,
1515 super.hitTestBehavior,
1516 }) : assert(
1517 (itemExtent == null && prototypeItem == null) ||
1518 (itemExtent == null && itemExtentBuilder == null) ||
1519 (prototypeItem == null && itemExtentBuilder == null),
1520 'You can only pass one of itemExtent, prototypeItem and itemExtentBuilder.',
1521 );
1522
1523 /// {@template flutter.widgets.list_view.itemExtent}
1524 /// If non-null, forces the children to have the given extent in the scroll
1525 /// direction.
1526 ///
1527 /// Specifying an [itemExtent] is more efficient than letting the children
1528 /// determine their own extent because the scrolling machinery can make use of
1529 /// the foreknowledge of the children's extent to save work, for example when
1530 /// the scroll position changes drastically.
1531 ///
1532 /// See also:
1533 ///
1534 /// * [SliverFixedExtentList], the sliver used internally when this property
1535 /// is provided. It constrains its box children to have a specific given
1536 /// extent along the main axis.
1537 /// * The [prototypeItem] property, which allows forcing the children's
1538 /// extent to be the same as the given widget.
1539 /// * The [itemExtentBuilder] property, which allows forcing the children's
1540 /// extent to be the value returned by the callback.
1541 /// {@endtemplate}
1542 final double? itemExtent;
1543
1544 /// {@template flutter.widgets.list_view.itemExtentBuilder}
1545 /// If non-null, forces the children to have the corresponding extent returned
1546 /// by the builder.
1547 ///
1548 /// Specifying an [itemExtentBuilder] is more efficient than letting the children
1549 /// determine their own extent because the scrolling machinery can make use of
1550 /// the foreknowledge of the children's extent to save work, for example when
1551 /// the scroll position changes drastically.
1552 ///
1553 /// This will be called multiple times during the layout phase of a frame to find
1554 /// the items that should be loaded by the lazy loading process.
1555 ///
1556 /// Should return null if asked to build an item extent with a greater index than
1557 /// exists.
1558 ///
1559 /// Unlike [itemExtent] or [prototypeItem], this allows children to have
1560 /// different extents.
1561 ///
1562 /// See also:
1563 ///
1564 /// * [SliverVariedExtentList], the sliver used internally when this property
1565 /// is provided. It constrains its box children to have a specific given
1566 /// extent along the main axis.
1567 /// * The [itemExtent] property, which allows forcing the children's extent
1568 /// to a given value.
1569 /// * The [prototypeItem] property, which allows forcing the children's
1570 /// extent to be the same as the given widget.
1571 /// {@endtemplate}
1572 final ItemExtentBuilder? itemExtentBuilder;
1573
1574 /// {@template flutter.widgets.list_view.prototypeItem}
1575 /// If non-null, forces the children to have the same extent as the given
1576 /// widget in the scroll direction.
1577 ///
1578 /// Specifying an [prototypeItem] is more efficient than letting the children
1579 /// determine their own extent because the scrolling machinery can make use of
1580 /// the foreknowledge of the children's extent to save work, for example when
1581 /// the scroll position changes drastically.
1582 ///
1583 /// See also:
1584 ///
1585 /// * [SliverPrototypeExtentList], the sliver used internally when this
1586 /// property is provided. It constrains its box children to have the same
1587 /// extent as a prototype item along the main axis.
1588 /// * The [itemExtent] property, which allows forcing the children's extent
1589 /// to a given value.
1590 /// * The [itemExtentBuilder] property, which allows forcing the children's
1591 /// extent to be the value returned by the callback.
1592 /// {@endtemplate}
1593 final Widget? prototypeItem;
1594
1595 /// A delegate that provides the children for the [ListView].
1596 ///
1597 /// The [ListView.custom] constructor lets you specify this delegate
1598 /// explicitly. The [ListView] and [ListView.builder] constructors create a
1599 /// [childrenDelegate] that wraps the given [List] and [IndexedWidgetBuilder],
1600 /// respectively.
1601 final SliverChildDelegate childrenDelegate;
1602
1603 @override
1604 Widget buildChildLayout(BuildContext context) {
1605 if (itemExtent != null) {
1606 return SliverFixedExtentList(delegate: childrenDelegate, itemExtent: itemExtent!);
1607 } else if (itemExtentBuilder != null) {
1608 return SliverVariedExtentList(
1609 delegate: childrenDelegate,
1610 itemExtentBuilder: itemExtentBuilder!,
1611 );
1612 } else if (prototypeItem != null) {
1613 return SliverPrototypeExtentList(delegate: childrenDelegate, prototypeItem: prototypeItem!);
1614 }
1615 return SliverList(delegate: childrenDelegate);
1616 }
1617
1618 @override
1619 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1620 super.debugFillProperties(properties);
1621 properties.add(DoubleProperty('itemExtent', itemExtent, defaultValue: null));
1622 }
1623
1624 // Helper method to compute the actual child count for the separated constructor.
1625 static int _computeActualChildCount(int itemCount) {
1626 return math.max(0, itemCount * 2 - 1);
1627 }
1628}
1629
1630/// A scrollable, 2D array of widgets.
1631///
1632/// {@youtube 560 315 https://www.youtube.com/watch?v=bLOtZDTm4H8}
1633///
1634/// The main axis direction of a grid is the direction in which it scrolls (the
1635/// [scrollDirection]).
1636///
1637/// The most commonly used grid layouts are [GridView.count], which creates a
1638/// layout with a fixed number of tiles in the cross axis, and
1639/// [GridView.extent], which creates a layout with tiles that have a maximum
1640/// cross-axis extent. A custom [SliverGridDelegate] can produce an arbitrary 2D
1641/// arrangement of children, including arrangements that are unaligned or
1642/// overlapping.
1643///
1644/// To create a grid with a large (or infinite) number of children, use the
1645/// [GridView.builder] constructor with either a
1646/// [SliverGridDelegateWithFixedCrossAxisCount] or a
1647/// [SliverGridDelegateWithMaxCrossAxisExtent] for the [gridDelegate].
1648///
1649/// To use a custom [SliverChildDelegate], use [GridView.custom].
1650///
1651/// To create a linear array of children, use a [ListView].
1652///
1653/// To control the initial scroll offset of the scroll view, provide a
1654/// [controller] with its [ScrollController.initialScrollOffset] property set.
1655///
1656/// ## Transitioning to [CustomScrollView]
1657///
1658/// A [GridView] is basically a [CustomScrollView] with a single [SliverGrid] in
1659/// its [CustomScrollView.slivers] property.
1660///
1661/// If [GridView] is no longer sufficient, for example because the scroll view
1662/// is to have both a grid and a list, or because the grid is to be combined
1663/// with a [SliverAppBar], etc, it is straight-forward to port code from using
1664/// [GridView] to using [CustomScrollView] directly.
1665///
1666/// The [key], [scrollDirection], [reverse], [controller], [primary], [physics],
1667/// and [shrinkWrap] properties on [GridView] map directly to the identically
1668/// named properties on [CustomScrollView].
1669///
1670/// The [CustomScrollView.slivers] property should be a list containing just a
1671/// [SliverGrid].
1672///
1673/// The [childrenDelegate] property on [GridView] corresponds to the
1674/// [SliverGrid.delegate] property, and the [gridDelegate] property on the
1675/// [GridView] corresponds to the [SliverGrid.gridDelegate] property.
1676///
1677/// The [GridView], [GridView.count], and [GridView.extent]
1678/// constructors' `children` arguments correspond to the [childrenDelegate]
1679/// being a [SliverChildListDelegate] with that same argument. The
1680/// [GridView.builder] constructor's `itemBuilder` and `childCount` arguments
1681/// correspond to the [childrenDelegate] being a [SliverChildBuilderDelegate]
1682/// with the matching arguments.
1683///
1684/// The [GridView.count] and [GridView.extent] constructors create
1685/// custom grid delegates, and have equivalently named constructors on
1686/// [SliverGrid] to ease the transition: [SliverGrid.count] and
1687/// [SliverGrid.extent] respectively.
1688///
1689/// The [padding] property corresponds to having a [SliverPadding] in the
1690/// [CustomScrollView.slivers] property instead of the grid itself, and having
1691/// the [SliverGrid] instead be a child of the [SliverPadding].
1692///
1693/// Once code has been ported to use [CustomScrollView], other slivers, such as
1694/// [SliverList] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
1695/// list.
1696///
1697/// {@macro flutter.widgets.ScrollView.PageStorage}
1698///
1699/// ## Examples
1700///
1701/// {@tool snippet}
1702/// This example demonstrates how to create a [GridView] with two columns. The
1703/// children are spaced apart using the `crossAxisSpacing` and `mainAxisSpacing`
1704/// properties.
1705///
1706/// ![The GridView displays six children with different background colors arranged in two columns](https://flutter.github.io/assets-for-api-docs/assets/widgets/grid_view.png)
1707///
1708/// ```dart
1709/// GridView.count(
1710/// primary: false,
1711/// padding: const EdgeInsets.all(20),
1712/// crossAxisSpacing: 10,
1713/// mainAxisSpacing: 10,
1714/// crossAxisCount: 2,
1715/// children: <Widget>[
1716/// Container(
1717/// padding: const EdgeInsets.all(8),
1718/// color: Colors.teal[100],
1719/// child: const Text("He'd have you all unravel at the"),
1720/// ),
1721/// Container(
1722/// padding: const EdgeInsets.all(8),
1723/// color: Colors.teal[200],
1724/// child: const Text('Heed not the rabble'),
1725/// ),
1726/// Container(
1727/// padding: const EdgeInsets.all(8),
1728/// color: Colors.teal[300],
1729/// child: const Text('Sound of screams but the'),
1730/// ),
1731/// Container(
1732/// padding: const EdgeInsets.all(8),
1733/// color: Colors.teal[400],
1734/// child: const Text('Who scream'),
1735/// ),
1736/// Container(
1737/// padding: const EdgeInsets.all(8),
1738/// color: Colors.teal[500],
1739/// child: const Text('Revolution is coming...'),
1740/// ),
1741/// Container(
1742/// padding: const EdgeInsets.all(8),
1743/// color: Colors.teal[600],
1744/// child: const Text('Revolution, they...'),
1745/// ),
1746/// ],
1747/// )
1748/// ```
1749/// {@end-tool}
1750///
1751/// {@tool snippet}
1752/// This example shows how to create the same grid as the previous example
1753/// using a [CustomScrollView] and a [SliverGrid].
1754///
1755/// ![The CustomScrollView contains a SliverGrid that displays six children with different background colors arranged in two columns](https://flutter.github.io/assets-for-api-docs/assets/widgets/grid_view_custom_scroll.png)
1756///
1757/// ```dart
1758/// CustomScrollView(
1759/// primary: false,
1760/// slivers: <Widget>[
1761/// SliverPadding(
1762/// padding: const EdgeInsets.all(20),
1763/// sliver: SliverGrid.count(
1764/// crossAxisSpacing: 10,
1765/// mainAxisSpacing: 10,
1766/// crossAxisCount: 2,
1767/// children: <Widget>[
1768/// Container(
1769/// padding: const EdgeInsets.all(8),
1770/// color: Colors.green[100],
1771/// child: const Text("He'd have you all unravel at the"),
1772/// ),
1773/// Container(
1774/// padding: const EdgeInsets.all(8),
1775/// color: Colors.green[200],
1776/// child: const Text('Heed not the rabble'),
1777/// ),
1778/// Container(
1779/// padding: const EdgeInsets.all(8),
1780/// color: Colors.green[300],
1781/// child: const Text('Sound of screams but the'),
1782/// ),
1783/// Container(
1784/// padding: const EdgeInsets.all(8),
1785/// color: Colors.green[400],
1786/// child: const Text('Who scream'),
1787/// ),
1788/// Container(
1789/// padding: const EdgeInsets.all(8),
1790/// color: Colors.green[500],
1791/// child: const Text('Revolution is coming...'),
1792/// ),
1793/// Container(
1794/// padding: const EdgeInsets.all(8),
1795/// color: Colors.green[600],
1796/// child: const Text('Revolution, they...'),
1797/// ),
1798/// ],
1799/// ),
1800/// ),
1801/// ],
1802/// )
1803/// ```
1804/// {@end-tool}
1805///
1806/// {@tool dartpad}
1807/// This example shows a custom implementation of selection in list and grid views.
1808/// Use the button in the top right (possibly hidden under the DEBUG banner) to toggle between
1809/// [ListView] and [GridView].
1810/// Long press any [ListTile] or [GridTile] to enable selection mode.
1811///
1812/// ** See code in examples/api/lib/widgets/scroll_view/list_view.0.dart **
1813/// {@end-tool}
1814///
1815/// {@tool dartpad}
1816/// This example shows a custom [SliverGridDelegate].
1817///
1818/// ** See code in examples/api/lib/widgets/scroll_view/grid_view.0.dart **
1819/// {@end-tool}
1820///
1821/// ## Troubleshooting
1822///
1823/// ### Padding
1824///
1825/// By default, [GridView] will automatically pad the limits of the
1826/// grid's scrollable to avoid partial obstructions indicated by
1827/// [MediaQuery]'s padding. To avoid this behavior, override with a
1828/// zero [padding] property.
1829///
1830/// {@tool snippet}
1831/// The following example demonstrates how to override the default top padding
1832/// using [MediaQuery.removePadding].
1833///
1834/// ```dart
1835/// Widget myWidget(BuildContext context) {
1836/// return MediaQuery.removePadding(
1837/// context: context,
1838/// removeTop: true,
1839/// child: GridView.builder(
1840/// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
1841/// crossAxisCount: 3,
1842/// ),
1843/// itemCount: 300,
1844/// itemBuilder: (BuildContext context, int index) {
1845/// return Card(
1846/// color: Colors.amber,
1847/// child: Center(child: Text('$index')),
1848/// );
1849/// }
1850/// ),
1851/// );
1852/// }
1853/// ```
1854/// {@end-tool}
1855///
1856/// See also:
1857///
1858/// * [SingleChildScrollView], which is a scrollable widget that has a single
1859/// child.
1860/// * [ListView], which is scrollable, linear list of widgets.
1861/// * [PageView], which is a scrolling list of child widgets that are each the
1862/// size of the viewport.
1863/// * [CustomScrollView], which is a scrollable widget that creates custom
1864/// scroll effects using slivers.
1865/// * [SliverGridDelegateWithFixedCrossAxisCount], which creates a layout with
1866/// a fixed number of tiles in the cross axis.
1867/// * [SliverGridDelegateWithMaxCrossAxisExtent], which creates a layout with
1868/// tiles that have a maximum cross-axis extent.
1869/// * [ScrollNotification] and [NotificationListener], which can be used to watch
1870/// the scroll position without using a [ScrollController].
1871/// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
1872class GridView extends BoxScrollView {
1873 /// Creates a scrollable, 2D array of widgets with a custom
1874 /// [SliverGridDelegate].
1875 ///
1876 /// The `addAutomaticKeepAlives` argument corresponds to the
1877 /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
1878 /// `addRepaintBoundaries` argument corresponds to the
1879 /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
1880 /// null.
1881 GridView({
1882 super.key,
1883 super.scrollDirection,
1884 super.reverse,
1885 super.controller,
1886 super.primary,
1887 super.physics,
1888 super.shrinkWrap,
1889 super.padding,
1890 required this.gridDelegate,
1891 bool addAutomaticKeepAlives = true,
1892 bool addRepaintBoundaries = true,
1893 bool addSemanticIndexes = true,
1894 super.cacheExtent,
1895 List<Widget> children = const <Widget>[],
1896 int? semanticChildCount,
1897 super.dragStartBehavior,
1898 super.clipBehavior,
1899 super.keyboardDismissBehavior,
1900 super.restorationId,
1901 super.hitTestBehavior,
1902 }) : childrenDelegate = SliverChildListDelegate(
1903 children,
1904 addAutomaticKeepAlives: addAutomaticKeepAlives,
1905 addRepaintBoundaries: addRepaintBoundaries,
1906 addSemanticIndexes: addSemanticIndexes,
1907 ),
1908 super(semanticChildCount: semanticChildCount ?? children.length);
1909
1910 /// Creates a scrollable, 2D array of widgets that are created on demand.
1911 ///
1912 /// This constructor is appropriate for grid views with a large (or infinite)
1913 /// number of children because the builder is called only for those children
1914 /// that are actually visible.
1915 ///
1916 /// Providing a non-null `itemCount` improves the ability of the [GridView] to
1917 /// estimate the maximum scroll extent.
1918 ///
1919 /// `itemBuilder` will be called only with indices greater than or equal to
1920 /// zero and less than `itemCount`.
1921 ///
1922 /// {@macro flutter.widgets.ListView.builder.itemBuilder}
1923 ///
1924 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
1925 ///
1926 /// The [gridDelegate] argument is required.
1927 ///
1928 /// The `addAutomaticKeepAlives` argument corresponds to the
1929 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
1930 /// `addRepaintBoundaries` argument corresponds to the
1931 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
1932 /// `addSemanticIndexes` argument corresponds to the
1933 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
1934 GridView.builder({
1935 super.key,
1936 super.scrollDirection,
1937 super.reverse,
1938 super.controller,
1939 super.primary,
1940 super.physics,
1941 super.shrinkWrap,
1942 super.padding,
1943 required this.gridDelegate,
1944 required NullableIndexedWidgetBuilder itemBuilder,
1945 ChildIndexGetter? findChildIndexCallback,
1946 int? itemCount,
1947 bool addAutomaticKeepAlives = true,
1948 bool addRepaintBoundaries = true,
1949 bool addSemanticIndexes = true,
1950 super.cacheExtent,
1951 int? semanticChildCount,
1952 super.dragStartBehavior,
1953 super.keyboardDismissBehavior,
1954 super.restorationId,
1955 super.clipBehavior,
1956 super.hitTestBehavior,
1957 }) : childrenDelegate = SliverChildBuilderDelegate(
1958 itemBuilder,
1959 findChildIndexCallback: findChildIndexCallback,
1960 childCount: itemCount,
1961 addAutomaticKeepAlives: addAutomaticKeepAlives,
1962 addRepaintBoundaries: addRepaintBoundaries,
1963 addSemanticIndexes: addSemanticIndexes,
1964 ),
1965 super(semanticChildCount: semanticChildCount ?? itemCount);
1966
1967 /// Creates a scrollable, 2D array of widgets with both a custom
1968 /// [SliverGridDelegate] and a custom [SliverChildDelegate].
1969 ///
1970 /// To use an [IndexedWidgetBuilder] callback to build children, either use
1971 /// a [SliverChildBuilderDelegate] or use the [GridView.builder] constructor.
1972 const GridView.custom({
1973 super.key,
1974 super.scrollDirection,
1975 super.reverse,
1976 super.controller,
1977 super.primary,
1978 super.physics,
1979 super.shrinkWrap,
1980 super.padding,
1981 required this.gridDelegate,
1982 required this.childrenDelegate,
1983 super.cacheExtent,
1984 super.semanticChildCount,
1985 super.dragStartBehavior,
1986 super.keyboardDismissBehavior,
1987 super.restorationId,
1988 super.clipBehavior,
1989 super.hitTestBehavior,
1990 });
1991
1992 /// Creates a scrollable, 2D array of widgets with a fixed number of tiles in
1993 /// the cross axis.
1994 ///
1995 /// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate].
1996 ///
1997 /// The `addAutomaticKeepAlives` argument corresponds to the
1998 /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
1999 /// `addRepaintBoundaries` argument corresponds to the
2000 /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
2001 /// null.
2002 ///
2003 /// See also:
2004 ///
2005 /// * [SliverGrid.count], the equivalent constructor for [SliverGrid].
2006 GridView.count({
2007 super.key,
2008 super.scrollDirection,
2009 super.reverse,
2010 super.controller,
2011 super.primary,
2012 super.physics,
2013 super.shrinkWrap,
2014 super.padding,
2015 required int crossAxisCount,
2016 double mainAxisSpacing = 0.0,
2017 double crossAxisSpacing = 0.0,
2018 double childAspectRatio = 1.0,
2019 bool addAutomaticKeepAlives = true,
2020 bool addRepaintBoundaries = true,
2021 bool addSemanticIndexes = true,
2022 super.cacheExtent,
2023 List<Widget> children = const <Widget>[],
2024 int? semanticChildCount,
2025 super.dragStartBehavior,
2026 super.keyboardDismissBehavior,
2027 super.restorationId,
2028 super.clipBehavior,
2029 super.hitTestBehavior,
2030 }) : gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
2031 crossAxisCount: crossAxisCount,
2032 mainAxisSpacing: mainAxisSpacing,
2033 crossAxisSpacing: crossAxisSpacing,
2034 childAspectRatio: childAspectRatio,
2035 ),
2036 childrenDelegate = SliverChildListDelegate(
2037 children,
2038 addAutomaticKeepAlives: addAutomaticKeepAlives,
2039 addRepaintBoundaries: addRepaintBoundaries,
2040 addSemanticIndexes: addSemanticIndexes,
2041 ),
2042 super(semanticChildCount: semanticChildCount ?? children.length);
2043
2044 /// Creates a scrollable, 2D array of widgets with tiles that each have a
2045 /// maximum cross-axis extent.
2046 ///
2047 /// Uses a [SliverGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate].
2048 ///
2049 /// The `addAutomaticKeepAlives` argument corresponds to the
2050 /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
2051 /// `addRepaintBoundaries` argument corresponds to the
2052 /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
2053 /// null.
2054 ///
2055 /// See also:
2056 ///
2057 /// * [SliverGrid.extent], the equivalent constructor for [SliverGrid].
2058 GridView.extent({
2059 super.key,
2060 super.scrollDirection,
2061 super.reverse,
2062 super.controller,
2063 super.primary,
2064 super.physics,
2065 super.shrinkWrap,
2066 super.padding,
2067 required double maxCrossAxisExtent,
2068 double mainAxisSpacing = 0.0,
2069 double crossAxisSpacing = 0.0,
2070 double childAspectRatio = 1.0,
2071 bool addAutomaticKeepAlives = true,
2072 bool addRepaintBoundaries = true,
2073 bool addSemanticIndexes = true,
2074 super.cacheExtent,
2075 List<Widget> children = const <Widget>[],
2076 int? semanticChildCount,
2077 super.dragStartBehavior,
2078 super.keyboardDismissBehavior,
2079 super.restorationId,
2080 super.clipBehavior,
2081 super.hitTestBehavior,
2082 }) : gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
2083 maxCrossAxisExtent: maxCrossAxisExtent,
2084 mainAxisSpacing: mainAxisSpacing,
2085 crossAxisSpacing: crossAxisSpacing,
2086 childAspectRatio: childAspectRatio,
2087 ),
2088 childrenDelegate = SliverChildListDelegate(
2089 children,
2090 addAutomaticKeepAlives: addAutomaticKeepAlives,
2091 addRepaintBoundaries: addRepaintBoundaries,
2092 addSemanticIndexes: addSemanticIndexes,
2093 ),
2094 super(semanticChildCount: semanticChildCount ?? children.length);
2095
2096 /// A delegate that controls the layout of the children within the [GridView].
2097 ///
2098 /// The [GridView], [GridView.builder], and [GridView.custom] constructors let you specify this
2099 /// delegate explicitly. The other constructors create a [gridDelegate]
2100 /// implicitly.
2101 final SliverGridDelegate gridDelegate;
2102
2103 /// A delegate that provides the children for the [GridView].
2104 ///
2105 /// The [GridView.custom] constructor lets you specify this delegate
2106 /// explicitly. The other constructors create a [childrenDelegate] that wraps
2107 /// the given child list.
2108 final SliverChildDelegate childrenDelegate;
2109
2110 @override
2111 Widget buildChildLayout(BuildContext context) {
2112 return SliverGrid(delegate: childrenDelegate, gridDelegate: gridDelegate);
2113 }
2114}
2115

Provided by KDAB

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