Unverified Commit 375c7268 authored by Gary Qian's avatar Gary Qian Committed by GitHub

Add clipBeheavior support to TextFields (#90423)

parent c603d8aa
......@@ -294,6 +294,7 @@ class CupertinoTextField extends StatefulWidget {
this.scrollController,
this.scrollPhysics,
this.autofillHints = const <String>[],
this.clipBehavior = Clip.hardEdge,
this.restorationId,
this.enableIMEPersonalizedLearning = true,
}) : assert(textAlign != null),
......@@ -450,6 +451,7 @@ class CupertinoTextField extends StatefulWidget {
this.scrollController,
this.scrollPhysics,
this.autofillHints = const <String>[],
this.clipBehavior = Clip.hardEdge,
this.restorationId,
this.enableIMEPersonalizedLearning = true,
}) : assert(textAlign != null),
......@@ -493,6 +495,7 @@ class CupertinoTextField extends StatefulWidget {
!identical(keyboardType, TextInputType.text),
'Use keyboardType TextInputType.multiline when using TextInputAction.newline on a multiline TextField.',
),
assert(clipBehavior != null),
assert(enableIMEPersonalizedLearning != null),
keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline),
toolbarOptions = toolbarOptions ?? (obscureText ?
......@@ -786,6 +789,11 @@ class CupertinoTextField extends StatefulWidget {
/// {@macro flutter.services.AutofillConfiguration.autofillHints}
final Iterable<String>? autofillHints;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
/// {@macro flutter.material.textfield.restorationId}
final String? restorationId;
......@@ -833,6 +841,7 @@ class CupertinoTextField extends StatefulWidget {
properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: TextAlign.start));
properties.add(DiagnosticsProperty<TextAlignVertical>('textAlignVertical', textAlignVertical, defaultValue: null));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.hardEdge));
properties.add(DiagnosticsProperty<bool>('enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, defaultValue: true));
}
}
......@@ -1265,6 +1274,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio
scrollPhysics: widget.scrollPhysics,
enableInteractiveSelection: widget.enableInteractiveSelection,
autofillClient: this,
clipBehavior: widget.clipBehavior,
restorationId: 'editable',
enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning,
),
......
......@@ -346,6 +346,7 @@ class TextField extends StatefulWidget {
this.scrollController,
this.scrollPhysics,
this.autofillHints = const <String>[],
this.clipBehavior = Clip.hardEdge,
this.restorationId,
this.enableIMEPersonalizedLearning = true,
}) : assert(textAlign != null),
......@@ -387,6 +388,7 @@ class TextField extends StatefulWidget {
!identical(keyboardType, TextInputType.text),
'Use keyboardType TextInputType.multiline when using TextInputAction.newline on a multiline TextField.',
),
assert(clipBehavior != null),
assert(enableIMEPersonalizedLearning != null),
keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline),
toolbarOptions = toolbarOptions ?? (obscureText ?
......@@ -761,6 +763,11 @@ class TextField extends StatefulWidget {
/// {@macro flutter.services.AutofillConfiguration.autofillHints}
final Iterable<String>? autofillHints;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.hardEdge].
final Clip clipBehavior;
/// {@template flutter.material.textfield.restorationId}
/// Restoration ID to save and restore the state of the text field.
///
......@@ -823,6 +830,7 @@ class TextField extends StatefulWidget {
properties.add(DiagnosticsProperty<TextSelectionControls>('selectionControls', selectionControls, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollController>('scrollController', scrollController, defaultValue: null));
properties.add(DiagnosticsProperty<ScrollPhysics>('scrollPhysics', scrollPhysics, defaultValue: null));
properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: Clip.hardEdge));
properties.add(DiagnosticsProperty<bool>('enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, defaultValue: true));
}
}
......@@ -1265,6 +1273,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
scrollPhysics: widget.scrollPhysics,
autofillClient: this,
autocorrectionTextRectColor: autocorrectionTextRectColor,
clipBehavior: widget.clipBehavior,
restorationId: 'editable',
enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning,
),
......
......@@ -23,6 +23,7 @@ import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
import '../widgets/clipboard_utils.dart';
import '../widgets/editable_text_utils.dart' show OverflowWidgetTextEditingController;
import '../widgets/semantics_tester.dart';
// On web, the context menu (aka toolbar) is provided by the browser.
......@@ -4819,4 +4820,46 @@ void main() {
final EditableText rtlWidget = tester.widget(find.byType(EditableText));
expect(rtlWidget.textDirection, TextDirection.rtl);
});
testWidgets('clipBehavior has expected defaults', (WidgetTester tester) async {
await tester.pumpWidget(
const CupertinoApp(
home: CupertinoTextField(
),
),
);
final CupertinoTextField textField = tester.firstWidget(find.byType(CupertinoTextField));
expect(textField.clipBehavior, Clip.hardEdge);
});
testWidgets('Overflow clipBehavior none golden', (WidgetTester tester) async {
final Widget widget = CupertinoApp(
home: RepaintBoundary(
key: const ValueKey<int>(1),
child: SizedBox(
height: 200.0,
width: 200.0,
child: Center(
child: CupertinoTextField(
controller: OverflowWidgetTextEditingController(),
clipBehavior: Clip.none,
),
),
),
),
);
await tester.pumpWidget(widget);
final CupertinoTextField textField = tester.firstWidget(find.byType(CupertinoTextField));
expect(textField.clipBehavior, Clip.none);
final EditableText editableText = tester.firstWidget(find.byType(EditableText));
expect(editableText.clipBehavior, Clip.none);
await expectLater(
find.byKey(const ValueKey<int>(1)),
matchesGoldenFile('overflow_clipbehavior_none.cupertino.0.png'),
);
});
}
......@@ -24,7 +24,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/clipboard_utils.dart';
import '../widgets/editable_text_utils.dart' show findRenderEditable, globalize, textOffsetToPosition;
import '../widgets/editable_text_utils.dart' show findRenderEditable, globalize, textOffsetToPosition, OverflowWidgetTextEditingController;
import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart';
......@@ -552,6 +552,48 @@ void main() {
expect(textField.cursorRadius, const Radius.circular(3.0));
});
testWidgets('clipBehavior has expected defaults', (WidgetTester tester) async {
await tester.pumpWidget(
overlay(
child: const TextField(
),
),
);
final TextField textField = tester.firstWidget(find.byType(TextField));
expect(textField.clipBehavior, Clip.hardEdge);
});
testWidgets('Overflow clipBehavior none golden', (WidgetTester tester) async {
final Widget widget = overlay(
child: RepaintBoundary(
key: const ValueKey<int>(1),
child: SizedBox(
height: 200,
width: 200,
child: Center(
child: TextField(
controller: OverflowWidgetTextEditingController(),
clipBehavior: Clip.none,
),
),
),
),
);
await tester.pumpWidget(widget);
final TextField textField = tester.firstWidget(find.byType(TextField));
expect(textField.clipBehavior, Clip.none);
final EditableText editableText = tester.firstWidget(find.byType(EditableText));
expect(editableText.clipBehavior, Clip.none);
await expectLater(
find.byKey(const ValueKey<int>(1)),
matchesGoldenFile('overflow_clipbehavior_none.material.0.png'),
);
});
testWidgets('Material cursor android golden', (WidgetTester tester) async {
final Widget widget = overlay(
child: const RepaintBoundary(
......
......@@ -45,3 +45,27 @@ Offset textOffsetToPosition(WidgetTester tester, int offset) {
expect(endpoints.length, 1);
return endpoints[0].point + const Offset(0.0, -2.0);
}
// Simple controller that builds a WidgetSpan with 100 height.
class OverflowWidgetTextEditingController extends TextEditingController {
@override
TextSpan buildTextSpan({
required BuildContext context,
TextStyle? style,
required bool withComposing,
}) {
return TextSpan(
style: style,
children: <InlineSpan>[
const TextSpan(text: 'Hi'),
WidgetSpan(
alignment: PlaceholderAlignment.bottom,
child: Container(
color: Colors.redAccent,
height: 100.0,
),
),
],
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment