From c4df6b6d09a2dc8fcaae341dc0cbefe675626d20 Mon Sep 17 00:00:00 2001 From: xubaolin Date: Tue, 2 Aug 2022 12:37:04 +0800 Subject: [PATCH] [SelectionOverlay]Move the debug statement to the scope of the assertion. (#108508) --- .../api/lib/widgets/overlay/overlay.0.dart | 2 +- .../lib/src/cupertino/context_menu.dart | 2 +- .../src/material/bottom_navigation_bar.dart | 2 +- .../lib/src/material/range_slider.dart | 2 +- packages/flutter/lib/src/material/slider.dart | 2 +- .../flutter/lib/src/material/tooltip.dart | 2 +- .../flutter/lib/src/widgets/autocomplete.dart | 2 +- .../flutter/lib/src/widgets/drag_target.dart | 3 +- .../lib/src/widgets/reorderable_list.dart | 6 ++-- .../lib/src/widgets/selectable_region.dart | 3 +- .../lib/src/widgets/text_selection.dart | 12 ++----- .../test/widgets/selectable_text_test.dart | 31 +++++++++++++++++++ 12 files changed, 48 insertions(+), 21 deletions(-) diff --git a/examples/api/lib/widgets/overlay/overlay.0.dart b/examples/api/lib/widgets/overlay/overlay.0.dart index b90d0f0928..c49ce87c8e 100644 --- a/examples/api/lib/widgets/overlay/overlay.0.dart +++ b/examples/api/lib/widgets/overlay/overlay.0.dart @@ -132,7 +132,7 @@ class _OverlayExampleState extends State { ); // Add the OverlayEntry to the Overlay. - Overlay.of(context)!.insert(overlayEntry!); + Overlay.of(context, debugRequiredFor: widget)!.insert(overlayEntry!); } // Remove the OverlayEntry. diff --git a/packages/flutter/lib/src/cupertino/context_menu.dart b/packages/flutter/lib/src/cupertino/context_menu.dart index 9dc484033e..39216fc56a 100644 --- a/packages/flutter/lib/src/cupertino/context_menu.dart +++ b/packages/flutter/lib/src/cupertino/context_menu.dart @@ -363,7 +363,7 @@ class _CupertinoContextMenuState extends State with Ticker ); }, ); - Overlay.of(context, rootOverlay: true)!.insert(_lastOverlayEntry!); + Overlay.of(context, rootOverlay: true, debugRequiredFor: widget)!.insert(_lastOverlayEntry!); _openController.forward(); } diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 5635c561f3..656b74041f 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -1004,7 +1004,7 @@ class _BottomNavigationBarState extends State with TickerPr assert(debugCheckHasDirectionality(context)); assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMediaQuery(context)); - assert(Overlay.of(context, debugRequiredFor: widget) != null); + assert(debugCheckHasOverlay(context)); final BottomNavigationBarThemeData bottomTheme = BottomNavigationBarTheme.of(context); final BottomNavigationBarLandscapeLayout layout = widget.landscapeLayout diff --git a/packages/flutter/lib/src/material/range_slider.dart b/packages/flutter/lib/src/material/range_slider.dart index 48cf5ad4b6..df6839f3f4 100644 --- a/packages/flutter/lib/src/material/range_slider.dart +++ b/packages/flutter/lib/src/material/range_slider.dart @@ -655,7 +655,7 @@ class _RangeSliderState extends State with TickerProviderStateMixin ); }, ); - Overlay.of(context)!.insert(overlayEntry!); + Overlay.of(context, debugRequiredFor: widget)!.insert(overlayEntry!); } } } diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart index 1a5b1ab36f..04c761b0b6 100644 --- a/packages/flutter/lib/src/material/slider.dart +++ b/packages/flutter/lib/src/material/slider.dart @@ -842,7 +842,7 @@ class _SliderState extends State with TickerProviderStateMixin { ); }, ); - Overlay.of(context)!.insert(overlayEntry!); + Overlay.of(context, debugRequiredFor: widget)!.insert(overlayEntry!); } } } diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart index 698619ee9c..f5853f0d79 100644 --- a/packages/flutter/lib/src/material/tooltip.dart +++ b/packages/flutter/lib/src/material/tooltip.dart @@ -698,7 +698,7 @@ class TooltipState extends State with SingleTickerProviderStateMixin { if (_tooltipMessage.isEmpty) { return widget.child ?? const SizedBox(); } - assert(Overlay.of(context, debugRequiredFor: widget) != null); + assert(debugCheckHasOverlay(context)); final ThemeData theme = Theme.of(context); final TooltipThemeData tooltipTheme = TooltipTheme.of(context); final TextStyle defaultTextStyle; diff --git a/packages/flutter/lib/src/widgets/autocomplete.dart b/packages/flutter/lib/src/widgets/autocomplete.dart index c78f7d0d5a..197e657e60 100644 --- a/packages/flutter/lib/src/widgets/autocomplete.dart +++ b/packages/flutter/lib/src/widgets/autocomplete.dart @@ -432,7 +432,7 @@ class _RawAutocompleteState extends State> ); }, ); - Overlay.of(context, rootOverlay: true)!.insert(newFloatingOptions); + Overlay.of(context, rootOverlay: true, debugRequiredFor: widget)!.insert(newFloatingOptions); _floatingOptions = newFloatingOptions; } else { _floatingOptions = null; diff --git a/packages/flutter/lib/src/widgets/drag_target.dart b/packages/flutter/lib/src/widgets/drag_target.dart index 9da861b0ac..17a685960d 100644 --- a/packages/flutter/lib/src/widgets/drag_target.dart +++ b/packages/flutter/lib/src/widgets/drag_target.dart @@ -9,6 +9,7 @@ import 'package:flutter/services.dart'; import 'basic.dart'; import 'binding.dart'; +import 'debug.dart'; import 'framework.dart'; import 'media_query.dart'; import 'overlay.dart'; @@ -598,7 +599,7 @@ class _DraggableState extends State> { @override Widget build(BuildContext context) { - assert(Overlay.of(context, debugRequiredFor: widget, rootOverlay: widget.rootOverlay) != null); + assert(debugCheckHasOverlay(context)); final bool canDrag = widget.maxSimultaneousDrags == null || _activeCount < widget.maxSimultaneousDrags!; final bool showChild = _activeCount == 0 || widget.childWhenDragging == null; diff --git a/packages/flutter/lib/src/widgets/reorderable_list.dart b/packages/flutter/lib/src/widgets/reorderable_list.dart index a7261bb342..9cd2c38302 100644 --- a/packages/flutter/lib/src/widgets/reorderable_list.dart +++ b/packages/flutter/lib/src/widgets/reorderable_list.dart @@ -702,7 +702,7 @@ class SliverReorderableListState extends State with Ticke ); _dragInfo!.startDrag(); - final OverlayState overlay = Overlay.of(context)!; + final OverlayState overlay = Overlay.of(context, debugRequiredFor: widget)!; assert(_overlayEntry == null); _overlayEntry = OverlayEntry(builder: _dragInfo!.createProxy); overlay.insert(_overlayEntry!); @@ -897,7 +897,7 @@ class SliverReorderableListState extends State with Ticke } final Widget child = widget.itemBuilder(context, index); assert(child.key != null, 'All list items must have a key'); - final OverlayState overlay = Overlay.of(context)!; + final OverlayState overlay = Overlay.of(context, debugRequiredFor: widget)!; return _ReorderableItem( key: _ReorderableItemGlobalKey(child.key!, index, this), index: index, @@ -1284,7 +1284,7 @@ class _DragInfo extends Drag { } Offset _overlayOrigin(BuildContext context) { - final OverlayState overlay = Overlay.of(context)!; + final OverlayState overlay = Overlay.of(context, debugRequiredFor: context.widget)!; final RenderBox overlayBox = overlay.context.findRenderObject()! as RenderBox; return overlayBox.localToGlobal(Offset.zero); } diff --git a/packages/flutter/lib/src/widgets/selectable_region.dart b/packages/flutter/lib/src/widgets/selectable_region.dart index d0681872f2..e34543df94 100644 --- a/packages/flutter/lib/src/widgets/selectable_region.dart +++ b/packages/flutter/lib/src/widgets/selectable_region.dart @@ -12,6 +12,7 @@ import 'package:flutter/services.dart'; import 'actions.dart'; import 'basic.dart'; +import 'debug.dart'; import 'focus_manager.dart'; import 'focus_scope.dart'; import 'framework.dart'; @@ -804,7 +805,7 @@ class _SelectableRegionState extends State with TextSelectionD @override Widget build(BuildContext context) { - assert(Overlay.of(context, debugRequiredFor: widget) != null); + assert(debugCheckHasOverlay(context)); return CompositedTransformTarget( link: _toolbarLayerLink, child: RawGestureDetector( diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index 59e6df4228..79b702bfc1 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart @@ -16,6 +16,7 @@ import 'basic.dart'; import 'binding.dart'; import 'constants.dart'; import 'container.dart'; +import 'debug.dart'; import 'editable_text.dart'; import 'framework.dart'; import 'gesture_detector.dart'; @@ -616,15 +617,8 @@ class SelectionOverlay { _endHandleType = endHandleType, _lineHeightAtEnd = lineHeightAtEnd, _selectionEndpoints = selectionEndpoints, - _toolbarLocation = toolbarLocation { - final OverlayState? overlay = Overlay.of(context, rootOverlay: true); - assert( - overlay != null, - 'No Overlay widget exists above $context.\n' - 'Usually the Navigator created by WidgetsApp provides the overlay. Perhaps your ' - 'app content was created above the Navigator with the WidgetsApp builder parameter.', - ); - } + _toolbarLocation = toolbarLocation, + assert(debugCheckHasOverlay(context)); /// The context in which the selection handles should appear. /// diff --git a/packages/flutter/test/widgets/selectable_text_test.dart b/packages/flutter/test/widgets/selectable_text_test.dart index e53da5b7b9..ba31a8974d 100644 --- a/packages/flutter/test/widgets/selectable_text_test.dart +++ b/packages/flutter/test/widgets/selectable_text_test.dart @@ -176,6 +176,37 @@ void main() { ); } + testWidgets('throw if no Overlay widget exists above', (WidgetTester tester) async { + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: MediaQueryData(size: Size(800.0, 600.0)), + child: Center( + child: Material( + child: SelectableText('I love Flutter!'), + ), + ), + ), + ), + ); + + final Offset textFieldStart = tester.getTopLeft(find.byType(SelectableText)); + final TestGesture gesture = await tester.startGesture(textFieldStart, kind: PointerDeviceKind.mouse); + await tester.pump(const Duration(seconds: 2)); + await gesture.up(); + await tester.pumpAndSettle(); + + final FlutterError error = tester.takeException() as FlutterError; + expect( + error.message, + contains('EditableText widgets require an Overlay widget ancestor'), + ); + + await tester.pumpWidget(const SizedBox.shrink()); + expect(tester.takeException(), isNotNull); // side effect exception + }); + testWidgets('Do not crash when remove SelectableText during handle drag', (WidgetTester tester) async { // Regression test https://github.com/flutter/flutter/issues/108242 bool isShow = true;