This repository was archived by the owner on Oct 13, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 529
Expand file tree
/
Copy pathmain.dart
More file actions
250 lines (226 loc) · 8.1 KB
/
main.dart
File metadata and controls
250 lines (226 loc) · 8.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
const numberOfItems = 5001;
const minItemHeight = 20.0;
const maxItemHeight = 150.0;
const scrollDuration = Duration(seconds: 2);
void main() {
runApp(ScrollablePositionedListExample());
}
// The root widget for the example app.
class ScrollablePositionedListExample extends StatelessWidget {
const ScrollablePositionedListExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ScrollablePositionedList Example',
theme: ThemeData(primarySwatch: Colors.blue),
home: const ScrollablePositionedListPage(),
);
}
}
/// Example widget that uses [ScrollablePositionedList].
///
/// Shows a [ScrollablePositionedList] along with the following controls:
/// - Buttons to jump or scroll to certain items in the list.
/// - Slider to control the alignment of the items being scrolled or jumped
/// to.
/// - A checkbox to reverse the list.
///
/// If the device this example is being used on is in portrait mode, the list
/// will be vertically scrollable, and if the device is in landscape mode, the
/// list will be horizontally scrollable.
class ScrollablePositionedListPage extends StatefulWidget {
const ScrollablePositionedListPage({Key? key}) : super(key: key);
@override
State<ScrollablePositionedListPage> createState() =>
_ScrollablePositionedListPageState();
}
class _ScrollablePositionedListPageState
extends State<ScrollablePositionedListPage> {
/// Controller to scroll or jump to a particular item.
final ItemScrollController itemScrollController = ItemScrollController();
/// Listener that reports the position of items when the list is scrolled.
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
late List<double> itemHeights;
late List<Color> itemColors;
bool reversed = false;
/// The alignment to be used next time the user scrolls or jumps to an item.
double alignment = 0;
@override
void initState() {
super.initState();
final heightGenerator = Random(328902348);
itemHeights = List<double>.generate(
numberOfItems,
(int _) =>
heightGenerator.nextDouble() * (maxItemHeight - minItemHeight) +
minItemHeight);
itemColors = List<Color>.generate(
numberOfItems,
(int _) => Color(
(Random().nextDouble() * 0xFFFFFF).toInt() << 0,
).withOpacity(1.0));
}
@override
Widget build(BuildContext context) => Material(
child: OrientationBuilder(
builder: (context, orientation) => Column(
children: <Widget>[
Expanded(
child: list(orientation),
),
positionsView,
Row(
children: <Widget>[
Column(
children: <Widget>[
scrollControlButtons,
const SizedBox(height: 10),
jumpControlButtons,
alignmentControl,
],
),
],
)
],
),
),
);
Widget get alignmentControl => Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const Text('Alignment: '),
SizedBox(
width: 200,
child: SliderTheme(
data: SliderThemeData(
showValueIndicator: ShowValueIndicator.always,
),
child: Slider(
value: alignment,
label: alignment.toStringAsFixed(2),
onChanged: (double value) => setState(() => alignment = value),
),
),
),
],
);
Widget list(Orientation orientation) => ScrollablePositionedList.builder(
itemCount: numberOfItems,
itemBuilder: (context, index) => item(index, orientation),
itemScrollController: itemScrollController,
itemPositionsListener: itemPositionsListener,
reverse: reversed,
scrollDirection: orientation == Orientation.portrait
? Axis.vertical
: Axis.horizontal,
);
Widget get positionsView => ValueListenableBuilder<Iterable<ItemPosition>>(
valueListenable: itemPositionsListener.itemPositions,
builder: (context, positions, child) {
int? min;
int? max;
if (positions.isNotEmpty) {
// Determine the first visible item by finding the item with the
// smallest trailing edge that is greater than 0. i.e. the first
// item whose trailing edge in visible in the viewport.
min = positions
.where((ItemPosition position) => position.itemTrailingEdge > 0)
.reduce((ItemPosition min, ItemPosition position) =>
position.itemTrailingEdge < min.itemTrailingEdge
? position
: min)
.index;
// Determine the last visible item by finding the item with the
// greatest leading edge that is less than 1. i.e. the last
// item whose leading edge in visible in the viewport.
max = positions
.where((ItemPosition position) => position.itemLeadingEdge < 1)
.reduce((ItemPosition max, ItemPosition position) =>
position.itemLeadingEdge > max.itemLeadingEdge
? position
: max)
.index;
}
return Row(
children: <Widget>[
Expanded(child: Text('First Item: ${min ?? ''}')),
Expanded(child: Text('Last Item: ${max ?? ''}')),
const Text('Reversed: '),
Checkbox(
value: reversed,
onChanged: (bool? value) => setState(() {
reversed = value!;
}))
],
);
},
);
Widget get scrollControlButtons => Row(
children: <Widget>[
const Text('scroll to'),
scrollButton(0),
scrollButton(5),
scrollButton(10),
scrollButton(100),
scrollButton(1000),
scrollButton(5000),
],
);
Widget get jumpControlButtons => Row(
children: <Widget>[
const Text('jump to'),
jumpButton(0),
jumpButton(5),
jumpButton(10),
jumpButton(100),
jumpButton(1000),
jumpButton(5000),
],
);
final _scrollButtonStyle = ButtonStyle(
padding: MaterialStateProperty.all(
const EdgeInsets.symmetric(horizontal: 20, vertical: 0),
),
minimumSize: MaterialStateProperty.all(Size.zero),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
);
Widget scrollButton(int value) => TextButton(
key: ValueKey<String>('Scroll$value'),
onPressed: () => scrollTo(value),
child: Text('$value'),
style: _scrollButtonStyle,
);
Widget jumpButton(int value) => TextButton(
key: ValueKey<String>('Jump$value'),
onPressed: () => jumpTo(value),
child: Text('$value'),
style: _scrollButtonStyle,
);
void scrollTo(int index) => itemScrollController.scrollTo(
index: index,
duration: scrollDuration,
curve: Curves.easeInOutCubic,
alignment: alignment);
void jumpTo(int index) =>
itemScrollController.jumpTo(index: index, alignment: alignment);
/// Generate item number [i].
Widget item(int i, Orientation orientation) {
return SizedBox(
height: orientation == Orientation.portrait ? itemHeights[i] : null,
width: orientation == Orientation.landscape ? itemHeights[i] : null,
child: Container(
color: itemColors[i],
child: Center(
child: Text('Item $i'),
),
),
);
}
}