Skip to content

Commit 34eb408

Browse files
authored
[SuperEditor] - Ignore spurious metrics change notification (#2127)
1 parent 1676e43 commit 34eb408

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

super_editor/lib/src/default_editor/document_gestures_touch_android.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:ui';
12
import 'dart:async';
23

34
import 'package:flutter/foundation.dart';
@@ -471,6 +472,10 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt
471472

472473
bool _isCaretDragInProgress = false;
473474

475+
// Cached view metrics to ignore unnecessary didChangeMetrics calls.
476+
Size? _lastSize;
477+
ViewPadding? _lastInsets;
478+
474479
@override
475480
void initState() {
476481
super.initState();
@@ -494,6 +499,10 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt
494499
void didChangeDependencies() {
495500
super.didChangeDependencies();
496501

502+
final view = View.of(context);
503+
_lastSize = view.physicalSize;
504+
_lastInsets = view.viewInsets;
505+
497506
_controlsController = SuperEditorAndroidControlsScope.rootOf(context);
498507

499508
_ancestorScrollPosition = context.findAncestorScrollableWithVerticalScroll?.position;
@@ -529,6 +538,20 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt
529538

530539
@override
531540
void didChangeMetrics() {
541+
// It is possible to get the notification even though the metrics for view are same.
542+
final view = View.of(context);
543+
final size = view.physicalSize;
544+
final insets = view.viewInsets;
545+
if (size == _lastSize &&
546+
_lastInsets?.left == insets.left &&
547+
_lastInsets?.right == insets.right &&
548+
_lastInsets?.top == insets.top &&
549+
_lastInsets?.bottom == insets.bottom) {
550+
return;
551+
}
552+
_lastSize = size;
553+
_lastInsets = insets;
554+
532555
// The available screen dimensions may have changed, e.g., due to keyboard
533556
// appearance/disappearance. Reflow the layout. Use a post-frame callback
534557
// to give the rest of the UI a chance to reflow, first.

super_editor/lib/src/default_editor/document_gestures_touch_ios.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:ui';
12
import 'dart:async';
23

34
import 'package:flutter/foundation.dart';
@@ -317,6 +318,10 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
317318
bool get _isLongPressInProgress => _longPressStrategy != null;
318319
IosLongPressSelectionStrategy? _longPressStrategy;
319320

321+
// Cached view metrics to ignore unnecessary didChangeMetrics calls.
322+
Size? _lastSize;
323+
ViewPadding? _lastInsets;
324+
320325
@override
321326
void initState() {
322327
super.initState();
@@ -344,6 +349,10 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
344349
void didChangeDependencies() {
345350
super.didChangeDependencies();
346351

352+
final view = View.of(context);
353+
_lastSize = view.physicalSize;
354+
_lastInsets = view.viewInsets;
355+
347356
if (_controlsController != null) {
348357
_controlsController!.floatingCursorController.removeListener(_floatingCursorListener);
349358
_controlsController!.floatingCursorController.cursorGeometryInViewport
@@ -407,6 +416,21 @@ class _IosDocumentTouchInteractorState extends State<IosDocumentTouchInteractor>
407416

408417
@override
409418
void didChangeMetrics() {
419+
// DidChangeMetrics is sometimes called even when metrics doesn't change
420+
// (i.e. on iOS with keyboard visible)
421+
final view = View.of(context);
422+
final size = view.physicalSize;
423+
final insets = view.viewInsets;
424+
if (size == _lastSize &&
425+
_lastInsets?.left == insets.left &&
426+
_lastInsets?.right == insets.right &&
427+
_lastInsets?.top == insets.top &&
428+
_lastInsets?.bottom == insets.bottom) {
429+
return;
430+
}
431+
_lastSize = size;
432+
_lastInsets = insets;
433+
410434
// The available screen dimensions may have changed, e.g., due to keyboard
411435
// appearance/disappearance. Ensure the extent is still visible. Use a
412436
// post-frame callback to give the rest of the UI a chance to reflow, first.

super_editor/test/super_editor/supereditor_scrolling_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,26 @@ void main() {
14411441
findsNothing,
14421442
);
14431443
});
1444+
1445+
testWidgetsOnMobile('spurious metrics change is ignored', (tester) async {
1446+
final scrollController = ScrollController();
1447+
await tester //
1448+
.createDocument()
1449+
.withLongDoc()
1450+
.withEditorSize(const Size(300, 300))
1451+
.withScrollController(scrollController)
1452+
.pump();
1453+
await tester.tapInParagraph('1', 0);
1454+
final gesture = await tester.startGesture(Offset(100, 100), kind: PointerDeviceKind.touch);
1455+
await gesture.moveBy(Offset(0, -100));
1456+
await tester.pumpAndSettle();
1457+
final pixels = scrollController.position.pixels;
1458+
// This should not change scroll position.
1459+
WidgetsBinding.instance.handleMetricsChanged();
1460+
await Future.microtask(() {});
1461+
await tester.pump();
1462+
expect(scrollController.position.pixels, pixels);
1463+
});
14441464
});
14451465
}
14461466

0 commit comments

Comments
 (0)