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
5import 'package:flutter/rendering.dart';
6
7import 'framework.dart';
8import 'scroll_delegate.dart';
9import 'sliver.dart';
10
11/// A sliver that places its box children in a linear array and constrains them
12/// to have the corresponding extent returned by [itemExtentBuilder].
13///
14/// _To learn more about slivers, see [CustomScrollView.slivers]._
15///
16/// [SliverVariedExtentList] arranges its children in a line along
17/// the main axis starting at offset zero and without gaps. Each child is
18/// constrained to the corresponding extent along the main axis
19/// and the [SliverConstraints.crossAxisExtent] along the cross axis.
20///
21/// [SliverVariedExtentList] is more efficient than [SliverList] because
22/// [SliverVariedExtentList] does not need to lay out its children to obtain
23/// their extent along the main axis. It's a little more flexible than
24/// [SliverFixedExtentList] because this allow the children to have different extents.
25///
26/// See also:
27///
28/// * [SliverFixedExtentList], whose children are forced to a given pixel
29/// extent.
30/// * [SliverList], which does not require its children to have the same
31/// extent in the main axis.
32/// * [SliverFillViewport], which sizes its children based on the
33/// size of the viewport, regardless of what else is in the scroll view.
34class SliverVariedExtentList extends SliverMultiBoxAdaptorWidget {
35 /// Creates a sliver that places box children with the same main axis extent
36 /// in a linear array.
37 const SliverVariedExtentList({
38 super.key,
39 required super.delegate,
40 required this.itemExtentBuilder,
41 });
42
43 /// A sliver that places multiple box children in a linear array along the main
44 /// axis.
45 ///
46 /// [SliverVariedExtentList] places its children in a linear array along the main
47 /// axis starting at offset zero and without gaps. Each child is forced to have
48 /// the returned extent of [itemExtentBuilder] in the main axis and the
49 /// [SliverConstraints.crossAxisExtent] in the cross axis.
50 ///
51 /// This constructor is appropriate for sliver lists with a large (or
52 /// infinite) number of children whose extent is already determined.
53 ///
54 /// Providing a non-null `itemCount` improves the ability of the [SliverGrid]
55 /// to estimate the maximum scroll extent.
56 SliverVariedExtentList.builder({
57 super.key,
58 required NullableIndexedWidgetBuilder itemBuilder,
59 required this.itemExtentBuilder,
60 ChildIndexGetter? findChildIndexCallback,
61 int? itemCount,
62 bool addAutomaticKeepAlives = true,
63 bool addRepaintBoundaries = true,
64 bool addSemanticIndexes = true,
65 }) : super(delegate: SliverChildBuilderDelegate(
66 itemBuilder,
67 findChildIndexCallback: findChildIndexCallback,
68 childCount: itemCount,
69 addAutomaticKeepAlives: addAutomaticKeepAlives,
70 addRepaintBoundaries: addRepaintBoundaries,
71 addSemanticIndexes: addSemanticIndexes,
72 ));
73
74 /// A sliver that places multiple box children in a linear array along the main
75 /// axis.
76 ///
77 /// [SliverVariedExtentList] places its children in a linear array along the main
78 /// axis starting at offset zero and without gaps. Each child is forced to have
79 /// the returned extent of [itemExtentBuilder] in the main axis and the
80 /// [SliverConstraints.crossAxisExtent] in the cross axis.
81 ///
82 /// This constructor uses a list of [Widget]s to build the sliver.
83 SliverVariedExtentList.list({
84 super.key,
85 required List<Widget> children,
86 required this.itemExtentBuilder,
87 bool addAutomaticKeepAlives = true,
88 bool addRepaintBoundaries = true,
89 bool addSemanticIndexes = true,
90 }) : super(delegate: SliverChildListDelegate(
91 children,
92 addAutomaticKeepAlives: addAutomaticKeepAlives,
93 addRepaintBoundaries: addRepaintBoundaries,
94 addSemanticIndexes: addSemanticIndexes,
95 ));
96
97 /// The children extent builder.
98 final ItemExtentBuilder itemExtentBuilder;
99
100 @override
101 RenderSliverVariedExtentList createRenderObject(BuildContext context) {
102 final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
103 return RenderSliverVariedExtentList(childManager: element, itemExtentBuilder: itemExtentBuilder);
104 }
105
106 @override
107 void updateRenderObject(BuildContext context, RenderSliverVariedExtentList renderObject) {
108 renderObject.itemExtentBuilder = itemExtentBuilder;
109 }
110}
111
112/// A sliver that places multiple box children with the corresponding main axis extent in
113/// a linear array.
114class RenderSliverVariedExtentList extends RenderSliverFixedExtentBoxAdaptor {
115 /// Creates a sliver that contains multiple box children that have a explicit
116 /// extent in the main axis.
117 RenderSliverVariedExtentList({
118 required super.childManager,
119 required ItemExtentBuilder itemExtentBuilder,
120 }) : _itemExtentBuilder = itemExtentBuilder;
121
122 @override
123 ItemExtentBuilder get itemExtentBuilder => _itemExtentBuilder;
124 ItemExtentBuilder _itemExtentBuilder;
125 set itemExtentBuilder(ItemExtentBuilder value) {
126 if (_itemExtentBuilder == value) {
127 return;
128 }
129 _itemExtentBuilder = value;
130 markNeedsLayout();
131 }
132
133 @override
134 double? get itemExtent => null;
135}
136