From 4d3bbf30c8a8d0ec9acc25228728019e515ac9d7 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 15 Nov 2024 14:39:41 -0800 Subject: [PATCH] Make the focus node on SelectableRegion optional. (#158994) ## Description This makes the `focusNode` for `SelectableRegion` optional so that: - Users of the widget are no longer required to use `SelectableRegion` from within a `StatefulWidget` - They aren't likely to forget to dispose of a node they didn't supply. - Simpler to use, and the node is not used very often anyhow. Also made the `SelectableRegion` sample actually use `SelectableRegion`. ## Tests - Modified all the `SelectableRegion` tests to remove 3 identical lines of boilerplate from each (except 2, which actually used their focus nodes). --- .../selectable_region.0.dart | 3 +- .../lib/src/material/selection_area.dart | 10 +- .../lib/src/widgets/selectable_region.dart | 39 ++- .../test/widgets/default_colors_test.dart | 3 - .../test/widgets/selectable_region_test.dart | 286 ------------------ 5 files changed, 28 insertions(+), 313 deletions(-) diff --git a/examples/api/lib/material/selectable_region/selectable_region.0.dart b/examples/api/lib/material/selectable_region/selectable_region.0.dart index 72925863a1..869d5a0195 100644 --- a/examples/api/lib/material/selectable_region/selectable_region.0.dart +++ b/examples/api/lib/material/selectable_region/selectable_region.0.dart @@ -15,7 +15,8 @@ class SelectableRegionExampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - home: SelectionArea( + home: SelectableRegion( + selectionControls: materialTextSelectionControls, child: Scaffold( appBar: AppBar(title: const Text('SelectableRegion Sample')), body: const Center( diff --git a/packages/flutter/lib/src/material/selection_area.dart b/packages/flutter/lib/src/material/selection_area.dart index 5d305589c5..be1a26aa2e 100644 --- a/packages/flutter/lib/src/material/selection_area.dart +++ b/packages/flutter/lib/src/material/selection_area.dart @@ -109,18 +109,10 @@ class SelectionArea extends StatefulWidget { /// State for a [SelectionArea]. class SelectionAreaState extends State { - FocusNode get _effectiveFocusNode => widget.focusNode ?? (_internalNode ??= FocusNode()); - FocusNode? _internalNode; final GlobalKey _selectableRegionKey = GlobalKey(); /// The [State] of the [SelectableRegion] for which this [SelectionArea] wraps. SelectableRegionState get selectableRegion => _selectableRegionKey.currentState!; - @override - void dispose() { - _internalNode?.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { assert(debugCheckHasMaterialLocalizations(context)); @@ -133,7 +125,7 @@ class SelectionAreaState extends State { return SelectableRegion( key: _selectableRegionKey, selectionControls: controls, - focusNode: _effectiveFocusNode, + focusNode: widget.focusNode, contextMenuBuilder: widget.contextMenuBuilder, magnifierConfiguration: widget.magnifierConfiguration ?? TextMagnifier.adaptiveMagnifierConfiguration, onSelectionChanged: widget.onSelectionChanged, diff --git a/packages/flutter/lib/src/widgets/selectable_region.dart b/packages/flutter/lib/src/widgets/selectable_region.dart index 002237cdc5..dace673d58 100644 --- a/packages/flutter/lib/src/widgets/selectable_region.dart +++ b/packages/flutter/lib/src/widgets/selectable_region.dart @@ -37,7 +37,6 @@ import 'text_selection.dart'; import 'text_selection_toolbar_anchors.dart'; // Examples can assume: -// FocusNode _focusNode = FocusNode(); // late GlobalKey key; const Set _kLongPressSelectionDevices = { @@ -105,7 +104,6 @@ const double _kSelectableVerticalComparingThreshold = 3.0; /// MaterialApp( /// home: SelectableRegion( /// selectionControls: materialTextSelectionControls, -/// focusNode: _focusNode, // initialized to FocusNode() /// child: Scaffold( /// appBar: AppBar(title: const Text('Flutter Code Sample')), /// body: ListView( @@ -218,7 +216,7 @@ class SelectableRegion extends StatefulWidget { const SelectableRegion({ super.key, this.contextMenuBuilder, - required this.focusNode, + this.focusNode, required this.selectionControls, required this.child, this.magnifierConfiguration = TextMagnifierConfiguration.disabled, @@ -235,7 +233,7 @@ class SelectableRegion extends StatefulWidget { final TextMagnifierConfiguration magnifierConfiguration; /// {@macro flutter.widgets.Focus.focusNode} - final FocusNode focusNode; + final FocusNode? focusNode; /// The child widget this selection area applies to. /// @@ -373,10 +371,14 @@ class SelectableRegionState extends State with TextSelectionDe /// The list of native text processing actions provided by the engine. final List _processTextActions = []; + // The focus node to use if the widget didn't supply one. + FocusNode? _localFocusNode; + FocusNode get _focusNode => widget.focusNode ?? (_localFocusNode ??= FocusNode(debugLabel: 'SelectableRegion')); + @override void initState() { super.initState(); - widget.focusNode.addListener(_handleFocusChanged); + _focusNode.addListener(_handleFocusChanged); _initMouseGestureRecognizer(); _initTouchGestureRecognizer(); // Right clicks. @@ -426,9 +428,15 @@ class SelectableRegionState extends State with TextSelectionDe void didUpdateWidget(SelectableRegion oldWidget) { super.didUpdateWidget(oldWidget); if (widget.focusNode != oldWidget.focusNode) { - oldWidget.focusNode.removeListener(_handleFocusChanged); - widget.focusNode.addListener(_handleFocusChanged); - if (widget.focusNode.hasFocus != oldWidget.focusNode.hasFocus) { + if (oldWidget.focusNode == null && widget.focusNode != null) { + _localFocusNode?.removeListener(_handleFocusChanged); + _localFocusNode?.dispose(); + _localFocusNode = null; + } else if (widget.focusNode == null && oldWidget.focusNode != null) { + oldWidget.focusNode!.removeListener(_handleFocusChanged); + } + _focusNode.addListener(_handleFocusChanged); + if (_focusNode.hasFocus != oldWidget.focusNode?.hasFocus) { _handleFocusChanged(); } } @@ -439,7 +447,7 @@ class SelectableRegionState extends State with TextSelectionDe } void _handleFocusChanged() { - if (!widget.focusNode.hasFocus) { + if (!_focusNode.hasFocus) { if (kIsWeb) { PlatformSelectableRegionContextMenu.detach(_selectionDelegate); } @@ -628,7 +636,7 @@ class SelectableRegionState extends State with TextSelectionDe _lastPointerDeviceKind = details.kind; switch (_getEffectiveConsecutiveTapCount(details.consecutiveTapCount)) { case 1: - widget.focusNode.requestFocus(); + _focusNode.requestFocus(); switch (defaultTargetPlatform) { case TargetPlatform.android: case TargetPlatform.fuchsia: @@ -843,7 +851,7 @@ class SelectableRegionState extends State with TextSelectionDe void _handleTouchLongPressStart(LongPressStartDetails details) { HapticFeedback.selectionClick(); - widget.focusNode.requestFocus(); + _focusNode.requestFocus(); _selectWordAt(offset: details.globalPosition); // Platforms besides Android will show the text selection handles when // the long press is initiated. Android shows the text selection handles when @@ -883,7 +891,7 @@ class SelectableRegionState extends State with TextSelectionDe final Offset? previousSecondaryTapDownPosition = _lastSecondaryTapDownPosition; final bool toolbarIsVisible = _selectionOverlay?.toolbarIsVisible ?? false; _lastSecondaryTapDownPosition = details.globalPosition; - widget.focusNode.requestFocus(); + _focusNode.requestFocus(); switch (defaultTargetPlatform) { case TargetPlatform.android: case TargetPlatform.fuchsia: @@ -1706,6 +1714,9 @@ class SelectableRegionState extends State with TextSelectionDe _selectionOverlay?.hideMagnifier(); _selectionOverlay?.dispose(); _selectionOverlay = null; + widget.focusNode?.removeListener(_handleFocusChanged); + _localFocusNode?.removeListener(_handleFocusChanged); + _localFocusNode?.dispose(); super.dispose(); } @@ -1730,9 +1741,9 @@ class SelectableRegionState extends State with TextSelectionDe excludeFromSemantics: true, child: Actions( actions: _actions, - child: Focus( + child: Focus.withExternalFocusNode( includeSemantics: false, - focusNode: widget.focusNode, + focusNode: _focusNode, child: result, ), ), diff --git a/packages/flutter/test/widgets/default_colors_test.dart b/packages/flutter/test/widgets/default_colors_test.dart index 916fba0aa3..367ca487bf 100644 --- a/packages/flutter/test/widgets/default_colors_test.dart +++ b/packages/flutter/test/widgets/default_colors_test.dart @@ -69,11 +69,8 @@ void main() { testWidgets('Default text selection color', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); final OverlayEntry overlayEntry = OverlayEntry( builder: (BuildContext context) => SelectableRegion( - focusNode: focusNode, selectionControls: emptyTextSelectionControls, child: Align( key: key, diff --git a/packages/flutter/test/widgets/selectable_region_test.dart b/packages/flutter/test/widgets/selectable_region_test.dart index bb8a0ac483..c20112d78b 100644 --- a/packages/flutter/test/widgets/selectable_region_test.dart +++ b/packages/flutter/test/widgets/selectable_region_test.dart @@ -40,13 +40,9 @@ void main() { group('SelectableRegion', () { testWidgets('mouse selection single click sends correct events', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -81,13 +77,10 @@ void main() { testWidgets('mouse double click sends select-word event', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -112,13 +105,10 @@ void main() { testWidgets('touch double click sends select-word event', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -143,9 +133,6 @@ void main() { testWidgets('Does not crash when using Navigator pages', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/119776 - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: Navigator( @@ -155,7 +142,6 @@ void main() { children: [ const Text('How are you?'), SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const SelectAllWidget(child: SizedBox(width: 100, height: 100)), ), @@ -177,8 +163,6 @@ void main() { testWidgets('can draw handles when they are at rect boundaries', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( @@ -186,7 +170,6 @@ void main() { children: [ const Text('How are you?'), SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectAllWidget(key: spy, child: const SizedBox(width: 100, height: 100)), ), @@ -210,13 +193,10 @@ void main() { testWidgets('touch does not accept drag', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -236,13 +216,10 @@ void main() { testWidgets('does not merge semantics node of the children', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center( @@ -315,8 +292,6 @@ void main() { testWidgets('Horizontal PageView beats SelectionArea child touch drag gestures on iOS', (WidgetTester tester) async { final PageController pageController = PageController(); const String testValue = 'abc def ghi jkl mno pqr stu vwx yz'; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); addTearDown(pageController.dispose); await tester.pumpWidget( @@ -326,7 +301,6 @@ void main() { children: [ Center( child: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text(testValue), ), @@ -379,8 +353,6 @@ void main() { // Regression test for https://github.com/flutter/flutter/issues/150897. final PageController pageController = PageController(); const String testValue = 'abc def ghi jkl mno pqr stu vwx yz'; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); addTearDown(pageController.dispose); await tester.pumpWidget( @@ -391,7 +363,6 @@ void main() { children: [ Center( child: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text(testValue), ), @@ -452,13 +423,10 @@ void main() { testWidgets('mouse single-click selection collapses the selection', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -481,13 +449,10 @@ void main() { testWidgets('touch long press sends select-word event', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -509,13 +474,10 @@ void main() { testWidgets('touch long press and drag sends correct events', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -547,13 +509,10 @@ void main() { 'touch long press cancel does not send ClearSelectionEvent', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -583,8 +542,6 @@ void main() { (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/128765 final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( @@ -594,7 +551,6 @@ void main() { child: SizedBox( height: 2000, child: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -627,13 +583,10 @@ void main() { testWidgets('mouse long press does not send select-word event', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -656,19 +609,16 @@ void main() { testWidgets('Can extend StaticSelectionContainerDelegate', (WidgetTester tester) async { SelectedContent? content; - final FocusNode focusNode = FocusNode(); // Inserts a new line between selected content of children selectables. final ColumnSelectionContainerDelegate selectionDelegate = ColumnSelectionContainerDelegate(); - addTearDown(focusNode.dispose); addTearDown(selectionDelegate.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( onSelectionChanged: (SelectedContent? selectedContent) => content = selectedContent, - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionContainer( delegate: selectionDelegate, @@ -711,13 +661,10 @@ void main() { addTearDown(() { tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, mockClipboard.handleMethodCall); }); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('How are you?'), ), @@ -814,13 +761,9 @@ void main() { }, variant: TargetPlatformVariant.desktop()); testWidgets('touch can select word-by-word on double tap drag on mobile platforms', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -890,13 +833,9 @@ void main() { ); testWidgets('touch can select multiple widgets on double tap drag on mobile platforms', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -942,13 +881,9 @@ void main() { ); testWidgets('touch can select multiple widgets on double tap drag and return to origin word on mobile platforms', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1006,13 +941,9 @@ void main() { ); testWidgets('touch can reverse selection across multiple widgets on double tap drag on mobile platforms', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1063,13 +994,9 @@ void main() { 'This will be the start of a new line. When triple clicking this block ' 'of text all of it should be selected.'; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text(longText), @@ -1166,13 +1093,9 @@ void main() { ); testWidgets('touch cannot select word-by-word on double tap drag when on Android web', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -1219,13 +1142,9 @@ void main() { ); testWidgets('touch can double tap + drag on iOS web', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -1275,13 +1194,9 @@ void main() { ); testWidgets('touch cannot double tap on iOS web', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -1311,15 +1226,12 @@ void main() { testWidgets('RenderParagraph should invalidate cachedRect on window size change', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/155143. - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); addTearDown(tester.view.reset); const String testString = 'How are you doing today? Good, and you?'; await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text(testString), @@ -1366,14 +1278,11 @@ void main() { testWidgets('RenderParagraph should invalidate cached bounding boxes', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); addTearDown(tester.view.reset); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center( @@ -1427,13 +1336,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can select single text on desktop platforms', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -1478,13 +1383,9 @@ void main() { }, variant: TargetPlatformVariant.desktop()); testWidgets('mouse can select single text on mobile platforms', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -1531,13 +1432,9 @@ void main() { }, variant: TargetPlatformVariant.mobile()); testWidgets('mouse can select word-by-word on double click drag', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -1604,13 +1501,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can select multiple widgets on double click drag', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1653,13 +1546,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can select multiple widgets on double click drag and return to origin word', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1714,13 +1603,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can reverse selection across multiple widgets on double click drag', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1768,13 +1653,9 @@ void main() { 'This will be the start of a new line. When triple clicking this block ' 'of text all of it should be selected.'; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text(longText), @@ -1843,13 +1724,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can select multiple widgets on triple click drag when selecting inside a WidgetSpan', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text.rich( WidgetSpan( @@ -1899,13 +1776,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can select multiple widgets on triple click drag', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1965,13 +1838,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can select multiple widgets on triple click drag and return to origin paragraph', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2041,13 +1910,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can reverse selection across multiple widgets on triple click drag', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2094,13 +1959,9 @@ void main() { }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. testWidgets('mouse can select multiple widgets', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2137,13 +1998,9 @@ void main() { }); testWidgets('mouse shift + click holds the selection start in place and moves the end', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2200,13 +2057,9 @@ void main() { }, variant: TargetPlatformVariant.desktop()); testWidgets('mouse shift + click collapses the selection when it has not been initialized', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2234,13 +2087,9 @@ void main() { }, variant: TargetPlatformVariant.desktop()); testWidgets('collapsing selection should clear selection of all other selectables', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2280,13 +2129,9 @@ void main() { }); testWidgets('mouse can work with disabled container', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2324,13 +2169,9 @@ void main() { }); testWidgets('mouse can reverse selection', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); - await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2374,12 +2215,9 @@ void main() { final bool isPlatformAndroid = defaultTargetPlatform == TargetPlatform.android; Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2466,13 +2304,10 @@ void main() { (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2542,13 +2377,10 @@ void main() { (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2626,13 +2458,10 @@ void main() { // Regression test for https://github.com/flutter/flutter/issues/150268. Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2701,13 +2530,10 @@ void main() { (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2808,13 +2634,10 @@ void main() { (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2931,13 +2754,10 @@ void main() { (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -3058,13 +2878,10 @@ void main() { ); testWidgets('can copy a selection made with the mouse', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3243,13 +3060,10 @@ void main() { testWidgets( 'mouse selection can handle widget span', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -3285,13 +3099,10 @@ void main() { testWidgets( 'double click + drag mouse selection can handle widget span', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -3333,13 +3144,10 @@ void main() { 'double click + drag mouse selection can handle widget span - multiline', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); final UniqueKey innerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -3384,13 +3192,10 @@ void main() { 'select word event can select inline widget', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); final UniqueKey innerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -3437,7 +3242,6 @@ void main() { await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center( @@ -3478,13 +3282,10 @@ void main() { 'can select word when a selectables rect is completely inside of another selectables rect', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/127076. final UniqueKey outerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center( @@ -3524,13 +3325,10 @@ void main() { testWidgets( 'can select word when selectable is broken up by an unselectable WidgetSpan', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center( @@ -3571,13 +3369,10 @@ void main() { 'widget span is ignored if it does not contain text - non Apple', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -3614,13 +3409,10 @@ void main() { 'widget span is ignored if it does not contain text - Apple', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -3654,13 +3446,10 @@ void main() { ); testWidgets('mouse can select across bidi text', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3698,13 +3487,10 @@ void main() { }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61020 testWidgets('long press and drag touch moves selection word by word', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3733,8 +3519,6 @@ void main() { testWidgets('can drag end handle when not covering entire screen', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/104620. - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( @@ -3742,7 +3526,6 @@ void main() { children: [ const Text('How are you?'), SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('Good, and you?'), ), @@ -3773,8 +3556,6 @@ void main() { testWidgets('can drag start handle when not covering entire screen', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/104620. - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( @@ -3782,7 +3563,6 @@ void main() { children: [ const Text('How are you?'), SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('Good, and you?'), ), @@ -3811,13 +3591,10 @@ void main() { }); testWidgets('can drag start selection handle', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3856,13 +3633,10 @@ void main() { }); testWidgets('can drag start selection handle across end selection handle', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3896,13 +3670,10 @@ void main() { }); testWidgets('can drag end selection handle across start selection handle', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3936,13 +3707,10 @@ void main() { }); testWidgets('can select all from toolbar', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3975,13 +3743,10 @@ void main() { }, skip: kIsWeb); // [intended] Web uses its native context menu. testWidgets('can copy from toolbar', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -4018,13 +3783,10 @@ void main() { }, skip: kIsWeb); // [intended] Web uses its native context menu. testWidgets('can use keyboard to granularly extend selection - character', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -4081,13 +3843,10 @@ void main() { }, variant: TargetPlatformVariant.all()); testWidgets('can use keyboard to granularly extend selection - word', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -4196,13 +3955,10 @@ void main() { }, variant: TargetPlatformVariant.all()); testWidgets('can use keyboard to granularly extend selection - line', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -4290,13 +4046,10 @@ void main() { }, variant: TargetPlatformVariant.all()); testWidgets('can use keyboard to granularly extend selection - document', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -4373,13 +4126,10 @@ void main() { }, variant: TargetPlatformVariant.all()); testWidgets('can use keyboard to directionally extend selection', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -4477,8 +4227,6 @@ void main() { testWidgets('Can drag handles to show, unshow, and update magnifier', (WidgetTester tester) async { const String text = 'Monkeys and rabbits in my soup'; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( @@ -4492,7 +4240,6 @@ void main() { return fakeMagnifier; }, ), - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text(text), ), @@ -4544,13 +4291,10 @@ void main() { testWidgets('toolbar is hidden on Android and iOS when orientation changes', (WidgetTester tester) async { addTearDown(tester.view.reset); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('How are you?'), ), @@ -4612,13 +4356,10 @@ void main() { testWidgets('the selection behavior when clicking `Copy` item in mobile platforms', (WidgetTester tester) async { List buttonItems = []; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -4668,13 +4409,10 @@ void main() { testWidgets('the handles do not disappear when clicking `Select all` item in mobile platforms', (WidgetTester tester) async { List buttonItems = []; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -4735,13 +4473,10 @@ void main() { testWidgets('Selection behavior when clicking the `Share` button on Android', (WidgetTester tester) async { List buttonItems = []; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -4795,13 +4530,10 @@ void main() { testWidgets('builds the correct button items', (WidgetTester tester) async { List buttonItems = []; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -4855,13 +4587,10 @@ void main() { ); testWidgets('can clear selection through SelectableRegionState', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -4919,13 +4648,10 @@ void main() { addTearDown(() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.processText, null)); Set buttonLabels = {}; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -4968,14 +4694,11 @@ void main() { testWidgets('onSelectionChange is called when the selection changes through gestures', (WidgetTester tester) async { SelectedContent? content; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( onSelectionChanged: (SelectedContent? selectedContent) => content = selectedContent, - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -5109,14 +4832,11 @@ void main() { testWidgets('onSelectionChange is called when the selection changes through keyboard actions', (WidgetTester tester) async { SelectedContent? content; - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( onSelectionChanged: (SelectedContent? selectedContent) => content = selectedContent, - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -5268,14 +4988,11 @@ void main() { }); testWidgets('web can show flutter context menu when the browser context menu is disabled', (WidgetTester tester) async { - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( onSelectionChanged: (SelectedContent? selectedContent) {}, - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -5305,13 +5022,10 @@ void main() { // Regression test for https://github.com/flutter/flutter/issues/127942. final UniqueKey outerText = UniqueKey(); const TextStyle textStyle = TextStyle(fontSize: 10); - final FocusNode focusNode = FocusNode(); - addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center(