From cb92ffc7fa2a3731b98bd4dc7ddfedcc1f50ea6e Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Wed, 16 Sep 2020 17:22:04 -0700 Subject: [PATCH] TextSelectionTheme support (step 2 of 3) (#65044) --- .../flutter/lib/src/material/text_field.dart | 12 ++--- .../flutter/lib/src/material/theme_data.dart | 11 +++- .../test/material/text_selection_test.dart | 4 +- .../material/text_selection_theme_test.dart | 53 +++---------------- .../widgets/editable_text_cursor_test.dart | 24 +++++---- 5 files changed, 37 insertions(+), 67 deletions(-) diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 2db56a3adbb..66e0232df70 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -1098,11 +1098,6 @@ class _TextFieldState extends State with RestorationMixin implements } } - Color _defaultSelectionColor(BuildContext context, Color primary) { - final bool isDark = Theme.of(context).brightness == Brightness.dark; - return primary.withOpacity(isDark ? 0.40 : 0.12); - } - @override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); @@ -1136,13 +1131,14 @@ class _TextFieldState extends State with RestorationMixin implements switch (theme.platform) { case TargetPlatform.iOS: case TargetPlatform.macOS: + final CupertinoThemeData cupertinoTheme = CupertinoTheme.of(context); forcePressEnabled = true; textSelectionControls = cupertinoTextSelectionControls; paintCursorAboveText = true; cursorOpacityAnimates = true; if (theme.useTextSelectionTheme) { - cursorColor ??= selectionTheme.cursorColor ?? CupertinoTheme.of(context).primaryColor; - selectionColor = selectionTheme.selectionColor ?? _defaultSelectionColor(context, CupertinoTheme.of(context).primaryColor); + cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor; + selectionColor = selectionTheme.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40); } else { cursorColor ??= CupertinoTheme.of(context).primaryColor; selectionColor = theme.textSelectionColor; @@ -1162,7 +1158,7 @@ class _TextFieldState extends State with RestorationMixin implements cursorOpacityAnimates = false; if (theme.useTextSelectionTheme) { cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary; - selectionColor = selectionTheme.selectionColor ?? _defaultSelectionColor(context, theme.colorScheme.primary); + selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40); } else { cursorColor ??= theme.cursorColor; selectionColor = theme.textSelectionColor; diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index 566193e6eda..605c6c5d477 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -405,7 +405,7 @@ class ThemeData with Diagnosticable { dataTableTheme ??= const DataTableThemeData(); fixTextFieldOutlineLabel ??= false; - useTextSelectionTheme ??= false; + useTextSelectionTheme ??= true; return ThemeData.raw( visualDensity: visualDensity, @@ -883,12 +883,21 @@ class ThemeData with Diagnosticable { final Color secondaryHeaderColor; /// The color of text selections in text fields, such as [TextField]. + /// + /// By default this property is no longer used. It has been replaced with + /// [TextSelectionThemeData.selectionColor] and will soon be deprecated. final Color textSelectionColor; /// The color of cursors in Material-style text fields, such as [TextField]. + /// + /// By default this property is no longer used. It has been replaced with + /// [TextSelectionThemeData.cursorColor] and will soon be deprecated. final Color cursorColor; /// The color of the handles used to adjust what part of the text is currently selected. + /// + /// By default this property is no longer used. It has been replaced with + /// [TextSelectionThemeData.selectionHandleColor] and will soon be deprecated. final Color textSelectionHandleColor; /// A color that contrasts with the [primaryColor], e.g. used as the diff --git a/packages/flutter/test/material/text_selection_test.dart b/packages/flutter/test/material/text_selection_test.dart index ec367610103..57f6db478ef 100644 --- a/packages/flutter/test/material/text_selection_test.dart +++ b/packages/flutter/test/material/text_selection_test.dart @@ -554,7 +554,9 @@ void main() { await tester.pumpWidget(RepaintBoundary( child: Theme( data: ThemeData( - textSelectionHandleColor: const Color(0x550000AA), + textSelectionTheme: const TextSelectionThemeData( + selectionHandleColor: Color(0x550000AA), + ), ), isMaterialAppTheme: true, child: Builder( diff --git a/packages/flutter/test/material/text_selection_theme_test.dart b/packages/flutter/test/material/text_selection_theme_test.dart index ba3def44ea0..cb8df083969 100644 --- a/packages/flutter/test/material/text_selection_theme_test.dart +++ b/packages/flutter/test/material/text_selection_theme_test.dart @@ -58,6 +58,10 @@ void main() { }); testWidgets('Empty textSelectionTheme will use defaults', (WidgetTester tester) async { + const Color defaultCursorColor = Color(0x002196f3); + const Color defaultSelectionColor = Color(0x662196f3); + const Color defaultSelectionHandleColor = Color(0xff2196f3); + // Test TextField's cursor & selection color. await tester.pumpWidget( const MaterialApp( @@ -69,52 +73,12 @@ void main() { await tester.pumpAndSettle(); final EditableTextState editableTextState = tester.firstState(find.byType(EditableText)); final RenderEditable renderEditable = editableTextState.renderEditable; - expect(renderEditable.cursorColor, const Color(0x004285f4)); - expect(renderEditable.selectionColor, const Color(0xFF90CAF9)); + expect(renderEditable.cursorColor, defaultCursorColor); + expect(Color(renderEditable.selectionColor.value), defaultSelectionColor); // Test the selection handle color. await tester.pumpWidget( MaterialApp( - home: Material( - child: Builder( - builder: (BuildContext context) { - return materialTextSelectionControls.buildHandle( - context, TextSelectionHandleType.left, 10.0 - ); - }, - ), - ), - ), - ); - await tester.pumpAndSettle(); - final RenderBox handle = tester.firstRenderObject(find.byType(CustomPaint)); - expect(handle, paints..path(color: Colors.blue[300])); - - }); - - testWidgets('Empty textSelectionTheme with useTextSelectionTheme set will use new defaults', (WidgetTester tester) async { - final ThemeData theme = ThemeData.fallback().copyWith(useTextSelectionTheme: true); - final Color primaryColor = Color(theme.colorScheme.primary.value); - - // Test TextField's cursor & selection color. - await tester.pumpWidget( - MaterialApp( - theme: theme, - home: const Material( - child: TextField(), - ), - ), - ); - await tester.pumpAndSettle(); - final EditableTextState editableTextState = tester.firstState(find.byType(EditableText)); - final RenderEditable renderEditable = editableTextState.renderEditable; - expect(renderEditable.cursorColor, primaryColor.withAlpha(0)); - expect(Color(renderEditable.selectionColor.value), primaryColor.withOpacity(0.12)); - - // Test the selection handle color. - await tester.pumpWidget( - MaterialApp( - theme: theme, home: Material( child: Builder( builder: (BuildContext context) { @@ -128,7 +92,7 @@ void main() { ); await tester.pumpAndSettle(); final RenderBox handle = tester.firstRenderObject(find.byType(CustomPaint)); - expect(handle, paints..path(color: primaryColor)); + expect(handle, paints..path(color: defaultSelectionHandleColor)); }); testWidgets('ThemeDate.textSelectionTheme will be used if provided', (WidgetTester tester) async { @@ -138,7 +102,6 @@ void main() { selectionHandleColor: Color(0x00ccbbaa), ); final ThemeData theme = ThemeData.fallback().copyWith( - useTextSelectionTheme: true, textSelectionTheme: textSelectionTheme, ); @@ -184,7 +147,6 @@ void main() { selectionHandleColor: Color(0x00ccbbaa), ); final ThemeData theme = ThemeData.fallback().copyWith( - useTextSelectionTheme: true, textSelectionTheme: defaultTextSelectionTheme, ); const TextSelectionThemeData widgetTextSelectionTheme = TextSelectionThemeData( @@ -240,7 +202,6 @@ void main() { selectionHandleColor: Color(0x00ccbbaa), ); final ThemeData theme = ThemeData.fallback().copyWith( - useTextSelectionTheme: true, textSelectionTheme: defaultTextSelectionTheme, ); const TextSelectionThemeData widgetTextSelectionTheme = TextSelectionThemeData( diff --git a/packages/flutter/test/widgets/editable_text_cursor_test.dart b/packages/flutter/test/widgets/editable_text_cursor_test.dart index 5b8c1422161..f79db156d83 100644 --- a/packages/flutter/test/widgets/editable_text_cursor_test.dart +++ b/packages/flutter/test/widgets/editable_text_cursor_test.dart @@ -208,6 +208,7 @@ void main() { }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); testWidgets('Cursor does not animate on Android', (WidgetTester tester) async { + final Color defaultCursorColor = Color(ThemeData.fallback().colorScheme.primary.value); const Widget widget = MaterialApp( home: Material( child: TextField( @@ -225,12 +226,12 @@ void main() { await tester.pump(); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rect(color: const Color(0xff4285f4))); + expect(renderEditable, paints..rect(color: defaultCursorColor)); // Android cursor goes from exactly on to exactly off on the 500ms dot. await tester.pump(const Duration(milliseconds: 499)); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rect(color: const Color(0xff4285f4))); + expect(renderEditable, paints..rect(color: defaultCursorColor)); await tester.pump(const Duration(milliseconds: 1)); expect(renderEditable.cursorColor.alpha, 0); @@ -239,7 +240,7 @@ void main() { await tester.pump(const Duration(milliseconds: 500)); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rect(color: const Color(0xff4285f4))); + expect(renderEditable, paints..rect(color: defaultCursorColor)); await tester.pump(const Duration(milliseconds: 500)); expect(renderEditable.cursorColor.alpha, 0); @@ -248,6 +249,7 @@ void main() { testWidgets('Cursor does not animates when debugDeterministicCursor is set', (WidgetTester tester) async { EditableText.debugDeterministicCursor = true; + final Color defaultCursorColor = Color(ThemeData.fallback().colorScheme.primary.value); const Widget widget = MaterialApp( home: Material( child: TextField( @@ -268,24 +270,24 @@ void main() { await tester.pump(); await tester.pump(const Duration(milliseconds: 200)); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rrect(color: const Color(0xff2196f3))); + expect(renderEditable, paints..rrect(color: defaultCursorColor)); // Cursor draw never changes. await tester.pump(const Duration(milliseconds: 200)); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rrect(color: const Color(0xff2196f3))); + expect(renderEditable, paints..rrect(color: defaultCursorColor)); // No more transient calls. await tester.pumpAndSettle(); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rrect(color: const Color(0xff2196f3))); + expect(renderEditable, paints..rrect(color: defaultCursorColor)); EditableText.debugDeterministicCursor = false; }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); testWidgets('Cursor does not animate on Android when debugDeterministicCursor is set', (WidgetTester tester) async { + final Color defaultCursorColor = Color(ThemeData.fallback().colorScheme.primary.value); EditableText.debugDeterministicCursor = true; - const Widget widget = MaterialApp( home: Material( child: TextField( @@ -303,21 +305,21 @@ void main() { await tester.pump(); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rect(color: const Color(0xff4285f4))); + expect(renderEditable, paints..rect(color: defaultCursorColor)); await tester.pump(const Duration(milliseconds: 500)); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rect(color: const Color(0xff4285f4))); + expect(renderEditable, paints..rect(color: defaultCursorColor)); // Cursor draw never changes. await tester.pump(const Duration(milliseconds: 500)); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rect(color: const Color(0xff4285f4))); + expect(renderEditable, paints..rect(color: defaultCursorColor)); // No more transient calls. await tester.pumpAndSettle(); expect(renderEditable.cursorColor.alpha, 255); - expect(renderEditable, paints..rect(color: const Color(0xff4285f4))); + expect(renderEditable, paints..rect(color: defaultCursorColor)); EditableText.debugDeterministicCursor = false; });