Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,11 @@ class _TextScrollViewState extends State<TextScrollView>
child: SingleChildScrollView(
key: _textFieldViewportKey,
controller: _scrollController,
physics: const NeverScrollableScrollPhysics(),
// For single-line text fields, we do not allow horizontal scrolling,
// therefor we apply NeverScrollableScrollPhysics. For multi-line text
// fields, we pass null to allow the SingleChildScrollView to default
// to the appropriate scroll physics based on the host platform.
physics: isMultiline ? null : const NeverScrollableScrollPhysics(),
Copy link
Collaborator Author

@CillianMyles CillianMyles Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing null for scroll physics let's the platform dictate the appropriate scroll physics.
Also as far as I could tell this change only applies to mobile SuperTextFields.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also as far as I could text this change only applies to mobile SuperTextFields.

Can you mention what that's based on? Without opening the source code I can't tell. Is there a mobile platform check somewhere in this code, or a related place?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is within this widget called TextScrollView, which is only used in SuperAndroidTextField and SuperIOSTextField:

CleanShot 2024-07-09 at 21 37 51@2x

I said as far "as I could tell" because I'm not intimately familiar with the codebase, and did not want to say something incorrect or misleading.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a code comment that explains why this ternary is here, and can you also add a breadcrumb in that comment for where a developer should look to find scrolling behavior in the case of a single-line text field (for which we disable the physics)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CillianMyles I wanted to make sure you saw the above comment

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added + updated! ✅

scrollDirection: isMultiline ? Axis.vertical : Axis.horizontal,
child: Padding(
padding: widget.padding ?? EdgeInsets.zero,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,98 @@ void main() {
// Ensure the selection didn't change.
expect(SuperTextFieldInspector.findSelection(), TextRange.empty);
});

testWidgetsOnMobile("multi-line is vertically scrollable when text spans more lines than maxLines", (tester) async {
final initialText = "The first line of text in the field\n"
"The second line of text in the field\n"
"The third line of text in the field";
final controller = AttributedTextEditingController(
text: AttributedText(initialText),
);

// Pump the widget tree with a SuperTextField with a maxHeight of 2 lines
// of text, which should overflow considering there are 3 lines of text.
await _pumpTestApp(
tester,
textController: controller,
minLines: 1,
maxLines: 2,
maxHeight: 40,
);

// Ensure the text field has not yet scrolled.
var textTop = tester.getTopRight(find.byType(SuperTextField)).dy;
var viewportTop = tester.getTopRight(find.byType(SuperText)).dy;
expect(textTop, moreOrLessEquals(viewportTop));

// Scroll down to reveal the last line of text.
await tester.drag(find.byType(SuperTextField), const Offset(0, -1000.0));
await tester.pumpAndSettle();

// Ensure the text field has scrolled to the bottom.
var textBottom = tester.getBottomRight(find.byType(SuperTextField)).dy;
var viewportBottom = tester.getBottomRight(find.byType(SuperText)).dy;
expect(textBottom, moreOrLessEquals(viewportBottom));

// Scroll back up to the top of the text field.
await tester.drag(find.byType(SuperTextField), const Offset(0, 1000.0));
await tester.pumpAndSettle();

// Ensure the text field has scrolled back to the top.
textTop = tester.getTopRight(find.byType(SuperTextField)).dy;
viewportTop = tester.getTopRight(find.byType(SuperText)).dy;
expect(textTop, moreOrLessEquals(viewportTop));
});

testWidgetsOnDesktop("multi-line is vertically scrollable when text spans more lines than maxLines", (tester) async {
final initialText = "The first line of text in the field\n"
"The second line of text in the field\n"
"The third line of text in the field";
final controller = AttributedTextEditingController(
text: AttributedText(initialText),
);

// Pump the widget tree with a SuperTextField with a maxHeight of 2 lines
// of text, which should overflow considering there are 3 lines of text.
await _pumpTestApp(
tester,
textController: controller,
minLines: 1,
maxLines: 2,
maxHeight: 40,
);

// Ensure the text field has not yet scrolled.
var textTop = tester.getTopRight(find.byType(SuperTextField)).dy;
var viewportTop = tester.getTopRight(find.byType(SuperText)).dy;
expect(textTop, moreOrLessEquals(viewportTop));

// Scroll down to reveal the last line of text.
await tester.drag(
find.byType(SuperTextField),
const Offset(0, -1000.0),
kind: PointerDeviceKind.trackpad,
);
await tester.pumpAndSettle();

// Ensure the text field has scrolled to the bottom.
var textBottom = tester.getBottomRight(find.byType(SuperTextField)).dy;
var viewportBottom = tester.getBottomRight(find.byType(SuperText)).dy;
expect(textBottom, moreOrLessEquals(viewportBottom));

// Scroll back up to the top of the text field.
await tester.drag(
find.byType(SuperTextField),
const Offset(0, 1000.0),
kind: PointerDeviceKind.trackpad,
);
await tester.pumpAndSettle();

// Ensure the text field has scrolled back to the top.
textTop = tester.getTopRight(find.byType(SuperTextField)).dy;
viewportTop = tester.getTopRight(find.byType(SuperText)).dy;
expect(textTop, moreOrLessEquals(viewportTop));
});
});
}

Expand Down
Loading