Skip to content

Commit 72517f0

Browse files
authored
Encode scrolling status into tree (flutter#14536)
1 parent 248919f commit 72517f0

File tree

10 files changed

+317
-170
lines changed

10 files changed

+317
-170
lines changed

bin/internal/engine.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
a031239a5d4e44e60d0ebc62b8c544a9f592fc22
1+
8ac6f6efa177fb548dcdc81f1501f060b2ad1115

packages/flutter/lib/src/physics/utils.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ bool nearEqual(double a, double b, double epsilon) {
1212
assert(epsilon >= 0.0);
1313
if (a == null || b == null)
1414
return a == b;
15-
return (a > (b - epsilon)) && (a < (b + epsilon));
15+
return (a > (b - epsilon)) && (a < (b + epsilon)) || a == b;
1616
}
1717

1818
/// Whether a double is within a given distance of zero.

packages/flutter/lib/src/semantics/semantics.dart

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ class SemanticsData extends Diagnosticable {
9595
@required this.nextNodeId,
9696
@required this.rect,
9797
@required this.textSelection,
98+
@required this.scrollPosition,
99+
@required this.scrollExtentMax,
100+
@required this.scrollExtentMin,
98101
this.tags,
99102
this.transform,
100103
}) : assert(flags != null),
@@ -156,6 +159,38 @@ class SemanticsData extends Diagnosticable {
156159
/// if this node represents a text field.
157160
final TextSelection textSelection;
158161

162+
/// Indicates the current scrolling position in logical pixels if the node is
163+
/// scrollable.
164+
///
165+
/// The properties [scrollExtentMin] and [scrollExtentMax] indicate the valid
166+
/// in-range values for this property. The value for [scrollPosition] may
167+
/// (temporarily) be outside that range, e.g. during an overscroll.
168+
///
169+
/// See also:
170+
///
171+
/// * [ScrollPosition.pixels], from where this value is usually taken.
172+
final double scrollPosition;
173+
174+
/// Indicates the maximum in-range value for [scrollPosition] if the node is
175+
/// scrollable.
176+
///
177+
/// This value may be infinity if the scroll is unbound.
178+
///
179+
/// See also:
180+
///
181+
/// * [ScrollPosition.maxScrollExtent], from where this value is usually taken.
182+
final double scrollExtentMax;
183+
184+
/// Indicates the mimimum in-range value for [scrollPosition] if the node is
185+
/// scrollable.
186+
///
187+
/// This value may be infinity if the scroll is unbound.
188+
///
189+
/// See also:
190+
///
191+
/// * [ScrollPosition.minScrollExtent], from where this value is usually taken.
192+
final double scrollExtentMin;
193+
159194
/// The bounding box for this node in its coordinate system.
160195
final Rect rect;
161196

@@ -204,7 +239,10 @@ class SemanticsData extends Diagnosticable {
204239
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
205240
properties.add(new IntProperty('nextNodeId', nextNodeId, defaultValue: null));
206241
if (textSelection?.isValid == true)
207-
properties.add(new MessageProperty('text selection', '[${textSelection.start}, ${textSelection.end}]'));
242+
properties.add(new MessageProperty('textSelection', '[${textSelection.start}, ${textSelection.end}]'));
243+
properties.add(new DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null));
244+
properties.add(new DoubleProperty('scrollPosition', scrollPosition, defaultValue: null));
245+
properties.add(new DoubleProperty('scrollExtentMax', scrollExtentMax, defaultValue: null));
208246
}
209247

210248
@override
@@ -224,11 +262,14 @@ class SemanticsData extends Diagnosticable {
224262
&& typedOther.rect == rect
225263
&& setEquals(typedOther.tags, tags)
226264
&& typedOther.textSelection == textSelection
265+
&& typedOther.scrollPosition == scrollPosition
266+
&& typedOther.scrollExtentMax == scrollExtentMax
267+
&& typedOther.scrollExtentMin == scrollExtentMin
227268
&& typedOther.transform == transform;
228269
}
229270

230271
@override
231-
int get hashCode => ui.hashValues(flags, actions, label, value, increasedValue, decreasedValue, hint, textDirection, nextNodeId, rect, tags, textSelection, transform);
272+
int get hashCode => ui.hashValues(flags, actions, label, value, increasedValue, decreasedValue, hint, textDirection, nextNodeId, rect, tags, textSelection, scrollPosition, scrollExtentMax, scrollExtentMin, transform);
232273
}
233274

234275
class _SemanticsDiagnosticableNode extends DiagnosticableNode<SemanticsNode> {
@@ -915,6 +956,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
915956
_textDirection != config.textDirection ||
916957
_sortOrder != config._sortOrder ||
917958
_textSelection != config._textSelection ||
959+
_scrollPosition != config._scrollPosition ||
960+
_scrollExtentMax != config._scrollExtentMax ||
961+
_scrollExtentMin != config._scrollExtentMin ||
918962
_actionsAsBits != config._actionsAsBits ||
919963
_mergeAllDescendantsIntoThisNode != config.isMergingSemanticsOfDescendants;
920964
}
@@ -1011,6 +1055,42 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
10111055
TextSelection get textSelection => _textSelection;
10121056
TextSelection _textSelection;
10131057

1058+
/// Indicates the current scrolling position in logical pixels if the node is
1059+
/// scrollable.
1060+
///
1061+
/// The properties [scrollExtentMin] and [scrollExtentMax] indicate the valid
1062+
/// in-range values for this property. The value for [scrollPosition] may
1063+
/// (temporarily) be outside that range, e.g. during an overscroll.
1064+
///
1065+
/// See also:
1066+
///
1067+
/// * [ScrollPosition.pixels], from where this value is usually taken.
1068+
double get scrollPosition => _scrollPosition;
1069+
double _scrollPosition;
1070+
1071+
1072+
/// Indicates the maximum in-range value for [scrollPosition] if the node is
1073+
/// scrollable.
1074+
///
1075+
/// This value may be infinity if the scroll is unbound.
1076+
///
1077+
/// See also:
1078+
///
1079+
/// * [ScrollPosition.maxScrollExtent], from where this value is usually taken.
1080+
double get scrollExtentMax => _scrollExtentMax;
1081+
double _scrollExtentMax;
1082+
1083+
/// Indicates the mimimum in-range value for [scrollPosition] if the node is
1084+
/// scrollable.
1085+
///
1086+
/// This value may be infinity if the scroll is unbound.
1087+
///
1088+
/// See also:
1089+
///
1090+
/// * [ScrollPosition.minScrollExtent] from where this value is usually taken.
1091+
double get scrollExtentMin => _scrollExtentMin;
1092+
double _scrollExtentMin;
1093+
10141094
bool _canPerformAction(SemanticsAction action) => _actions.containsKey(action);
10151095

10161096
static final SemanticsConfiguration _kEmptyConfig = new SemanticsConfiguration();
@@ -1043,6 +1123,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
10431123
_actions = new Map<SemanticsAction, _SemanticsActionHandler>.from(config._actions);
10441124
_actionsAsBits = config._actionsAsBits;
10451125
_textSelection = config._textSelection;
1126+
_scrollPosition = config._scrollPosition;
1127+
_scrollExtentMax = config._scrollExtentMax;
1128+
_scrollExtentMin = config._scrollExtentMin;
10461129
_mergeAllDescendantsIntoThisNode = config.isMergingSemanticsOfDescendants;
10471130
_replaceChildren(childrenInInversePaintOrder ?? const <SemanticsNode>[]);
10481131

@@ -1074,6 +1157,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
10741157
int nextNodeId = _nextNodeId;
10751158
Set<SemanticsTag> mergedTags = tags == null ? null : new Set<SemanticsTag>.from(tags);
10761159
TextSelection textSelection = _textSelection;
1160+
double scrollPosition = _scrollPosition;
1161+
double scrollExtentMax = _scrollExtentMax;
1162+
double scrollExtentMin = _scrollExtentMin;
10771163

10781164
if (mergeAllDescendantsIntoThisNode) {
10791165
_visitDescendants((SemanticsNode node) {
@@ -1083,6 +1169,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
10831169
textDirection ??= node._textDirection;
10841170
nextNodeId ??= node._nextNodeId;
10851171
textSelection ??= node._textSelection;
1172+
scrollPosition ??= node._scrollPosition;
1173+
scrollExtentMax ??= node._scrollExtentMax;
1174+
scrollExtentMin ??= node._scrollExtentMin;
10861175
if (value == '' || value == null)
10871176
value = node._value;
10881177
if (increasedValue == '' || increasedValue == null)
@@ -1123,6 +1212,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
11231212
transform: transform,
11241213
tags: mergedTags,
11251214
textSelection: textSelection,
1215+
scrollPosition: scrollPosition,
1216+
scrollExtentMax: scrollExtentMax,
1217+
scrollExtentMin: scrollExtentMin,
11261218
);
11271219
}
11281220

@@ -1160,6 +1252,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
11601252
nextNodeId: data.nextNodeId,
11611253
textSelectionBase: data.textSelection != null ? data.textSelection.baseOffset : -1,
11621254
textSelectionExtent: data.textSelection != null ? data.textSelection.extentOffset : -1,
1255+
scrollPosition: data.scrollPosition != null ? data.scrollPosition : double.nan,
1256+
scrollExtentMax: data.scrollExtentMax != null ? data.scrollExtentMax : double.nan,
1257+
scrollExtentMin: data.scrollExtentMin != null ? data.scrollExtentMin : double.nan,
11631258
transform: data.transform?.storage ?? _kIdentityTransform,
11641259
children: children,
11651260
);
@@ -1232,6 +1327,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
12321327
properties.add(new DiagnosticsProperty<SemanticsSortOrder>('sortOrder', sortOrder, defaultValue: null));
12331328
if (_textSelection?.isValid == true)
12341329
properties.add(new MessageProperty('text selection', '[${_textSelection.start}, ${_textSelection.end}]'));
1330+
properties.add(new DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null));
1331+
properties.add(new DoubleProperty('scrollPosition', scrollPosition, defaultValue: null));
1332+
properties.add(new DoubleProperty('scrollExtentMax', scrollExtentMax, defaultValue: null));
12351333
}
12361334

12371335
/// Returns a string representation of this node and its descendants.
@@ -2088,6 +2186,56 @@ class SemanticsConfiguration {
20882186
_hasBeenAnnotated = true;
20892187
}
20902188

2189+
/// Indicates the current scrolling position in logical pixels if the node is
2190+
/// scrollable.
2191+
///
2192+
/// The properties [scrollExtentMin] and [scrollExtentMax] indicate the valid
2193+
/// in-range values for this property. The value for [scrollPosition] may
2194+
/// (temporarily) be outside that range, e.g. during an overscroll.
2195+
///
2196+
/// See also:
2197+
///
2198+
/// * [ScrollPosition.pixels], from where this value is usually taken.
2199+
double get scrollPosition => _scrollPosition;
2200+
double _scrollPosition;
2201+
set scrollPosition(double value) {
2202+
assert(value != null);
2203+
_scrollPosition = value;
2204+
_hasBeenAnnotated = true;
2205+
}
2206+
2207+
/// Indicates the maximum in-range value for [scrollPosition] if the node is
2208+
/// scrollable.
2209+
///
2210+
/// This value may be infinity if the scroll is unbound.
2211+
///
2212+
/// See also:
2213+
///
2214+
/// * [ScrollPosition.maxScrollExtent], from where this value is usually taken.
2215+
double get scrollExtentMax => _scrollExtentMax;
2216+
double _scrollExtentMax;
2217+
set scrollExtentMax(double value) {
2218+
assert(value != null);
2219+
_scrollExtentMax = value;
2220+
_hasBeenAnnotated = true;
2221+
}
2222+
2223+
/// Indicates the minimum in-range value for [scrollPosition] if the node is
2224+
/// scrollable.
2225+
///
2226+
/// This value may be infinity if the scroll is unbound.
2227+
///
2228+
/// See also:
2229+
///
2230+
/// * [ScrollPosition.minScrollExtent], from where this value is usually taken.
2231+
double get scrollExtentMin => _scrollExtentMin;
2232+
double _scrollExtentMin;
2233+
set scrollExtentMin(double value) {
2234+
assert(value != null);
2235+
_scrollExtentMin = value;
2236+
_hasBeenAnnotated = true;
2237+
}
2238+
20912239
// TAGS
20922240

20932241
/// The set of tags that this configuration wants to add to all child
@@ -2171,6 +2319,9 @@ class SemanticsConfiguration {
21712319
_actionsAsBits |= other._actionsAsBits;
21722320
_flags |= other._flags;
21732321
_textSelection ??= other._textSelection;
2322+
_scrollPosition ??= other._scrollPosition;
2323+
_scrollExtentMax ??= other._scrollExtentMax;
2324+
_scrollExtentMin ??= other._scrollExtentMin;
21742325

21752326
textDirection ??= other.textDirection;
21762327
_sortOrder = _sortOrder?.merge(other._sortOrder);
@@ -2214,6 +2365,9 @@ class SemanticsConfiguration {
22142365
.._flags = _flags
22152366
.._tagsForChildren = _tagsForChildren
22162367
.._textSelection = _textSelection
2368+
.._scrollPosition = _scrollPosition
2369+
.._scrollExtentMax = _scrollExtentMax
2370+
.._scrollExtentMin = _scrollExtentMin
22172371
.._actionsAsBits = _actionsAsBits
22182372
.._actions.addAll(_actions);
22192373
}

packages/flutter/lib/src/semantics/semantics_event.dart

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'package:flutter/foundation.dart';
65
import 'package:flutter/painting.dart';
76

87
/// An event sent by the application to notify interested listeners that
@@ -53,75 +52,6 @@ abstract class SemanticsEvent {
5352
}
5453
}
5554

56-
/// Notifies that a scroll action has been completed.
57-
///
58-
/// This event translates into a `AccessibilityEvent.TYPE_VIEW_SCROLLED` on
59-
/// Android and a `UIAccessibilityPageScrolledNotification` on iOS. It is
60-
/// processed by the accessibility systems of the operating system to provide
61-
/// additional feedback to the user about the state of a scrollable view (e.g.
62-
/// on Android, a ping sound is played to indicate that a scroll action was
63-
/// successful).
64-
class ScrollCompletedSemanticsEvent extends SemanticsEvent {
65-
/// Creates a [ScrollCompletedSemanticsEvent].
66-
///
67-
/// This event should be sent after a scroll action is completed. It is
68-
/// interpreted by assistive technologies to provide additional feedback about
69-
/// the just completed scroll action to the user.
70-
///
71-
/// The parameters [axis], [pixels], [minScrollExtent], and [maxScrollExtent] are
72-
/// required and may not be null.
73-
ScrollCompletedSemanticsEvent({
74-
@required this.axis,
75-
@required this.pixels,
76-
@required this.maxScrollExtent,
77-
@required this.minScrollExtent
78-
}) : assert(axis != null),
79-
assert(pixels != null),
80-
assert(maxScrollExtent != null),
81-
assert(minScrollExtent != null),
82-
super('scroll');
83-
84-
/// The axis in which the scroll view was scrolled.
85-
///
86-
/// See also [ScrollPosition.axis].
87-
final Axis axis;
88-
89-
/// The current scroll position, in logical pixels.
90-
///
91-
/// See also [ScrollPosition.pixels].
92-
final double pixels;
93-
94-
/// The minimum in-range value for [pixels].
95-
///
96-
/// See also [ScrollPosition.minScrollExtent].
97-
final double minScrollExtent;
98-
99-
/// The maximum in-range value for [pixels].
100-
///
101-
/// See also [ScrollPosition.maxScrollExtent].
102-
final double maxScrollExtent;
103-
104-
@override
105-
Map<String, dynamic> getDataMap() {
106-
final Map<String, dynamic> map = <String, dynamic>{
107-
'pixels': pixels.clamp(minScrollExtent, maxScrollExtent),
108-
'minScrollExtent': minScrollExtent,
109-
'maxScrollExtent': maxScrollExtent,
110-
};
111-
112-
switch (axis) {
113-
case Axis.horizontal:
114-
map['axis'] = 'h';
115-
break;
116-
case Axis.vertical:
117-
map['axis'] = 'v';
118-
break;
119-
}
120-
121-
return map;
122-
}
123-
}
124-
12555
/// An event for a semantic announcement.
12656
///
12757
/// This should be used for announcement that are not seamlessly announced by

packages/flutter/lib/src/widgets/scroll_metrics.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,15 @@ abstract class ScrollMetrics {
5959
/// The minimum in-range value for [pixels].
6060
///
6161
/// The actual [pixels] value might be [outOfRange].
62+
///
63+
/// This value can be negative infinity, if the scroll is unbounded.
6264
double get minScrollExtent;
6365

6466
/// The maximum in-range value for [pixels].
6567
///
6668
/// The actual [pixels] value might be [outOfRange].
69+
///
70+
/// This value can be infinity, if the scroll is unbounded.
6771
double get maxScrollExtent;
6872

6973
/// The current scroll position, in logical pixels along the [axisDirection].
@@ -140,4 +144,4 @@ class FixedScrollMetrics extends ScrollMetrics {
140144
String toString() {
141145
return '$runtimeType(${extentBefore.toStringAsFixed(1)}..[${extentInside.toStringAsFixed(1)}]..${extentAfter.toStringAsFixed(1)})';
142146
}
143-
}
147+
}

0 commit comments

Comments
 (0)