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`.
This commit is contained in:
Greg Spencer 2024-11-18 15:59:28 -08:00 committed by GitHub
parent 3a17b67353
commit e68ed3d1f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 51 additions and 27 deletions

View File

@ -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<TextSelectionControls>('selectionControls', selectionControls, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollBehavior>('scrollBehavior', scrollBehavior, defaultValue: null));
properties.add(DiagnosticsProperty<TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
}
}
@ -738,6 +744,7 @@ class _SelectableTextState extends State<SelectableText> implements TextSelectio
magnifierConfiguration: widget.magnifierConfiguration ?? TextMagnifier.adaptiveMagnifierConfiguration,
dragStartBehavior: widget.dragStartBehavior,
scrollPhysics: widget.scrollPhysics,
scrollBehavior: widget.scrollBehavior,
autofillHints: null,
contextMenuBuilder: widget.contextMenuBuilder,
),

View File

@ -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

View File

@ -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].

View File

@ -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

View File

@ -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].

View File

@ -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}

View File

@ -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}

View File

@ -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<EditableText>(find.byType(EditableText)).scrollBehavior, isNull);
expect(tester.widget<Scrollable>(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<EditableText>(find.byType(EditableText)).scrollBehavior, equals(behavior));
expect(tester.widget<Scrollable>(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',
]);
});