From 3b28f7ffffc58ed2d37f4438a13df8ed3c118817 Mon Sep 17 00:00:00 2001 From: Amir Panahandeh Date: Wed, 27 Mar 2024 11:37:39 +0330 Subject: [PATCH] Scroll to selection after keyboard opened (#285) --- packages/fleather/lib/src/widgets/editor.dart | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/fleather/lib/src/widgets/editor.dart b/packages/fleather/lib/src/widgets/editor.dart index a1cfbb00..e2af25ad 100644 --- a/packages/fleather/lib/src/widgets/editor.dart +++ b/packages/fleather/lib/src/widgets/editor.dart @@ -1499,6 +1499,7 @@ class RawEditorState extends EditorState if (_hasFocus) { // Listen for changing viewInsets, which indicates keyboard showing up. WidgetsBinding.instance.addObserver(this); + _lastBottomViewInset = View.of(context).viewInsets.bottom; _showCaretOnScreen(); // _lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom; // if (!_value.selection.isValid) { @@ -1539,7 +1540,7 @@ class RawEditorState extends EditorState bool _showCaretOnScreenScheduled = false; - void _showCaretOnScreen() { + void _showCaretOnScreen([bool withAnimation = true]) { if (!widget.showCursor || _showCaretOnScreenScheduled) { return; } @@ -1564,11 +1565,16 @@ class RawEditorState extends EditorState ); if (offset != null) { - _scrollController.animateTo( - math.min(offset, _scrollController.position.maxScrollExtent), - duration: _caretAnimationDuration, - curve: _caretAnimationCurve, - ); + if (withAnimation) { + _scrollController.animateTo( + math.min(offset, _scrollController.position.maxScrollExtent), + duration: _caretAnimationDuration, + curve: _caretAnimationCurve, + ); + } else { + _scrollController.jumpTo( + math.min(offset, _scrollController.position.maxScrollExtent)); + } } }); } @@ -1594,6 +1600,28 @@ class RawEditorState extends EditorState } } + late double _lastBottomViewInset; + + @override + void didChangeMetrics() { + super.didChangeMetrics(); + if (!mounted) { + return; + } + final bottomViewInset = View.of(context).viewInsets.bottom; + if (_lastBottomViewInset != bottomViewInset) { + SchedulerBinding.instance.addPostFrameCallback((_) { + _selectionOverlay?.updateForScroll(); + }); + if (_lastBottomViewInset < bottomViewInset) { + // Because the metrics change signal from engine will come here every frame + // (on both iOS and Android). So we don't need to show caret with animation. + _showCaretOnScreen(false); + } + } + _lastBottomViewInset = bottomViewInset; + } + // On MacOS some actions are sent as selectors. We need to manually find the right Action and invoke it. // Ref: https://github.com/flutter/flutter/blob/3.7.0/packages/flutter/lib/src/widgets/editable_text.dart#L3731 @override