Unverified Commit 4509ad59 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Do not implicitly scroll a PageView when showOnScreen is called (#19735)

Fixes #19523
parent 1b5dd718
......@@ -877,6 +877,15 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
Duration duration = Duration.zero,
Curve curve = Curves.ease,
}) {
if (!offset.allowImplicitScrolling) {
return super.showOnScreen(
descendant: descendant,
rect: rect,
duration: duration,
curve: curve,
);
}
final Rect newRect = RenderViewportBase.showInViewport(
descendant: descendant,
viewport: this,
......
......@@ -195,6 +195,15 @@ abstract class ViewportOffset extends ChangeNotifier {
/// offset direction.
ScrollDirection get userScrollDirection;
/// Whether a viewport is allowed to change [pixels] implicitly to respond to
/// a call to [RenderObject.showOnScreen].
///
/// [RenderObject.showOnScreen] is for example used to bring a text field
/// fully on screen after it has received focus. This property controls
/// whether the viewport associated with this offset is allowed to change the
/// offset's [pixels] value to fulfill such a request.
bool get allowImplicitScrolling;
@override
String toString() {
final List<String> description = <String>[];
......@@ -250,4 +259,7 @@ class _FixedViewportOffset extends ViewportOffset {
@override
ScrollDirection get userScrollDirection => ScrollDirection.idle;
@override
bool get allowImplicitScrolling => false;
}
......@@ -372,6 +372,9 @@ class PageScrollPhysics extends ScrollPhysics {
return new ScrollSpringSimulation(spring, position.pixels, target, velocity, tolerance: tolerance);
return null;
}
@override
bool get allowImplicitScrolling => false;
}
// Having this global (mutable) page controller is a bit of a hack. We need it
......
......@@ -225,6 +225,15 @@ class ScrollPhysics {
/// If null, no minimum threshold is enforced.
double get dragStartDistanceMotionThreshold => parent?.dragStartDistanceMotionThreshold;
/// Whether a viewport is allowed to change its scroll position implicitly in
/// responds to a call to [RenderObject.showOnScreen].
///
/// [RenderObject.showOnScreen] is for example used to bring a text field
/// fully on screen after it has received focus. This property controls
/// whether the viewport associated with this object is allowed to change the
/// scroll position to fulfill such a request.
bool get allowImplicitScrolling => true;
@override
String toString() {
if (parent == null)
......@@ -485,4 +494,7 @@ class NeverScrollableScrollPhysics extends ScrollPhysics {
@override
bool shouldAcceptUserOffset(ScrollMetrics position) => false;
@override
bool get allowImplicitScrolling => false;
}
......@@ -562,6 +562,9 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
@override
void jumpTo(double value);
@override
bool get allowImplicitScrolling => physics.allowImplicitScrolling;
/// Deprecated. Use [jumpTo] or a custom [ScrollPosition] instead.
@Deprecated('This will lead to bugs.')
void jumpToWithoutSettling(double value);
......
......@@ -596,6 +596,15 @@ class _RenderSingleChildViewport extends RenderBox with RenderObjectWithChildMix
Duration duration = Duration.zero,
Curve curve = Curves.ease,
}) {
if (!offset.allowImplicitScrolling) {
return super.showOnScreen(
descendant: descendant,
rect: rect,
duration: duration,
curve: curve,
);
}
final Rect newRect = RenderViewportBase.showInViewport(
descendant: descendant,
viewport: this,
......
......@@ -142,6 +142,97 @@ void main() {
expect(find.byType(EditableText), findsOneWidget);
});
testWidgets('entering text does not scroll when scrollPhysics.allowImplicitScrolling = false', (WidgetTester tester) async {
// regression test for https://github.com/flutter/flutter/issues/19523
final ScrollController scrollController = new ScrollController(initialScrollOffset: 100.0);
final TextEditingController controller = new TextEditingController();
final FocusNode focusNode = new FocusNode();
await tester.pumpWidget(new MaterialApp(
home: new Center(
child: new Container(
height: 300.0,
child: new ListView(
physics: const NoImplicitScrollPhysics(),
controller: scrollController,
children: <Widget>[
new Container(
height: 350.0,
),
new EditableText(
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
new Container(
height: 350.0,
),
],
),
),
),
));
// Focus the EditableText and scroll it off screen.
await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle();
expect(focusNode.hasFocus, isTrue);
scrollController.jumpTo(0.0);
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
expect(find.byType(EditableText), findsNothing);
// Entering text brings it not back on screen.
tester.testTextInput.enterText('Hello');
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
expect(find.byType(EditableText), findsNothing);
});
testWidgets('entering text does not scroll a sourrounding PageView', (WidgetTester tester) async {
// regression test for https://github.com/flutter/flutter/issues/19523
final TextEditingController textController = new TextEditingController();
final PageController pageController = new PageController(initialPage: 1);
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new Material(
child: new PageView(
controller: pageController,
children: <Widget>[
new Container(
color: Colors.red,
),
new Container(
child: new TextField(
controller: textController,
),
color: Colors.green,
),
new Container(
color: Colors.red,
),
],
),
),
));
await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle();
expect(textController.text, '');
tester.testTextInput.enterText('H');
final int frames = await tester.pumpAndSettle();
// The text input should not trigger any animations, which would indicate
// that the surrounding PageView is incorrectly scrolling back-and-forth.
expect(frames, 1);
expect(textController.text, 'H');
});
testWidgets('focused multi-line editable scrolls caret back into view when typing', (WidgetTester tester) async {
final ScrollController scrollController = new ScrollController();
final TextEditingController controller = new TextEditingController();
......@@ -234,3 +325,15 @@ void main() {
expect(find.byKey(container), findsNothing);
});
}
class NoImplicitScrollPhysics extends AlwaysScrollableScrollPhysics {
const NoImplicitScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);
@override
bool get allowImplicitScrolling => false;
@override
NoImplicitScrollPhysics applyTo(ScrollPhysics ancestor) {
return new NoImplicitScrollPhysics(parent: buildParent(ancestor));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment