Report ScrollNotification depth (#4992)
This commit is contained in:
parent
6cc6c95b79
commit
7beedd7a95
@ -188,6 +188,8 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _handleScrollNotification(ScrollNotification notification) {
|
bool _handleScrollNotification(ScrollNotification notification) {
|
||||||
|
if (notification.depth != 0)
|
||||||
|
return false;
|
||||||
if (config.scrollableKey == null || config.scrollableKey == notification.scrollable.config.key) {
|
if (config.scrollableKey == null || config.scrollableKey == notification.scrollable.config.key) {
|
||||||
final ScrollableState scrollable = notification.scrollable;
|
final ScrollableState scrollable = notification.scrollable;
|
||||||
switch(notification.kind) {
|
switch(notification.kind) {
|
||||||
|
@ -9,18 +9,22 @@ typedef bool NotificationListenerCallback<T extends Notification>(T notification
|
|||||||
|
|
||||||
/// A notification that can bubble up the widget tree.
|
/// A notification that can bubble up the widget tree.
|
||||||
abstract class Notification {
|
abstract class Notification {
|
||||||
|
/// Applied to each ancestor of the [dispatch] target. Dispatches this
|
||||||
|
/// Notification to ancestor [NotificationListener] widgets.
|
||||||
|
bool visitAncestor(Element element) {
|
||||||
|
if (element is StatelessElement &&
|
||||||
|
element.widget is NotificationListener<Notification>) {
|
||||||
|
final NotificationListener<Notification> widget = element.widget;
|
||||||
|
if (widget._dispatch(this)) // that function checks the type dynamically
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Start bubbling this notification at the given build context.
|
/// Start bubbling this notification at the given build context.
|
||||||
void dispatch(BuildContext target) {
|
void dispatch(BuildContext target) {
|
||||||
assert(target != null); // Only call dispatch if the widget's State is still mounted.
|
assert(target != null); // Only call dispatch if the widget's State is still mounted.
|
||||||
target.visitAncestorElements((Element element) {
|
target.visitAncestorElements(visitAncestor);
|
||||||
if (element is StatelessElement &&
|
|
||||||
element.widget is NotificationListener<Notification>) {
|
|
||||||
final NotificationListener<Notification> widget = element.widget;
|
|
||||||
if (widget._dispatch(this)) // that function checks the type dynamically
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -710,7 +710,11 @@ enum ScrollNotificationKind {
|
|||||||
ended
|
ended
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates that a descendant scrollable has scrolled.
|
/// Indicates that a scrollable descendant is scrolling.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [NotificationListener]
|
||||||
class ScrollNotification extends Notification {
|
class ScrollNotification extends Notification {
|
||||||
/// Creates a notification about scrolling.
|
/// Creates a notification about scrolling.
|
||||||
ScrollNotification(this.scrollable, this.kind);
|
ScrollNotification(this.scrollable, this.kind);
|
||||||
@ -720,6 +724,19 @@ class ScrollNotification extends Notification {
|
|||||||
|
|
||||||
/// The scrollable that scrolled.
|
/// The scrollable that scrolled.
|
||||||
final ScrollableState scrollable;
|
final ScrollableState scrollable;
|
||||||
|
|
||||||
|
/// The number of scrollable widgets that have already received this
|
||||||
|
/// notification. Typically listeners only respond to notifications
|
||||||
|
/// with depth = 0.
|
||||||
|
int get depth => _depth;
|
||||||
|
int _depth = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool visitAncestor(Element element) {
|
||||||
|
if (element is StatefulElement && element.state is ScrollableState)
|
||||||
|
_depth += 1;
|
||||||
|
return super.visitAncestor(element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simple scrolling widget that has a single child.
|
/// A simple scrolling widget that has a single child.
|
||||||
|
86
packages/flutter/test/widget/scroll_notification_test.dart
Normal file
86
packages/flutter/test/widget/scroll_notification_test.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2015 The Chromium 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 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Scroll notifcation basics', (WidgetTester tester) async {
|
||||||
|
ScrollNotification notification;
|
||||||
|
|
||||||
|
await tester.pumpWidget(new NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (ScrollNotification value) {
|
||||||
|
notification = value;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: new ScrollableViewport(
|
||||||
|
child: new SizedBox(height: 1200.0)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
TestGesture gesture = await tester.startGesture(new Point(100.0, 100.0));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(notification.kind, equals(ScrollNotificationKind.started));
|
||||||
|
expect(notification.depth, equals(0));
|
||||||
|
|
||||||
|
await gesture.moveBy(new Offset(-10.0, -10.0));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(notification.kind, equals(ScrollNotificationKind.updated));
|
||||||
|
expect(notification.depth, equals(0));
|
||||||
|
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(notification.kind, equals(ScrollNotificationKind.ended));
|
||||||
|
expect(notification.depth, equals(0));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Scroll notifcation depth', (WidgetTester tester) async {
|
||||||
|
final List<ScrollNotificationKind> depth0Kinds = <ScrollNotificationKind>[];
|
||||||
|
final List<ScrollNotificationKind> depth1Kinds = <ScrollNotificationKind>[];
|
||||||
|
final List<int> depth0Values = <int>[];
|
||||||
|
final List<int> depth1Values = <int>[];
|
||||||
|
|
||||||
|
await tester.pumpWidget(new NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (ScrollNotification value) {
|
||||||
|
depth1Kinds.add(value.kind);
|
||||||
|
depth1Values.add(value.depth);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: new ScrollableViewport(
|
||||||
|
child: new SizedBox(
|
||||||
|
height: 1200.0,
|
||||||
|
child: new NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (ScrollNotification value) {
|
||||||
|
depth0Kinds.add(value.kind);
|
||||||
|
depth0Values.add(value.depth);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: new Container(
|
||||||
|
padding: const EdgeInsets.all(50.0),
|
||||||
|
child: new ScrollableViewport(child: new SizedBox(height: 1200.0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
TestGesture gesture = await tester.startGesture(new Point(100.0, 100.0));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
await gesture.moveBy(new Offset(-10.0, -10.0));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
final List<ScrollNotificationKind> kinds = <ScrollNotificationKind>[
|
||||||
|
ScrollNotificationKind.started,
|
||||||
|
ScrollNotificationKind.updated,
|
||||||
|
ScrollNotificationKind.ended
|
||||||
|
];
|
||||||
|
expect(depth0Kinds, equals(kinds));
|
||||||
|
expect(depth1Kinds, equals(kinds));
|
||||||
|
|
||||||
|
expect(depth0Values, equals(<int>[0, 0, 0]));
|
||||||
|
expect(depth1Values, equals(<int>[1, 1, 1]));
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user