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 'basic.dart';
6/// @docImport 'scroll_view.dart';
7/// @docImport 'sliver_fill.dart';
8library;
9
10import 'package:flutter/rendering.dart';
11
12import 'framework.dart';
13import 'scroll_delegate.dart';
14import 'sliver.dart';
15
16/// A sliver that places its box children in a linear array and constrains them
17/// to have the same extent as a prototype item along the main axis.
18///
19/// _To learn more about slivers, see [CustomScrollView.slivers]._
20///
21/// [SliverPrototypeExtentList] arranges its children in a line along
22/// the main axis starting at offset zero and without gaps. Each child is
23/// constrained to the same extent as the [prototypeItem] along the main axis
24/// and the [SliverConstraints.crossAxisExtent] along the cross axis.
25///
26/// [SliverPrototypeExtentList] is more efficient than [SliverList] because
27/// [SliverPrototypeExtentList] does not need to lay out its children to obtain
28/// their extent along the main axis. It's a little more flexible than
29/// [SliverFixedExtentList] because there's no need to determine the appropriate
30/// item extent in pixels.
31///
32/// See also:
33///
34/// * [SliverFixedExtentList], whose children are forced to a given pixel
35/// extent.
36/// * [SliverVariedExtentList], which supports children with varying (but known
37/// upfront) extents.
38/// * [SliverList], which does not require its children to have the same
39/// extent in the main axis.
40/// * [SliverFillViewport], which sizes its children based on the
41/// size of the viewport, regardless of what else is in the scroll view.
42class SliverPrototypeExtentList extends SliverMultiBoxAdaptorWidget {
43 /// Creates a sliver that places its box children in a linear array and
44 /// constrains them to have the same extent as a prototype item along
45 /// the main axis.
46 const SliverPrototypeExtentList({
47 super.key,
48 required super.delegate,
49 required this.prototypeItem,
50 });
51
52 /// A sliver that places its box children in a linear array and constrains them
53 /// to have the same extent as a prototype item along the main axis.
54 ///
55 /// This constructor is appropriate for sliver lists with a large (or
56 /// infinite) number of children whose extent is already determined.
57 ///
58 /// Providing a non-null `itemCount` improves the ability of the [SliverGrid]
59 /// to estimate the maximum scroll extent.
60 ///
61 /// `itemBuilder` will be called only with indices greater than or equal to
62 /// zero and less than `itemCount`.
63 ///
64 /// {@macro flutter.widgets.ListView.builder.itemBuilder}
65 ///
66 /// The `prototypeItem` argument is used to determine the extent of each item.
67 ///
68 /// {@macro flutter.widgets.PageView.findChildIndexCallback}
69 ///
70 /// The `addAutomaticKeepAlives` argument corresponds to the
71 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
72 /// `addRepaintBoundaries` argument corresponds to the
73 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
74 /// `addSemanticIndexes` argument corresponds to the
75 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
76 ///
77 /// {@tool snippet}
78 /// This example, which would be inserted into a [CustomScrollView.slivers]
79 /// list, shows an infinite number of items in varying shades of blue:
80 ///
81 /// ```dart
82 /// SliverPrototypeExtentList.builder(
83 /// prototypeItem: Container(
84 /// alignment: Alignment.center,
85 /// child: const Text('list item prototype'),
86 /// ),
87 /// itemBuilder: (BuildContext context, int index) {
88 /// return Container(
89 /// alignment: Alignment.center,
90 /// color: Colors.lightBlue[100 * (index % 9)],
91 /// child: Text('list item $index'),
92 /// );
93 /// },
94 /// )
95 /// ```
96 /// {@end-tool}
97 SliverPrototypeExtentList.builder({
98 super.key,
99 required NullableIndexedWidgetBuilder itemBuilder,
100 required this.prototypeItem,
101 ChildIndexGetter? findChildIndexCallback,
102 int? itemCount,
103 bool addAutomaticKeepAlives = true,
104 bool addRepaintBoundaries = true,
105 bool addSemanticIndexes = true,
106 }) : super(
107 delegate: SliverChildBuilderDelegate(
108 itemBuilder,
109 findChildIndexCallback: findChildIndexCallback,
110 childCount: itemCount,
111 addAutomaticKeepAlives: addAutomaticKeepAlives,
112 addRepaintBoundaries: addRepaintBoundaries,
113 addSemanticIndexes: addSemanticIndexes,
114 ),
115 );
116
117 /// A sliver that places multiple box children in a linear array along the main
118 /// axis.
119 ///
120 /// This constructor uses a list of [Widget]s to build the sliver.
121 ///
122 /// The `addAutomaticKeepAlives` argument corresponds to the
123 /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
124 /// `addRepaintBoundaries` argument corresponds to the
125 /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. The
126 /// `addSemanticIndexes` argument corresponds to the
127 /// [SliverChildBuilderDelegate.addSemanticIndexes] property.
128 ///
129 /// {@tool snippet}
130 /// This example, which would be inserted into a [CustomScrollView.slivers]
131 /// list, shows an infinite number of items in varying shades of blue:
132 ///
133 /// ```dart
134 /// SliverPrototypeExtentList.list(
135 /// prototypeItem: const Text('Hello'),
136 /// children: const <Widget>[
137 /// Text('Hello'),
138 /// Text('World!'),
139 /// ],
140 /// );
141 /// ```
142 /// {@end-tool}
143 SliverPrototypeExtentList.list({
144 super.key,
145 required List<Widget> children,
146 required this.prototypeItem,
147 bool addAutomaticKeepAlives = true,
148 bool addRepaintBoundaries = true,
149 bool addSemanticIndexes = true,
150 }) : super(
151 delegate: SliverChildListDelegate(
152 children,
153 addAutomaticKeepAlives: addAutomaticKeepAlives,
154 addRepaintBoundaries: addRepaintBoundaries,
155 addSemanticIndexes: addSemanticIndexes,
156 ),
157 );
158
159 /// Defines the main axis extent of all of this sliver's children.
160 ///
161 /// The [prototypeItem] is laid out before the rest of the sliver's children
162 /// and its size along the main axis fixes the size of each child. The
163 /// [prototypeItem] is essentially [Offstage]: it is not painted and it
164 /// cannot respond to input.
165 final Widget prototypeItem;
166
167 @override
168 RenderSliverMultiBoxAdaptor createRenderObject(BuildContext context) {
169 final _SliverPrototypeExtentListElement element = context as _SliverPrototypeExtentListElement;
170 return _RenderSliverPrototypeExtentList(childManager: element);
171 }
172
173 @override
174 SliverMultiBoxAdaptorElement createElement() => _SliverPrototypeExtentListElement(this);
175}
176
177class _SliverPrototypeExtentListElement extends SliverMultiBoxAdaptorElement {
178 _SliverPrototypeExtentListElement(SliverPrototypeExtentList super.widget);
179
180 @override
181 _RenderSliverPrototypeExtentList get renderObject =>
182 super.renderObject as _RenderSliverPrototypeExtentList;
183
184 Element? _prototype;
185 static final Object _prototypeSlot = Object();
186
187 @override
188 void insertRenderObjectChild(covariant RenderObject child, covariant Object slot) {
189 if (slot == _prototypeSlot) {
190 assert(child is RenderBox);
191 renderObject.child = child as RenderBox;
192 } else {
193 super.insertRenderObjectChild(child, slot as int);
194 }
195 }
196
197 @override
198 void didAdoptChild(RenderBox child) {
199 if (child != renderObject.child) {
200 super.didAdoptChild(child);
201 }
202 }
203
204 @override
205 void moveRenderObjectChild(RenderBox child, Object oldSlot, Object newSlot) {
206 if (newSlot == _prototypeSlot) {
207 // There's only one prototype child so it cannot be moved.
208 assert(false);
209 } else {
210 super.moveRenderObjectChild(child, oldSlot as int, newSlot as int);
211 }
212 }
213
214 @override
215 void removeRenderObjectChild(RenderBox child, Object slot) {
216 if (renderObject.child == child) {
217 renderObject.child = null;
218 } else {
219 super.removeRenderObjectChild(child, slot as int);
220 }
221 }
222
223 @override
224 void visitChildren(ElementVisitor visitor) {
225 if (_prototype != null) {
226 visitor(_prototype!);
227 }
228 super.visitChildren(visitor);
229 }
230
231 @override
232 void mount(Element? parent, Object? newSlot) {
233 super.mount(parent, newSlot);
234 _prototype = updateChild(
235 _prototype,
236 (widget as SliverPrototypeExtentList).prototypeItem,
237 _prototypeSlot,
238 );
239 }
240
241 @override
242 void update(SliverPrototypeExtentList newWidget) {
243 super.update(newWidget);
244 assert(widget == newWidget);
245 _prototype = updateChild(
246 _prototype,
247 (widget as SliverPrototypeExtentList).prototypeItem,
248 _prototypeSlot,
249 );
250 }
251}
252
253class _RenderSliverPrototypeExtentList extends RenderSliverFixedExtentBoxAdaptor {
254 _RenderSliverPrototypeExtentList({required _SliverPrototypeExtentListElement childManager})
255 : super(childManager: childManager);
256
257 RenderBox? _child;
258 RenderBox? get child => _child;
259 set child(RenderBox? value) {
260 if (_child != null) {
261 dropChild(_child!);
262 }
263 _child = value;
264 if (_child != null) {
265 adoptChild(_child!);
266 }
267 markNeedsLayout();
268 }
269
270 @override
271 void performLayout() {
272 child!.layout(constraints.asBoxConstraints(), parentUsesSize: true);
273 super.performLayout();
274 }
275
276 @override
277 void attach(PipelineOwner owner) {
278 super.attach(owner);
279 _child?.attach(owner);
280 }
281
282 @override
283 void detach() {
284 super.detach();
285 _child?.detach();
286 }
287
288 @override
289 void redepthChildren() {
290 if (_child != null) {
291 redepthChild(_child!);
292 }
293 super.redepthChildren();
294 }
295
296 @override
297 void visitChildren(RenderObjectVisitor visitor) {
298 if (_child != null) {
299 visitor(_child!);
300 }
301 super.visitChildren(visitor);
302 }
303
304 @override
305 double get itemExtent {
306 assert(child != null && child!.hasSize);
307 return constraints.axis == Axis.vertical ? child!.size.height : child!.size.width;
308 }
309}
310

Provided by KDAB

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