Unverified Commit d2a2a5cf authored by jslavitz's avatar jslavitz Committed by GitHub

Adds a fade in and out, rounds corners, fixes offset and fixes height of cursor on iOS (#24876)

* Fixes cursor on iOS devices
parent 9f20bd6c
6fc7ec65d51116c3f83acb5251e57e779af2ebbb b530d67675a5aa9c5458b93019ce91e20ad88758
...@@ -110,6 +110,11 @@ class CupertinoButton extends StatefulWidget { ...@@ -110,6 +110,11 @@ class CupertinoButton extends StatefulWidget {
/// Defaults to round corners of 8 logical pixels. /// Defaults to round corners of 8 logical pixels.
final BorderRadius borderRadius; final BorderRadius borderRadius;
/// The shape of the button.
///
/// Defaults to a super ellipse with
// final ShapeBorder shape;
final bool _filled; final bool _filled;
/// Whether the button is enabled or disabled. Buttons are disabled by default. To /// Whether the button is enabled or disabled. Buttons are disabled by default. To
......
...@@ -35,6 +35,14 @@ const Color _kSelectionHighlightColor = Color(0x667FAACF); ...@@ -35,6 +35,14 @@ const Color _kSelectionHighlightColor = Color(0x667FAACF);
const Color _kInactiveTextColor = Color(0xFFC2C2C2); const Color _kInactiveTextColor = Color(0xFFC2C2C2);
const Color _kDisabledBackground = Color(0xFFFAFAFA); const Color _kDisabledBackground = Color(0xFFFAFAFA);
// An eyeballed value that moves the cursor slightly left of where it is
// rendered for text on Android so it's positioning more accurately matches the
// native iOS text cursor positioning.
//
// This value is in device pixels, not logical pixels as is typically used
// throughout the codebase.
const int _iOSHorizontalCursorOffsetPixels = -2;
/// Visibility of text field overlays based on the state of the current text entry. /// Visibility of text field overlays based on the state of the current text entry.
/// ///
/// Used to toggle the visibility behavior of the optional decorating widgets /// Used to toggle the visibility behavior of the optional decorating widgets
...@@ -163,7 +171,7 @@ class CupertinoTextField extends StatefulWidget { ...@@ -163,7 +171,7 @@ class CupertinoTextField extends StatefulWidget {
this.inputFormatters, this.inputFormatters,
this.enabled, this.enabled,
this.cursorWidth = 2.0, this.cursorWidth = 2.0,
this.cursorRadius, this.cursorRadius = const Radius.circular(2.0),
this.cursorColor = CupertinoColors.activeBlue, this.cursorColor = CupertinoColors.activeBlue,
this.keyboardAppearance, this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0), this.scrollPadding = const EdgeInsets.all(20.0),
...@@ -598,6 +606,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK ...@@ -598,6 +606,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
final TextEditingController controller = _effectiveController; final TextEditingController controller = _effectiveController;
final List<TextInputFormatter> formatters = widget.inputFormatters ?? <TextInputFormatter>[]; final List<TextInputFormatter> formatters = widget.inputFormatters ?? <TextInputFormatter>[];
final bool enabled = widget.enabled ?? true; final bool enabled = widget.enabled ?? true;
final Offset cursorOffset = Offset(_iOSHorizontalCursorOffsetPixels / MediaQuery.of(context).devicePixelRatio, 0);
if (widget.maxLength != null && widget.maxLengthEnforced) { if (widget.maxLength != null && widget.maxLengthEnforced) {
formatters.add(LengthLimitingTextInputFormatter(widget.maxLength)); formatters.add(LengthLimitingTextInputFormatter(widget.maxLength));
} }
...@@ -631,6 +640,9 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK ...@@ -631,6 +640,9 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
cursorWidth: widget.cursorWidth, cursorWidth: widget.cursorWidth,
cursorRadius: widget.cursorRadius, cursorRadius: widget.cursorRadius,
cursorColor: widget.cursorColor, cursorColor: widget.cursorColor,
cursorOpacityAnimates: true,
cursorOffset: cursorOffset,
paintCursorAboveText: true,
backgroundCursorColor: CupertinoColors.inactiveGray, backgroundCursorColor: CupertinoColors.inactiveGray,
scrollPadding: widget.scrollPadding, scrollPadding: widget.scrollPadding,
keyboardAppearance: keyboardAppearance, keyboardAppearance: keyboardAppearance,
......
...@@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart'; ...@@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'debug.dart'; import 'debug.dart';
...@@ -36,6 +37,14 @@ typedef InputCounterWidgetBuilder = Widget Function( ...@@ -36,6 +37,14 @@ typedef InputCounterWidgetBuilder = Widget Function(
} }
); );
// An eyeballed value that moves the cursor slightly left of where it is
// rendered for text on Android so it's positioning more accurately matches the
// native iOS text cursor positioning.
//
// This value is in device pixels, not logical pixels as is typically used
// throughout the codebase.
const int _iOSHorizontalCursorOffsetPixels = 2;
/// A material design text field. /// A material design text field.
/// ///
/// A text field lets the user enter text, either with hardware keyboard or with /// A text field lets the user enter text, either with hardware keyboard or with
...@@ -469,6 +478,14 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -469,6 +478,14 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
&& widget.decoration != null && widget.decoration != null
&& widget.decoration.counterText == null; && widget.decoration.counterText == null;
Radius get _cursorRadius {
if (widget.cursorRadius != null)
return widget.cursorRadius;
if (Theme.of(context).platform == TargetPlatform.iOS)
return const Radius.circular(2.0);
return null;
}
InputDecoration _getEffectiveDecoration() { InputDecoration _getEffectiveDecoration() {
final MaterialLocalizations localizations = MaterialLocalizations.of(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
...@@ -682,6 +699,22 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -682,6 +699,22 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
@override @override
bool get wantKeepAlive => _splashes != null && _splashes.isNotEmpty; bool get wantKeepAlive => _splashes != null && _splashes.isNotEmpty;
bool get _cursorOpacityAnimates => Theme.of(context).platform == TargetPlatform.iOS ? true : false;
Offset get _getCursorOffset => Offset(_iOSHorizontalCursorOffsetPixels / MediaQuery.of(context).devicePixelRatio, 0);
bool get _paintCursorAboveText => Theme.of(context).platform == TargetPlatform.iOS ? true : false;
Color get _cursorColor {
if (widget.cursorColor == null) {
if (Theme.of(context).platform == TargetPlatform.iOS)
return CupertinoTheme.of(context).primaryColor;
else
return Theme.of(context).cursorColor;
}
return widget.cursorColor;
}
@override @override
void deactivate() { void deactivate() {
if (_splashes != null) { if (_splashes != null) {
...@@ -704,7 +737,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -704,7 +737,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
assert(debugCheckHasDirectionality(context)); assert(debugCheckHasDirectionality(context));
assert( assert(
!(widget.style != null && widget.style.inherit == false && !(widget.style != null && widget.style.inherit == false &&
(widget.style.fontSize == null || widget.style.textBaseline == null)), (widget.style.fontSize == null || widget.style.textBaseline == null)),
'inherit false style must supply fontSize and textBaseline', 'inherit false style must supply fontSize and textBaseline',
); );
...@@ -755,8 +788,11 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -755,8 +788,11 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
inputFormatters: formatters, inputFormatters: formatters,
rendererIgnoresPointer: true, rendererIgnoresPointer: true,
cursorWidth: widget.cursorWidth, cursorWidth: widget.cursorWidth,
cursorRadius: widget.cursorRadius, cursorRadius: _cursorRadius,
cursorColor: widget.cursorColor ?? themeData.cursorColor, cursorColor: _cursorColor,
cursorOpacityAnimates: _cursorOpacityAnimates,
cursorOffset: _getCursorOffset,
paintCursorAboveText: _paintCursorAboveText,
backgroundCursorColor: CupertinoColors.inactiveGray, backgroundCursorColor: CupertinoColors.inactiveGray,
scrollPadding: widget.scrollPadding, scrollPadding: widget.scrollPadding,
keyboardAppearance: keyboardAppearance, keyboardAppearance: keyboardAppearance,
......
...@@ -7,6 +7,7 @@ import 'dart:async'; ...@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
class MockClipboard { class MockClipboard {
...@@ -143,6 +144,72 @@ void main() { ...@@ -143,6 +144,72 @@ void main() {
}, },
); );
testWidgets('iOS cursor has offset', (WidgetTester tester) async {
await tester.pumpWidget(
const CupertinoApp(
home: CupertinoTextField(),
),
);
final EditableText editableText = tester.firstWidget(find.byType(EditableText));
expect(editableText.cursorOffset, const Offset(-2.0 / 3.0, 0));
});
testWidgets('Cursor animates on iOS', (WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
await tester.pumpWidget(
const CupertinoApp(
home: CupertinoTextField(),
),
);
final Finder textFinder = find.byType(CupertinoTextField);
await tester.tap(textFinder);
await tester.pump();
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
final RenderEditable renderEditable = editableTextState.renderEditable;
expect(renderEditable.cursorColor.alpha, 255);
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 400));
expect(renderEditable.cursorColor.alpha, 255);
await tester.pump(const Duration(milliseconds: 200));
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor.alpha, 110);
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor.alpha, 16);
await tester.pump(const Duration(milliseconds: 50));
expect(renderEditable.cursorColor.alpha, 0);
debugDefaultTargetPlatformOverride = null;
});
testWidgets('Cursor radius is 2.0 on iOS', (WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
await tester.pumpWidget(
const CupertinoApp(
home: CupertinoTextField(),
),
);
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
final RenderEditable renderEditable = editableTextState.renderEditable;
expect(renderEditable.cursorRadius, const Radius.circular(2.0));
debugDefaultTargetPlatformOverride = null;
});
testWidgets( testWidgets(
'can control text content via controller', 'can control text content via controller',
(WidgetTester tester) async { (WidgetTester tester) async {
......
...@@ -277,6 +277,65 @@ void main() { ...@@ -277,6 +277,65 @@ void main() {
await checkCursorToggle(); await checkCursorToggle();
}); });
testWidgets('Cursor animates on iOS', (WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: TextField(),
),
),
);
final Finder textFinder = find.byType(TextField);
await tester.tap(textFinder);
await tester.pump();
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
final RenderEditable renderEditable = editableTextState.renderEditable;
expect(renderEditable.cursorColor.alpha, 255);
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 400));
expect(renderEditable.cursorColor.alpha, 255);
await tester.pump(const Duration(milliseconds: 200));
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor.alpha, 110);
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor.alpha, 16);
await tester.pump(const Duration(milliseconds: 50));
expect(renderEditable.cursorColor.alpha, 0);
debugDefaultTargetPlatformOverride = null;
});
testWidgets('Cursor radius is 2.0 on iOS', (WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: TextField(),
),
),
);
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
final RenderEditable renderEditable = editableTextState.renderEditable;
expect(renderEditable.cursorRadius, const Radius.circular(2.0));
debugDefaultTargetPlatformOverride = null;
});
testWidgets('cursor has expected defaults', (WidgetTester tester) async { testWidgets('cursor has expected defaults', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
...@@ -305,6 +364,7 @@ void main() { ...@@ -305,6 +364,7 @@ void main() {
}); });
testWidgets('cursor layout has correct width', (WidgetTester tester) async { testWidgets('cursor layout has correct width', (WidgetTester tester) async {
EditableText.debugDeterministicCursor = true;
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const RepaintBoundary( child: const RepaintBoundary(
...@@ -321,9 +381,11 @@ void main() { ...@@ -321,9 +381,11 @@ void main() {
find.byType(TextField), find.byType(TextField),
matchesGoldenFile('text_field_test.0.0.png'), matchesGoldenFile('text_field_test.0.0.png'),
); );
EditableText.debugDeterministicCursor = false;
}, skip: !Platform.isLinux); }, skip: !Platform.isLinux);
testWidgets('cursor layout has correct radius', (WidgetTester tester) async { testWidgets('cursor layout has correct radius', (WidgetTester tester) async {
EditableText.debugDeterministicCursor = true;
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: const RepaintBoundary( child: const RepaintBoundary(
...@@ -341,6 +403,7 @@ void main() { ...@@ -341,6 +403,7 @@ void main() {
find.byType(TextField), find.byType(TextField),
matchesGoldenFile('text_field_test.1.0.png'), matchesGoldenFile('text_field_test.1.0.png'),
); );
EditableText.debugDeterministicCursor = false;
}, skip: !Platform.isLinux); }, skip: !Platform.isLinux);
testWidgets('obscureText control test', (WidgetTester tester) async { testWidgets('obscureText control test', (WidgetTester tester) async {
...@@ -1469,7 +1532,7 @@ void main() { ...@@ -1469,7 +1532,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft,
); );
expect(topLeft.dx, equals(398.5)); expect(topLeft.dx, equals(401.0));
await tester.enterText(find.byType(TextField), 'abcd'); await tester.enterText(find.byType(TextField), 'abcd');
await tester.pump(); await tester.pump();
...@@ -1478,7 +1541,7 @@ void main() { ...@@ -1478,7 +1541,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft,
); );
expect(topLeft.dx, equals(398.5)); expect(topLeft.dx, equals(401.0));
}); });
testWidgets('Can align to center within center', (WidgetTester tester) async { testWidgets('Can align to center within center', (WidgetTester tester) async {
...@@ -1501,7 +1564,7 @@ void main() { ...@@ -1501,7 +1564,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft,
); );
expect(topLeft.dx, equals(398.5)); expect(topLeft.dx, equals(401.0));
await tester.enterText(find.byType(TextField), 'abcd'); await tester.enterText(find.byType(TextField), 'abcd');
await tester.pump(); await tester.pump();
...@@ -1510,7 +1573,7 @@ void main() { ...@@ -1510,7 +1573,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft,
); );
expect(topLeft.dx, equals(398.5)); expect(topLeft.dx, equals(401.0));
}); });
testWidgets('Controller can update server', (WidgetTester tester) async { testWidgets('Controller can update server', (WidgetTester tester) async {
...@@ -1723,7 +1786,7 @@ void main() { ...@@ -1723,7 +1786,7 @@ void main() {
scrollableState = tester.firstState(find.byType(Scrollable)); scrollableState = tester.firstState(find.byType(Scrollable));
// For a horizontal input, scrolls to the exact position of the caret. // For a horizontal input, scrolls to the exact position of the caret.
expect(scrollableState.position.pixels, equals(222.0)); expect(scrollableState.position.pixels, equals(223.0));
}); });
testWidgets('Multiline text field scrolls the caret into view', (WidgetTester tester) async { testWidgets('Multiline text field scrolls the caret into view', (WidgetTester tester) async {
...@@ -3130,7 +3193,7 @@ void main() { ...@@ -3130,7 +3193,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 10)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 10)).topLeft,
); );
expect(topLeft.dx, equals(701.0)); expect(topLeft.dx, equals(701.6666870117188));
await tester.pumpWidget( await tester.pumpWidget(
const MaterialApp( const MaterialApp(
...@@ -3150,7 +3213,7 @@ void main() { ...@@ -3150,7 +3213,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 10)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 10)).topLeft,
); );
expect(topLeft.dx, equals(160.0)); expect(topLeft.dx, equals(160.6666717529297));
}); });
testWidgets('TextField semantics', (WidgetTester tester) async { testWidgets('TextField semantics', (WidgetTester tester) async {
......
...@@ -124,11 +124,14 @@ void main() { ...@@ -124,11 +124,14 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Padding(
child: TextField( padding: const EdgeInsets.only(top: 0.25),
controller: controller, child: Material(
focusNode: focusNode, child: TextField(
style: textStyle, controller: controller,
focusNode: focusNode,
style: textStyle,
),
), ),
), ),
), ),
...@@ -144,16 +147,32 @@ void main() { ...@@ -144,16 +147,32 @@ void main() {
offset: const Offset(20, 20))); offset: const Offset(20, 20)));
await tester.pump(); await tester.pump();
expect(find.byType(EditableText), paints..rrect( expect(editable, paints
rrect: RRect.fromRectAndRadius(Rect.fromLTRB(464.5, 0, 467.5, 16.0), const Radius.circular(1.0)), color: const Color(0xff4285f4)) ..rrect(rrect: RRect.fromRectAndRadius(
Rect.fromLTRB(464.6666564941406, -1.5833333730697632, 466.6666564941406, 16.41666603088379),
const Radius.circular(2.0)),
color: const Color(0xff8e8e93))
..rrect(rrect: RRect.fromRectAndRadius(
Rect.fromLTRB(465.1666564941406, -2.416666269302368, 468.1666564941406, 17.58333396911621),
const Radius.circular(1.0)),
color: const Color(0xbf2196f3))
); );
// Moves the cursor right a few characters. // Moves the cursor right a few characters.
editableTextState.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.Update, editableTextState.updateFloatingCursor(
offset: const Offset(-250, 20))); RawFloatingCursorPoint(
state: FloatingCursorDragState.Update,
expect(find.byType(EditableText), paints..rrect( offset: const Offset(-250, 20)));
rrect: RRect.fromRectAndRadius(Rect.fromLTRB(194.5, 0, 197.5, 16.0), const Radius.circular(1.0)), color: const Color(0xff4285f4))
expect(find.byType(EditableText), paints
..rrect(rrect: RRect.fromRectAndRadius(
Rect.fromLTRB(192.6666717529297, -1.5833333730697632, 194.6666717529297, 16.41666603088379),
const Radius.circular(2.0)),
color: const Color(0xff8e8e93))
..rrect(rrect: RRect.fromRectAndRadius(
Rect.fromLTRB(195.16665649414062, -2.416666269302368, 198.16665649414062, 17.58333396911621),
const Radius.circular(1.0)),
color: const Color(0xbf2196f3))
); );
editableTextState.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.End)); editableTextState.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.End));
......
...@@ -201,28 +201,33 @@ void main() { ...@@ -201,28 +201,33 @@ void main() {
final TextEditingController textController = TextEditingController(); final TextEditingController textController = TextEditingController();
final PageController pageController = PageController(initialPage: 1); final PageController pageController = PageController(initialPage: 1);
await tester.pumpWidget(Directionality( await tester.pumpWidget(
textDirection: TextDirection.ltr, MediaQuery(
child: Material( data: const MediaQueryData(devicePixelRatio: 1.0),
child: PageView( child: Directionality(
controller: pageController, textDirection: TextDirection.ltr,
children: <Widget>[ child: Material(
Container( child: PageView(
color: Colors.red, controller: pageController,
children: <Widget>[
Container(
color: Colors.red,
),
Container(
child: TextField(
controller: textController,
),
color: Colors.green,
),
Container(
color: Colors.red,
),
],
), ),
Container( ),
child: TextField(
controller: textController,
),
color: Colors.green,
),
Container(
color: Colors.red,
),
],
), ),
), ),
)); );
await tester.showKeyboard(find.byType(EditableText)); await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
......
...@@ -11,14 +11,17 @@ void main() { ...@@ -11,14 +11,17 @@ void main() {
String fieldValue; String fieldValue;
Widget builder() { Widget builder() {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
key: formKey, child: Material(
child: TextFormField( child: Form(
onSaved: (String value) { fieldValue = value; }, key: formKey,
child: TextFormField(
onSaved: (String value) { fieldValue = value; },
),
), ),
), ),
), ),
...@@ -45,13 +48,16 @@ void main() { ...@@ -45,13 +48,16 @@ void main() {
String fieldValue; String fieldValue;
Widget builder() { Widget builder() {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
child: TextField( child: Material(
onChanged: (String value) { fieldValue = value; }, child: Form(
child: TextField(
onChanged: (String value) { fieldValue = value; },
),
), ),
), ),
), ),
...@@ -78,15 +84,18 @@ void main() { ...@@ -78,15 +84,18 @@ void main() {
String errorText(String value) => value + '/error'; String errorText(String value) => value + '/error';
Widget builder(bool autovalidate) { Widget builder(bool autovalidate) {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
key: formKey, child: Material(
autovalidate: autovalidate, child: Form(
child: TextFormField( key: formKey,
validator: errorText, autovalidate: autovalidate,
child: TextFormField(
validator: errorText,
),
), ),
), ),
), ),
...@@ -129,22 +138,25 @@ void main() { ...@@ -129,22 +138,25 @@ void main() {
String errorText(String input) => '${fieldKey.currentState.value}/error'; String errorText(String input) => '${fieldKey.currentState.value}/error';
Widget builder() { Widget builder() {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
key: formKey, child: Material(
autovalidate: true, child: Form(
child: ListView( key: formKey,
children: <Widget>[ autovalidate: true,
TextFormField( child: ListView(
key: fieldKey, children: <Widget>[
), TextFormField(
TextFormField( key: fieldKey,
validator: errorText, ),
), TextFormField(
], validator: errorText,
),
],
),
), ),
), ),
), ),
...@@ -172,14 +184,17 @@ void main() { ...@@ -172,14 +184,17 @@ void main() {
final GlobalKey<FormFieldState<String>> inputKey = GlobalKey<FormFieldState<String>>(); final GlobalKey<FormFieldState<String>> inputKey = GlobalKey<FormFieldState<String>>();
Widget builder() { Widget builder() {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
child: TextFormField( child: Material(
key: inputKey, child: Form(
initialValue: 'hello', child: TextFormField(
key: inputKey,
initialValue: 'hello',
),
), ),
), ),
), ),
...@@ -212,14 +227,17 @@ void main() { ...@@ -212,14 +227,17 @@ void main() {
final GlobalKey<FormFieldState<String>> inputKey = GlobalKey<FormFieldState<String>>(); final GlobalKey<FormFieldState<String>> inputKey = GlobalKey<FormFieldState<String>>();
Widget builder() { Widget builder() {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
child: TextFormField( child: Material(
key: inputKey, child: Form(
controller: controller, child: TextFormField(
key: inputKey,
controller: controller,
),
), ),
), ),
), ),
...@@ -254,16 +272,19 @@ void main() { ...@@ -254,16 +272,19 @@ void main() {
final TextEditingController controller = TextEditingController(text: 'Plover'); final TextEditingController controller = TextEditingController(text: 'Plover');
Widget builder() { Widget builder() {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
key: formKey, child: Material(
child: TextFormField( child: Form(
key: inputKey, key: formKey,
controller: controller, child: TextFormField(
// initialValue is 'Plover' key: inputKey,
controller: controller,
// initialValue is 'Plover'
),
), ),
), ),
), ),
...@@ -301,14 +322,17 @@ void main() { ...@@ -301,14 +322,17 @@ void main() {
return StatefulBuilder( return StatefulBuilder(
builder: (BuildContext context, StateSetter setter) { builder: (BuildContext context, StateSetter setter) {
setState = setter; setState = setter;
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
child: TextFormField( child: Material(
key: inputKey, child: Form(
controller: currentController, child: TextFormField(
key: inputKey,
controller: currentController,
),
), ),
), ),
), ),
...@@ -396,16 +420,19 @@ void main() { ...@@ -396,16 +420,19 @@ void main() {
String fieldValue; String fieldValue;
Widget builder(bool remove) { Widget builder(bool remove) {
return Directionality( return MediaQuery(
textDirection: TextDirection.ltr, data: const MediaQueryData(devicePixelRatio: 1.0),
child: Center( child: Directionality(
child: Material( textDirection: TextDirection.ltr,
child: Form( child: Center(
key: formKey, child: Material(
child: remove ? Container() : TextFormField( child: Form(
autofocus: true, key: formKey,
onSaved: (String value) { fieldValue = value; }, child: remove ? Container() : TextFormField(
validator: (String value) { return value.isEmpty ? null : 'yes'; } autofocus: true,
onSaved: (String value) { fieldValue = value; },
validator: (String value) { return value.isEmpty ? null : 'yes'; }
),
), ),
), ),
), ),
......
...@@ -6,14 +6,18 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -6,14 +6,18 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
testWidgets('PhysicalModel - creates a physical model layer when it needs compositing', (WidgetTester tester) async { testWidgets('PhysicalModel - creates a physical model layer when it needs compositing', (WidgetTester tester) async {
debugDisableShadows = false; debugDisableShadows = false;
await tester.pumpWidget(Directionality( await tester.pumpWidget(
textDirection: TextDirection.ltr, MediaQuery(
child: PhysicalModel( data: const MediaQueryData(devicePixelRatio: 1.0),
shape: BoxShape.rectangle, child: Directionality(
color: Colors.grey, textDirection: TextDirection.ltr,
shadowColor: Colors.red, child: PhysicalModel(
elevation: 1.0, shape: BoxShape.rectangle,
child: Material(child: TextField(controller: TextEditingController())), color: Colors.grey,
shadowColor: Colors.red,
elevation: 1.0,
child: Material(child: TextField(controller: TextEditingController())),
),
), ),
), ),
); );
......
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