suspension_view.dart
4.82 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
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'dart:math' as math;
import 'az_common.dart';
const double kSusItemHeight = 40;
/// SuspensionView.
class SuspensionView extends StatefulWidget {
const SuspensionView({
super.key,
required this.data,
required this.itemCount,
required this.itemBuilder,
this.itemScrollController,
this.itemPositionsListener,
this.susItemBuilder,
this.susItemHeight = kSusItemHeight,
this.susPosition,
this.physics,
this.padding,
});
/// Suspension data.
final List<ISuspensionBean> data;
/// Number of items the [itemBuilder] can produce.
final int itemCount;
/// Called to build children for the list with
/// 0 <= index < itemCount.
final IndexedWidgetBuilder itemBuilder;
/// Controller for jumping or scrolling to an item.
final ItemScrollController? itemScrollController;
/// Notifier that reports the items laid out in the list after each frame.
final ItemPositionsListener? itemPositionsListener;
/// Called to build suspension header.
final IndexedWidgetBuilder? susItemBuilder;
/// Suspension item Height.
final double susItemHeight;
/// Suspension item position.
final Offset? susPosition;
/// How the scroll view should respond to user input.
///
/// For example, determines how the scroll view continues to animate after the
/// user stops dragging the scroll view.
///
/// See [ScrollView.physics].
final ScrollPhysics? physics;
/// The amount of space by which to inset the children.
final EdgeInsets? padding;
@override
createState() => _SuspensionViewState();
}
class _SuspensionViewState extends State<SuspensionView> {
/// Controller to scroll or jump to a particular item.
late ItemScrollController itemScrollController;
/// Listener that reports the position of items when the list is scrolled.
late ItemPositionsListener itemPositionsListener;
@override
void initState() {
super.initState();
itemScrollController = widget.itemScrollController ?? ItemScrollController();
itemPositionsListener = widget.itemPositionsListener ?? ItemPositionsListener.create();
}
@override
void dispose() {
super.dispose();
}
/// build sus widget.
Widget _buildSusWidget(BuildContext context) {
if (widget.susItemBuilder == null) {
return Container();
}
return ValueListenableBuilder<Iterable<ItemPosition>>(
valueListenable: itemPositionsListener.itemPositions,
builder: (ctx, positions, child) {
if (positions.isEmpty || widget.itemCount == 0) {
return Container();
}
ItemPosition itemPosition = positions.where((ItemPosition position) => position.itemTrailingEdge > 0).reduce(
(ItemPosition min, ItemPosition position) =>
position.itemTrailingEdge < min.itemTrailingEdge ? position : min);
if (itemPosition.itemLeadingEdge > 0) return Container();
int index = itemPosition.index;
double left = 0;
double top = 0;
if (index < widget.itemCount) {
if (widget.susPosition != null) {
left = widget.susPosition!.dx;
top = widget.susPosition!.dy;
} else {
int next = math.min(index + 1, widget.itemCount - 1);
ISuspensionBean bean = widget.data[next];
if (bean.isShowSuspension) {
double height = context.findRenderObject()?.paintBounds.height ?? 0;
double topTemp = itemPosition.itemTrailingEdge * height;
top = math.min(widget.susItemHeight, topTemp) - widget.susItemHeight;
}
}
} else {
index = 0;
}
return Positioned(
left: left,
top: top,
child: widget.susItemBuilder!(ctx, index),
);
},
);
}
Widget _buildItem(BuildContext context, int index) {
ISuspensionBean bean = widget.data[index];
if (!bean.isShowSuspension || widget.susItemBuilder == null) {
return widget.itemBuilder(context, index);
}
return Column(
mainAxisSize: MainAxisSize.min,
children: [
widget.susItemBuilder!(context, index),
widget.itemBuilder(context, index),
],
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
widget.itemCount == 0
? Container()
: ScrollablePositionedList.builder(
itemCount: widget.itemCount,
itemBuilder: (context, index) => _buildItem(context, index),
itemScrollController: itemScrollController,
itemPositionsListener: itemPositionsListener,
physics: widget.physics,
padding: widget.padding,
),
_buildSusWidget(context),
],
);
}
}