mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
Handles hidden by keyboard (#32838)
Extra space when scrolling to selected input so that the selection caret is visible.
This commit is contained in:
parent
04015b987b
commit
fce54ae685
|
@ -3,6 +3,7 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -1120,10 +1121,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
|
||||
double scrollOffset = _scrollController.offset;
|
||||
final double viewportExtent = _scrollController.position.viewportDimension;
|
||||
if (caretStart < 0.0) // cursor before start of bounds
|
||||
if (caretStart < 0.0) { // cursor before start of bounds
|
||||
scrollOffset += caretStart;
|
||||
else if (caretEnd >= viewportExtent) // cursor after end of bounds
|
||||
} else if (caretEnd >= viewportExtent) { // cursor after end of bounds
|
||||
scrollOffset += caretEnd - viewportExtent;
|
||||
}
|
||||
return scrollOffset;
|
||||
}
|
||||
|
||||
|
@ -1271,12 +1273,32 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
curve: _caretAnimationCurve,
|
||||
);
|
||||
final Rect newCaretRect = _getCaretRectAtScrollOffset(_currentCaretRect, scrollOffsetForCaret);
|
||||
// Enlarge newCaretRect by scrollPadding to ensure that caret is not positioned directly at the edge after scrolling.
|
||||
// Enlarge newCaretRect by scrollPadding to ensure that caret is not
|
||||
// positioned directly at the edge after scrolling.
|
||||
double bottomSpacing = widget.scrollPadding.bottom;
|
||||
if (_selectionOverlay?.selectionControls != null) {
|
||||
final double handleHeight = _selectionOverlay.selectionControls
|
||||
.getHandleSize(renderEditable.preferredLineHeight).height;
|
||||
final double interactiveHandleHeight = math.max(
|
||||
handleHeight,
|
||||
kMinInteractiveSize,
|
||||
);
|
||||
final Offset anchor = _selectionOverlay.selectionControls
|
||||
.getHandleAnchor(
|
||||
TextSelectionHandleType.collapsed,
|
||||
renderEditable.preferredLineHeight,
|
||||
);
|
||||
final double handleCenter = handleHeight / 2 - anchor.dy;
|
||||
bottomSpacing = math.max(
|
||||
handleCenter + interactiveHandleHeight / 2,
|
||||
bottomSpacing,
|
||||
);
|
||||
}
|
||||
final Rect inflatedRect = Rect.fromLTRB(
|
||||
newCaretRect.left - widget.scrollPadding.left,
|
||||
newCaretRect.top - widget.scrollPadding.top,
|
||||
newCaretRect.right + widget.scrollPadding.right,
|
||||
newCaretRect.bottom + widget.scrollPadding.bottom,
|
||||
newCaretRect.bottom + bottomSpacing,
|
||||
);
|
||||
_editableKey.currentContext.findRenderObject().showOnScreen(
|
||||
rect: inflatedRect,
|
||||
|
|
|
@ -2626,6 +2626,34 @@ void main() {
|
|||
debugDefaultTargetPlatformOverride = null;
|
||||
});
|
||||
|
||||
testWidgets('when CupertinoTextField would be blocked by keyboard, it is shown with enough space for the selection handle', (WidgetTester tester) async {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
await tester.pumpWidget(CupertinoApp(
|
||||
theme: const CupertinoThemeData(),
|
||||
home: Center(
|
||||
child: ListView(
|
||||
controller: scrollController,
|
||||
children: <Widget>[
|
||||
Container(height: 585), // Push field almost off screen.
|
||||
CupertinoTextField(controller: controller),
|
||||
Container(height: 1000),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Tap the TextField to put the cursor into it and bring it into view.
|
||||
expect(scrollController.offset, 0.0);
|
||||
await tester.tap(find.byType(CupertinoTextField));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The ListView has scrolled to keep the TextField and cursor handle
|
||||
// visible.
|
||||
expect(scrollController.offset, 26.0);
|
||||
});
|
||||
|
||||
testWidgets('disabled state golden', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
|
|
|
@ -6581,4 +6581,34 @@ void main() {
|
|||
await tester.tapAt(handlePos, pointer: 7);
|
||||
expect(editableText.selectionOverlay.toolbarIsVisible, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('when TextField would be blocked by keyboard, it is shown with enough space for the selection handle', (WidgetTester tester) async {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(),
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: ListView(
|
||||
controller: scrollController,
|
||||
children: <Widget>[
|
||||
Container(height: 579), // Push field almost off screen.
|
||||
TextField(controller: controller),
|
||||
Container(height: 1000),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Tap the TextField to put the cursor into it and bring it into view.
|
||||
expect(scrollController.offset, 0.0);
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The ListView has scrolled to keep the TextField and cursor handle
|
||||
// visible.
|
||||
expect(scrollController.offset, 44.0);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue