From e68ed3d1f1b5233489a6a417776b61d4c6df15c4 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 18 Nov 2024 15:59:28 -0800 Subject: [PATCH] Plumbs `scrollBehavior` into `SelectableText` so that the scrollbar may be hidden (#158887) ## Description This adds a `scrollBehavior` attribute to `SelectableText` so that the scrolling can be controlled more directly. I added this specifically because it's not possible to turn off the scrollbar on a selectable text, even if you set the scroll physics to be `NeverScrollableScrollPhysics`. We had a UI where we needed to have a clipped, multi-line selectable text field, but have it not be scrollable unless it was expanded in size, and it wasn't possible to hide the scrollbar, but still wanted it to be selectable. ## Tests - Added a test that makes sure that the scroll behavior makes it down to the `Scrollable` in the `EditableText`. --- .../lib/src/material/selectable_text.dart | 7 ++++ .../lib/src/widgets/editable_text.dart | 4 +-- .../src/widgets/list_wheel_scroll_view.dart | 7 +--- .../lib/src/widgets/nested_scroll_view.dart | 7 +--- .../flutter/lib/src/widgets/page_view.dart | 7 +--- .../flutter/lib/src/widgets/scroll_view.dart | 7 +--- .../flutter/lib/src/widgets/scrollable.dart | 7 +++- .../test/widgets/selectable_text_test.dart | 32 +++++++++++++++++++ 8 files changed, 51 insertions(+), 27 deletions(-) diff --git a/packages/flutter/lib/src/material/selectable_text.dart b/packages/flutter/lib/src/material/selectable_text.dart index d432ce4d7f..44cf398a65 100644 --- a/packages/flutter/lib/src/material/selectable_text.dart +++ b/packages/flutter/lib/src/material/selectable_text.dart @@ -190,6 +190,7 @@ class SelectableText extends StatefulWidget { this.selectionControls, this.onTap, this.scrollPhysics, + this.scrollBehavior, this.semanticsLabel, this.textHeightBehavior, this.textWidthBasis, @@ -247,6 +248,7 @@ class SelectableText extends StatefulWidget { this.selectionControls, this.onTap, this.scrollPhysics, + this.scrollBehavior, this.semanticsLabel, this.textHeightBehavior, this.textWidthBasis, @@ -412,6 +414,9 @@ class SelectableText extends StatefulWidget { /// {@macro flutter.widgets.editableText.scrollPhysics} final ScrollPhysics? scrollPhysics; + /// {@macro flutter.widgets.editableText.scrollBehavior} + final ScrollBehavior? scrollBehavior; + /// {@macro flutter.widgets.Text.semanticsLabel} final String? semanticsLabel; @@ -467,6 +472,7 @@ class SelectableText extends StatefulWidget { properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); properties.add(DiagnosticsProperty('selectionControls', selectionControls, defaultValue: null)); properties.add(DiagnosticsProperty('scrollPhysics', scrollPhysics, defaultValue: null)); + properties.add(DiagnosticsProperty('scrollBehavior', scrollBehavior, defaultValue: null)); properties.add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); } } @@ -738,6 +744,7 @@ class _SelectableTextState extends State implements TextSelectio magnifierConfiguration: widget.magnifierConfiguration ?? TextMagnifier.adaptiveMagnifierConfiguration, dragStartBehavior: widget.dragStartBehavior, scrollPhysics: widget.scrollPhysics, + scrollBehavior: widget.scrollBehavior, autofillHints: null, contextMenuBuilder: widget.contextMenuBuilder, ), diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 6e1a11f01e..a9f88c1dbb 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -1847,17 +1847,17 @@ class EditableText extends StatefulWidget { /// Flutter. final String? restorationId; - /// {@template flutter.widgets.shadow.scrollBehavior} + /// {@template flutter.widgets.editableText.scrollBehavior} /// A [ScrollBehavior] that will be applied to this widget individually. /// /// Defaults to null, wherein the inherited [ScrollBehavior] is copied and /// modified to alter the viewport decoration, like [Scrollbar]s. - /// {@endtemplate} /// /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit /// [ScrollPhysics] is provided in [scrollPhysics], it will take precedence, /// followed by [scrollBehavior], and then the inherited ancestor /// [ScrollBehavior]. + /// {@endtemplate} /// /// The [ScrollBehavior] of the inherited [ScrollConfiguration] will be /// modified by default to only apply a [Scrollbar] if [maxLines] is greater diff --git a/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart b/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart index b14ad8fdcf..31fca1ab55 100644 --- a/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart +++ b/packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart @@ -710,12 +710,7 @@ class ListWheelScrollView extends StatefulWidget { /// {@macro flutter.widgets.scrollable.restorationId} final String? restorationId; - /// {@macro flutter.widgets.shadow.scrollBehavior} - /// - /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit - /// [ScrollPhysics] is provided in [physics], it will take precedence, - /// followed by [scrollBehavior], and then the inherited ancestor - /// [ScrollBehavior]. + /// {@macro flutter.widgets.scrollable.scrollBehavior} /// /// The [ScrollBehavior] of the inherited [ScrollConfiguration] will be /// modified by default to not apply a [Scrollbar]. diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart index ef41d89586..177e33fb38 100644 --- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart +++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart @@ -313,12 +313,7 @@ class NestedScrollView extends StatefulWidget { /// {@macro flutter.widgets.scrollable.restorationId} final String? restorationId; - /// {@macro flutter.widgets.shadow.scrollBehavior} - /// - /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit - /// [ScrollPhysics] is provided in [physics], it will take precedence, - /// followed by [scrollBehavior], and then the inherited ancestor - /// [ScrollBehavior]. + /// {@macro flutter.widgets.scrollable.scrollBehavior} /// /// The [ScrollBehavior] of the inherited [ScrollConfiguration] will be /// modified by default to not apply a [Scrollbar]. This is because the diff --git a/packages/flutter/lib/src/widgets/page_view.dart b/packages/flutter/lib/src/widgets/page_view.dart index d1ea02483f..dcb5f295a2 100644 --- a/packages/flutter/lib/src/widgets/page_view.dart +++ b/packages/flutter/lib/src/widgets/page_view.dart @@ -839,12 +839,7 @@ class PageView extends StatefulWidget { /// Defaults to [HitTestBehavior.opaque]. final HitTestBehavior hitTestBehavior; - /// {@macro flutter.widgets.shadow.scrollBehavior} - /// - /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit - /// [ScrollPhysics] is provided in [physics], it will take precedence, - /// followed by [scrollBehavior], and then the inherited ancestor - /// [ScrollBehavior]. + /// {@macro flutter.widgets.scrollable.scrollBehavior} /// /// The [ScrollBehavior] of the inherited [ScrollConfiguration] will be /// modified by default to not apply a [Scrollbar]. diff --git a/packages/flutter/lib/src/widgets/scroll_view.dart b/packages/flutter/lib/src/widgets/scroll_view.dart index 087216670b..629823536e 100644 --- a/packages/flutter/lib/src/widgets/scroll_view.dart +++ b/packages/flutter/lib/src/widgets/scroll_view.dart @@ -256,12 +256,7 @@ abstract class ScrollView extends StatelessWidget { /// [physics]. final ScrollPhysics? physics; - /// {@macro flutter.widgets.shadow.scrollBehavior} - /// - /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit - /// [ScrollPhysics] is provided in [physics], it will take precedence, - /// followed by [scrollBehavior], and then the inherited ancestor - /// [ScrollBehavior]. + /// {@macro flutter.widgets.scrollable.scrollBehavior} final ScrollBehavior? scrollBehavior; /// {@template flutter.widgets.scroll_view.shrinkWrap} diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index 976c025c39..bd00ea99cd 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -304,12 +304,17 @@ class Scrollable extends StatefulWidget { /// {@endtemplate} final String? restorationId; - /// {@macro flutter.widgets.shadow.scrollBehavior} + /// {@template flutter.widgets.scrollable.scrollBehavior} + /// A [ScrollBehavior] that will be applied to this widget individually. + /// + /// Defaults to null, wherein the inherited [ScrollBehavior] is copied and + /// modified to alter the viewport decoration, like [Scrollbar]s. /// /// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit /// [ScrollPhysics] is provided in [physics], it will take precedence, /// followed by [scrollBehavior], and then the inherited ancestor /// [ScrollBehavior]. + /// {@endtemplate} final ScrollBehavior? scrollBehavior; /// {@macro flutter.material.Material.clipBehavior} diff --git a/packages/flutter/test/widgets/selectable_text_test.dart b/packages/flutter/test/widgets/selectable_text_test.dart index f9bf5c08f1..89830532b6 100644 --- a/packages/flutter/test/widgets/selectable_text_test.dart +++ b/packages/flutter/test/widgets/selectable_text_test.dart @@ -1391,6 +1391,36 @@ void main() { expect(inputBox.hitTest(BoxHitTestResult(), position: inputBox.globalToLocal(newFourthPos)), isFalse); }); + testWidgets('ScrollBehavior can be overridden', (WidgetTester tester) async { + await tester.pumpWidget( + boilerplate( + child: const SelectableText( + kMoreThanFourLines, + dragStartBehavior: DragStartBehavior.down, + style: TextStyle(color: Colors.black, fontSize: 34.0), + maxLines: 2, + ), + ), + ); + expect(tester.widget(find.byType(EditableText)).scrollBehavior, isNull); + expect(tester.widget(find.byType(Scrollable)).scrollBehavior, isNotNull); + + final ScrollBehavior behavior = const ScrollBehavior()..copyWith(scrollbars: false); + await tester.pumpWidget( + boilerplate( + child: SelectableText( + kMoreThanFourLines, + dragStartBehavior: DragStartBehavior.down, + style: const TextStyle(color: Colors.black, fontSize: 34.0), + maxLines: 2, + scrollBehavior: behavior, + ), + ), + ); + expect(tester.widget(find.byType(EditableText)).scrollBehavior, equals(behavior)); + expect(tester.widget(find.byType(Scrollable)).scrollBehavior, equals(behavior)); + }); + testWidgets('minLines cannot be greater than maxLines', (WidgetTester tester) async { expect( () async { @@ -4578,6 +4608,7 @@ void main() { cursorRadius: Radius.zero, cursorColor: Color(0xff00ff00), scrollPhysics: ClampingScrollPhysics(), + scrollBehavior: ScrollBehavior(), semanticsLabel: 'something else', enableInteractiveSelection: false, ).debugFillProperties(builder); @@ -4603,6 +4634,7 @@ void main() { 'cursorColor: ${const Color(0xff00ff00)}', 'selection disabled', 'scrollPhysics: ClampingScrollPhysics', + 'scrollBehavior: ScrollBehavior', ]); });