blob: 688f61c876c0241e7cce6c89b3d4566d62b7925e [file] [log] [blame]
// Copyright 2014 The Flutter 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/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/services.dart';
void main() {
const TextStyle textStyle = TextStyle();
const Color cursorColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00);
testWidgets('tapping on a partly visible editable brings it fully on screen', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(MaterialApp(
home: Center(
child: Container(
height: 300.0,
child: ListView(
controller: scrollController,
children: <Widget>[
EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
Container(
height: 350.0,
),
],
),
),
),
));
// Scroll the EditableText half off screen.
final RenderBox render = tester.renderObject(find.byType(EditableText));
scrollController.jumpTo(render.size.height / 2);
await tester.pumpAndSettle();
expect(scrollController.offset, render.size.height / 2);
await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
});
testWidgets('tapping on a partly visible editable brings it fully on screen with scrollInsets', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(MaterialApp(
home: Center(
child: Container(
height: 300.0,
child: ListView(
controller: scrollController,
children: <Widget>[
Container(
height: 200.0,
),
EditableText(
backgroundCursorColor: Colors.grey,
scrollPadding: const EdgeInsets.all(50.0),
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
Container(
height: 850.0,
),
],
),
),
),
));
// Scroll the EditableText half off screen.
final RenderBox render = tester.renderObject(find.byType(EditableText));
scrollController.jumpTo(200 + render.size.height / 2);
await tester.pumpAndSettle();
expect(scrollController.offset, 200 + render.size.height / 2);
await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle();
// Container above the text is 200 in height, the scrollInsets are 50
// Tolerance of 5 units (The actual value was 152.0 in the current tests instead of 150.0)
expect(scrollController.offset, lessThan(200.0 - 50.0 + 5.0));
expect(scrollController.offset, greaterThan(200.0 - 50.0 - 5.0));
});
testWidgets('editable comes back on screen when entering text while it is off-screen', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController(initialScrollOffset: 100.0);
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(MaterialApp(
home: Center(
child: Container(
height: 300.0,
child: ListView(
controller: scrollController,
children: <Widget>[
Container(
height: 350.0,
),
EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
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 back on screen.
tester.testTextInput.enterText('Hello');
await tester.pumpAndSettle();
expect(scrollController.offset, greaterThan(0.0));
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 = ScrollController(initialScrollOffset: 100.0);
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(MaterialApp(
home: Center(
child: Container(
height: 300.0,
child: ListView(
physics: const NoImplicitScrollPhysics(),
controller: scrollController,
children: <Widget>[
Container(
height: 350.0,
),
EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
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 = TextEditingController();
final PageController pageController = PageController(initialPage: 1);
await tester.pumpWidget(
MaterialApp(
home: MediaQuery(
data: const MediaQueryData(devicePixelRatio: 1.0),
child: Directionality(
textDirection: TextDirection.ltr,
child: Material(
child: PageView(
controller: pageController,
children: <Widget>[
Container(
color: Colors.red,
),
Container(
child: TextField(
controller: textController,
),
color: Colors.green,
),
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 = ScrollController();
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
controller.text = 'Start\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nEnd';
await tester.pumpWidget(MaterialApp(
home: Center(
child: Container(
height: 300.0,
child: ListView(
controller: scrollController,
children: <Widget>[
EditableText(
backgroundCursorColor: Colors.grey,
maxLines: null, // multiline
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
],
),
),
),
));
// Bring keyboard up and verify that end of EditableText is not on screen.
await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle();
scrollController.jumpTo(0.0);
await tester.pumpAndSettle();
final RenderBox render = tester.renderObject(find.byType(EditableText));
expect(render.size.height, greaterThan(500.0));
expect(scrollController.offset, 0.0);
// Enter text at end, which is off-screen.
final String textToEnter = '${controller.text} HELLO';
tester.testTextInput.updateEditingValue(TextEditingValue(
text: textToEnter,
selection: TextSelection.collapsed(offset: textToEnter.length),
));
await tester.pumpAndSettle();
// Caret scrolls into view.
expect(find.byType(EditableText), findsOneWidget);
expect(render.size.height, greaterThan(500.0));
expect(scrollController.offset, greaterThan(0.0));
});
testWidgets('scrolls into view with scrollInserts after the keyboard pops up', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
const Key container = Key('container');
await tester.pumpWidget(MaterialApp(
home: Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 300.0,
child: ListView(
controller: scrollController,
children: <Widget>[
Container(
key: container,
height: 200.0,
),
EditableText(
backgroundCursorColor: Colors.grey,
scrollPadding: const EdgeInsets.only(bottom: 300.0),
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
Container(
height: 400.0,
),
],
),
),
),
));
expect(scrollController.offset, 0.0);
await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle();
expect(scrollController.offset, greaterThan(0.0));
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 NoImplicitScrollPhysics(parent: buildParent(ancestor));
}
}