@@ -108,10 +108,22 @@ abstract interface class RenderAbstractViewport extends RenderObject {
108
108
/// when the offset of the viewport is changed by x then `target` also moves
109
109
/// by x within the viewport.
110
110
///
111
+ /// The optional [Axis] is used by
112
+ /// [RenderTwoDimensionalViewport.getOffsetToReveal] to
113
+ /// determine which of the two axes to compute an offset for. One dimensional
114
+ /// subclasses like [RenderViewportBase] and [RenderListWheelViewport] will
115
+ /// assert in debug builds if the `axis` value is provided and does not match
116
+ /// the single [Axis] that viewport is configured for.
117
+ ///
111
118
/// See also:
112
119
///
113
120
/// * [RevealedOffset] , which describes the return value of this method.
114
- RevealedOffset getOffsetToReveal (RenderObject target, double alignment, { Rect ? rect });
121
+ RevealedOffset getOffsetToReveal (
122
+ RenderObject target,
123
+ double alignment, {
124
+ Rect ? rect,
125
+ Axis ? axis,
126
+ });
115
127
116
128
/// The default value for the cache extent of the viewport.
117
129
///
@@ -169,6 +181,56 @@ class RevealedOffset {
169
181
/// value for a specific element.
170
182
final Rect rect;
171
183
184
+ /// Determines which provided leading or trailing edge of the viewport, as
185
+ /// [RevealedOffset] s, will be used for [RenderViewportBase.showInViewport]
186
+ /// accounting for the size and already visible portion of the [RenderObject]
187
+ /// that is being revealed.
188
+ ///
189
+ /// Also used by [RenderTwoDimensionalViewport.showInViewport] for each
190
+ /// horizontal and vertical [Axis] .
191
+ ///
192
+ /// If the target [RenderObject] is already fully visible, this will return
193
+ /// null.
194
+ static RevealedOffset ? clampOffset ({
195
+ required RevealedOffset leadingEdgeOffset,
196
+ required RevealedOffset trailingEdgeOffset,
197
+ required double currentOffset,
198
+ }) {
199
+ // scrollOffset
200
+ // 0 +---------+
201
+ // | |
202
+ // _ | |
203
+ // viewport position | | |
204
+ // with `descendant` at | | | _
205
+ // trailing edge |_ | xxxxxxx | | viewport position
206
+ // | | | with `descendant` at
207
+ // | | _| leading edge
208
+ // | |
209
+ // 800 +---------+
210
+ //
211
+ // `trailingEdgeOffset`: Distance from scrollOffset 0 to the start of the
212
+ // viewport on the left in image above.
213
+ // `leadingEdgeOffset`: Distance from scrollOffset 0 to the start of the
214
+ // viewport on the right in image above.
215
+ //
216
+ // The viewport position on the left is achieved by setting `offset.pixels`
217
+ // to `trailingEdgeOffset`, the one on the right by setting it to
218
+ // `leadingEdgeOffset`.
219
+ final bool inverted = leadingEdgeOffset.offset < trailingEdgeOffset.offset;
220
+ final RevealedOffset smaller;
221
+ final RevealedOffset larger;
222
+ (smaller, larger) = inverted
223
+ ? (leadingEdgeOffset, trailingEdgeOffset)
224
+ : (trailingEdgeOffset, leadingEdgeOffset);
225
+ if (currentOffset > larger.offset) {
226
+ return larger;
227
+ } else if (currentOffset < smaller.offset) {
228
+ return smaller;
229
+ } else {
230
+ return null ;
231
+ }
232
+ }
233
+
172
234
@override
173
235
String toString () {
174
236
return '${objectRuntimeType (this , 'RevealedOffset' )}(offset: $offset , rect: $rect )' ;
@@ -753,7 +815,17 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
753
815
}
754
816
755
817
@override
756
- RevealedOffset getOffsetToReveal (RenderObject target, double alignment, { Rect ? rect }) {
818
+ RevealedOffset getOffsetToReveal (
819
+ RenderObject target,
820
+ double alignment, {
821
+ Rect ? rect,
822
+ Axis ? axis,
823
+ }) {
824
+ // One dimensional viewport has only one axis, it should match if it has
825
+ // been provided.
826
+ axis ?? = this .axis;
827
+ assert (axis == this .axis);
828
+
757
829
// Steps to convert `rect` (from a RenderBox coordinate system) to its
758
830
// scroll offset within this viewport (not in the exact order):
759
831
//
@@ -1164,52 +1236,19 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
1164
1236
final RevealedOffset leadingEdgeOffset = viewport.getOffsetToReveal (descendant, 0.0 , rect: rect);
1165
1237
final RevealedOffset trailingEdgeOffset = viewport.getOffsetToReveal (descendant, 1.0 , rect: rect);
1166
1238
final double currentOffset = offset.pixels;
1167
-
1168
- // scrollOffset
1169
- // 0 +---------+
1170
- // | |
1171
- // _ | |
1172
- // viewport position | | |
1173
- // with `descendant` at | | | _
1174
- // trailing edge |_ | xxxxxxx | | viewport position
1175
- // | | | with `descendant` at
1176
- // | | _| leading edge
1177
- // | |
1178
- // 800 +---------+
1179
- //
1180
- // `trailingEdgeOffset`: Distance from scrollOffset 0 to the start of the
1181
- // viewport on the left in image above.
1182
- // `leadingEdgeOffset`: Distance from scrollOffset 0 to the start of the
1183
- // viewport on the right in image above.
1184
- //
1185
- // The viewport position on the left is achieved by setting `offset.pixels`
1186
- // to `trailingEdgeOffset`, the one on the right by setting it to
1187
- // `leadingEdgeOffset`.
1188
-
1189
- final RevealedOffset targetOffset;
1190
- if (leadingEdgeOffset.offset < trailingEdgeOffset.offset) {
1191
- // `descendant` is too big to be visible on screen in its entirety. Let's
1192
- // align it with the edge that requires the least amount of scrolling.
1193
- final double leadingEdgeDiff = (offset.pixels - leadingEdgeOffset.offset).abs ();
1194
- final double trailingEdgeDiff = (offset.pixels - trailingEdgeOffset.offset).abs ();
1195
- targetOffset = leadingEdgeDiff < trailingEdgeDiff ? leadingEdgeOffset : trailingEdgeOffset;
1196
- } else if (currentOffset > leadingEdgeOffset.offset) {
1197
- // `descendant` currently starts above the leading edge and can be shown
1198
- // fully on screen by scrolling down (which means: moving viewport up).
1199
- targetOffset = leadingEdgeOffset;
1200
- } else if (currentOffset < trailingEdgeOffset.offset) {
1201
- // `descendant currently ends below the trailing edge and can be shown
1202
- // fully on screen by scrolling up (which means: moving viewport down)
1203
- targetOffset = trailingEdgeOffset;
1204
- } else {
1239
+ final RevealedOffset ? targetOffset = RevealedOffset .clampOffset (
1240
+ leadingEdgeOffset: leadingEdgeOffset,
1241
+ trailingEdgeOffset: trailingEdgeOffset,
1242
+ currentOffset: currentOffset,
1243
+ );
1244
+ if (targetOffset == null ) {
1205
1245
// `descendant` is between leading and trailing edge and hence already
1206
1246
// fully shown on screen. No action necessary.
1207
1247
assert (viewport.parent != null );
1208
1248
final Matrix4 transform = descendant.getTransformTo (viewport.parent);
1209
1249
return MatrixUtils .transformRect (transform, rect ?? descendant.paintBounds);
1210
1250
}
1211
1251
1212
-
1213
1252
offset.moveTo (targetOffset.offset, duration: duration, curve: curve);
1214
1253
return targetOffset.rect;
1215
1254
}
0 commit comments