Unverified Commit c8057bc5 authored by chunhtai's avatar chunhtai Committed by GitHub

Add default selection style (#100719)

parent 4aa843a6
......@@ -43,6 +43,12 @@ import 'theme.dart';
/// widget, which the [CupertinoApp] composes. If you use Material widgets, a
/// [MaterialApp] also creates the needed dependencies for Cupertino widgets.
///
/// {@template flutter.cupertino.CupertinoApp.defaultSelectionStyle}
/// The [CupertinoApp] automatically creates a [DefaultSelectionStyle] with
/// selectionColor sets to [CupertinoThemeData.primaryColor] with 0.2 opacity
/// and cursorColor sets to [CupertinoThemeData.primaryColor].
/// {@endtemplate}
///
/// Use this widget with caution on Android since it may produce behaviors
/// Android users are not expecting such as:
///
......@@ -586,10 +592,14 @@ class _CupertinoAppState extends State<CupertinoApp> {
data: CupertinoUserInterfaceLevelData.base,
child: CupertinoTheme(
data: effectiveThemeData,
child: HeroControllerScope(
controller: _heroController,
child: Builder(
builder: _buildWidgetApp,
child: DefaultSelectionStyle(
selectionColor: effectiveThemeData.primaryColor.withOpacity(0.2),
cursorColor: effectiveThemeData.primaryColor,
child: HeroControllerScope(
controller: _heroController,
child: Builder(
builder: _buildWidgetApp,
),
),
),
),
......
......@@ -712,7 +712,8 @@ class CupertinoTextField extends StatefulWidget {
/// The color to use when painting the cursor.
///
/// Defaults to the [CupertinoThemeData.primaryColor] of the ambient theme,
/// Defaults to the [DefaultSelectionStyle.cursorColor]. If that color is
/// null, it uses the [CupertinoThemeData.primaryColor] of the ambient theme,
/// which itself defaults to [CupertinoColors.activeBlue] in the light theme
/// and [CupertinoColors.activeOrange] in the dark theme.
final Color? cursorColor;
......@@ -1180,7 +1181,11 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio
final TextStyle placeholderStyle = textStyle.merge(resolvedPlaceholderStyle);
final Brightness keyboardAppearance = widget.keyboardAppearance ?? CupertinoTheme.brightnessOf(context);
final Color cursorColor = CupertinoDynamicColor.maybeResolve(widget.cursorColor, context) ?? themeData.primaryColor;
final Color cursorColor = CupertinoDynamicColor.maybeResolve(
widget.cursorColor ?? DefaultSelectionStyle.of(context).cursorColor,
context,
) ?? themeData.primaryColor;
final Color disabledColor = CupertinoDynamicColor.resolve(_kDisabledBackground, context);
final Color? decorationColor = CupertinoDynamicColor.maybeResolve(widget.decoration?.color, context);
......@@ -1208,7 +1213,10 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio
color: enabled ? decorationColor : disabledColor,
);
final Color selectionColor = CupertinoTheme.of(context).primaryColor.withOpacity(0.2);
final Color selectionColor = CupertinoDynamicColor.maybeResolve(
DefaultSelectionStyle.of(context).selectionColor,
context,
) ?? CupertinoTheme.of(context).primaryColor.withOpacity(0.2);
final Widget paddedEditable = Padding(
padding: widget.padding,
......
......@@ -82,6 +82,14 @@ enum ThemeMode {
/// This widget also configures the observer of the top-level [Navigator] (if
/// any) to perform [Hero] animations.
///
/// {@template flutter.material.MaterialApp.defaultSelectionStyle}
/// The [MaterialApp] automatically creates a [DefaultSelectionStyle]. It uses
/// the colors in the [ThemeData.textSelectionTheme] if they are not null;
/// otherwise, the [MaterialApp] sets [DefaultSelectionStyle.selectionColor] to
/// [ColorScheme.primary] with 0.4 opacity and
/// [DefaultSelectionStyle.cursorColor] to [ColorScheme.primary].
/// {@endtemplate}
///
/// If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null,
/// and [builder] is not null, then no [Navigator] is created.
///
......@@ -874,29 +882,35 @@ class _MaterialAppState extends State<MaterialApp> {
theme = widget.highContrastTheme;
}
theme ??= widget.theme ?? ThemeData.light();
final Color effectiveSelectionColor = theme.textSelectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
final Color effectiveCursorColor = theme.textSelectionTheme.cursorColor ?? theme.colorScheme.primary;
return ScaffoldMessenger(
key: widget.scaffoldMessengerKey,
child: AnimatedTheme(
data: theme,
child: widget.builder != null
? Builder(
builder: (BuildContext context) {
// Why are we surrounding a builder with a builder?
//
// The widget.builder may contain code that invokes
// Theme.of(), which should return the theme we selected
// above in AnimatedTheme. However, if we invoke
// widget.builder() directly as the child of AnimatedTheme
// then there is no Context separating them, and the
// widget.builder() will not find the theme. Therefore, we
// surround widget.builder with yet another builder so that
// a context separates them and Theme.of() correctly
// resolves to the theme we passed to AnimatedTheme.
return widget.builder!(context, child);
},
)
: child ?? const SizedBox.shrink(),
child: DefaultSelectionStyle(
selectionColor: effectiveSelectionColor,
cursorColor: effectiveCursorColor,
child: AnimatedTheme(
data: theme,
child: widget.builder != null
? Builder(
builder: (BuildContext context) {
// Why are we surrounding a builder with a builder?
//
// The widget.builder may contain code that invokes
// Theme.of(), which should return the theme we selected
// above in AnimatedTheme. However, if we invoke
// widget.builder() directly as the child of AnimatedTheme
// then there is no Context separating them, and the
// widget.builder() will not find the theme. Therefore, we
// surround widget.builder with yet another builder so that
// a context separates them and Theme.of() correctly
// resolves to the theme we passed to AnimatedTheme.
return widget.builder!(context, child);
},
)
: child ?? const SizedBox.shrink(),
),
),
);
}
......
......@@ -12,7 +12,6 @@ import 'package:flutter/rendering.dart';
import 'desktop_text_selection.dart';
import 'feedback.dart';
import 'text_selection.dart';
import 'text_selection_theme.dart';
import 'theme.dart';
/// An eyeballed value that moves the cursor slightly left of where it is
......@@ -359,9 +358,14 @@ class SelectableText extends StatefulWidget {
/// {@macro flutter.widgets.editableText.cursorRadius}
final Radius? cursorRadius;
/// The color to use when painting the cursor.
/// The color of the cursor.
///
/// Defaults to the theme's `cursorColor` when null.
/// The cursor indicates the current text insertion point.
///
/// If null then [DefaultSelectionStyle.cursorColor] is used. If that is also
/// null and [ThemeData.platform] is [TargetPlatform.iOS] or
/// [TargetPlatform.macOS], then [CupertinoThemeData.primaryColor] is used.
/// Otherwise [ColorScheme.primary] of [ThemeData.colorScheme] is used.
final Color? cursorColor;
/// Controls how tall the selection highlight boxes are computed to be.
......@@ -597,14 +601,14 @@ class _SelectableTextState extends State<SelectableText> implements TextSelectio
);
final ThemeData theme = Theme.of(context);
final TextSelectionThemeData selectionTheme = TextSelectionTheme.of(context);
final DefaultSelectionStyle selectionStyle = DefaultSelectionStyle.of(context);
final FocusNode focusNode = _effectiveFocusNode;
TextSelectionControls? textSelectionControls = widget.selectionControls;
final bool paintCursorAboveText;
final bool cursorOpacityAnimates;
Offset? cursorOffset;
Color? cursorColor = widget.cursorColor;
final Color cursorColor;
final Color selectionColor;
Radius? cursorRadius = widget.cursorRadius;
......@@ -615,8 +619,8 @@ class _SelectableTextState extends State<SelectableText> implements TextSelectio
textSelectionControls ??= cupertinoTextSelectionControls;
paintCursorAboveText = true;
cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionTheme.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorRadius ??= const Radius.circular(2.0);
cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
break;
......@@ -627,8 +631,8 @@ class _SelectableTextState extends State<SelectableText> implements TextSelectio
textSelectionControls ??= cupertinoDesktopTextSelectionControls;
paintCursorAboveText = true;
cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionTheme.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorRadius ??= const Radius.circular(2.0);
cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
break;
......@@ -639,8 +643,8 @@ class _SelectableTextState extends State<SelectableText> implements TextSelectio
textSelectionControls ??= materialTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
break;
case TargetPlatform.linux:
......@@ -649,8 +653,8 @@ class _SelectableTextState extends State<SelectableText> implements TextSelectio
textSelectionControls ??= desktopTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
break;
}
......
......@@ -19,7 +19,6 @@ import 'material_localizations.dart';
import 'material_state.dart';
import 'selectable_text.dart' show iOSHorizontalOffset;
import 'text_selection.dart';
import 'text_selection_theme.dart';
import 'theme.dart';
export 'package:flutter/services.dart' show TextInputType, TextInputAction, TextCapitalization, SmartQuotesType, SmartDashesType;
......@@ -615,7 +614,7 @@ class TextField extends StatefulWidget {
/// the field.
///
/// If this is null it will default to the ambient
/// [TextSelectionThemeData.cursorColor]. If that is null, and the
/// [DefaultSelectionStyle.cursorColor]. If that is null, and the
/// [ThemeData.platform] is [TargetPlatform.iOS] or [TargetPlatform.macOS]
/// it will use [CupertinoThemeData.primaryColor]. Otherwise it will use
/// the value of [ColorScheme.primary] of [ThemeData.colorScheme].
......@@ -1117,7 +1116,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
);
final ThemeData theme = Theme.of(context);
final TextSelectionThemeData selectionTheme = TextSelectionTheme.of(context);
final DefaultSelectionStyle selectionStyle = DefaultSelectionStyle.of(context);
final TextStyle style = theme.textTheme.subtitle1!.merge(widget.style);
final Brightness keyboardAppearance = widget.keyboardAppearance ?? theme.brightness;
final TextEditingController controller = _effectiveController;
......@@ -1135,7 +1134,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
final bool paintCursorAboveText;
final bool cursorOpacityAnimates;
Offset? cursorOffset;
Color? cursorColor = widget.cursorColor;
final Color cursorColor;
final Color selectionColor;
Color? autocorrectionTextRectColor;
Radius? cursorRadius = widget.cursorRadius;
......@@ -1148,8 +1147,8 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
textSelectionControls ??= cupertinoTextSelectionControls;
paintCursorAboveText = true;
cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionTheme.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorRadius ??= const Radius.circular(2.0);
cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
autocorrectionTextRectColor = selectionColor;
......@@ -1161,8 +1160,8 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
textSelectionControls ??= cupertinoDesktopTextSelectionControls;
paintCursorAboveText = true;
cursorOpacityAnimates = true;
cursorColor ??= selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionTheme.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorRadius ??= const Radius.circular(2.0);
cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
handleDidGainAccessibilityFocus = () {
......@@ -1179,8 +1178,8 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
textSelectionControls ??= materialTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
break;
case TargetPlatform.linux:
......@@ -1188,8 +1187,8 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
textSelectionControls ??= desktopTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
break;
case TargetPlatform.windows:
......@@ -1197,8 +1196,8 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
textSelectionControls ??= desktopTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionTheme.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? theme.colorScheme.primary;
selectionColor = selectionStyle.selectionColor ?? theme.colorScheme.primary.withOpacity(0.40);
handleDidGainAccessibilityFocus = () {
// Automatically activate the TextField when it receives accessibility focus.
if (!_effectiveFocusNode.hasFocus && _effectiveFocusNode.canRequestFocus) {
......
......@@ -129,6 +129,9 @@ class TextSelectionThemeData with Diagnosticable {
/// )
/// ```
/// {@end-tool}
///
/// This widget also creates a [DefaultSelectionStyle] for its subtree with
/// [data].
class TextSelectionTheme extends InheritedTheme {
/// Creates a text selection theme widget that specifies the text
/// selection properties for all widgets below it in the widget tree.
......@@ -138,11 +141,28 @@ class TextSelectionTheme extends InheritedTheme {
Key? key,
required this.data,
required Widget child,
}) : assert(data != null), super(key: key, child: child);
}) : assert(data != null),
_child = child,
// See `get child` override below.
super(key: key, child: const _NullWidget());
/// The properties for descendant [TextField] and [SelectableText] widgets.
final TextSelectionThemeData data;
// Overriding the getter to insert `DefaultSelectionStyle` into the subtree
// without breaking API. In general, this approach should be avoided
// because it relies on an implementation detail of ProxyWidget. This
// workaround is necessary because TextSelectionTheme is const.
@override
Widget get child {
return DefaultSelectionStyle(
selectionColor: data.selectionColor,
cursorColor: data.cursorColor,
child: _child,
);
}
final Widget _child;
/// Returns the [data] from the closest [TextSelectionTheme] ancestor. If
/// there is no ancestor, it returns [ThemeData.textSelectionTheme].
/// Applications can assume that the returned value will not be null.
......@@ -165,3 +185,10 @@ class TextSelectionTheme extends InheritedTheme {
@override
bool updateShouldNotify(TextSelectionTheme oldWidget) => data != oldWidget.data;
}
class _NullWidget extends Widget {
const _NullWidget();
@override
Element createElement() => throw UnimplementedError();
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
import 'framework.dart';
import 'inherited_theme.dart';
/// The selection style to apply to descendant [EditableText] widgets which
/// don't have an explicit style.
///
/// {@macro flutter.cupertino.CupertinoApp.defaultSelectionStyle}
///
/// {@macro flutter.material.MaterialApp.defaultSelectionStyle}
///
/// See also:
/// * [TextSelectionTheme]: which also creates a [DefaultSelectionStyle] for
/// the subtree.
class DefaultSelectionStyle extends InheritedTheme {
/// Creates a default selection style widget that specifies the selection
/// properties for all widgets below it in the widget tree.
const DefaultSelectionStyle({
Key? key,
this.cursorColor,
this.selectionColor,
required Widget child,
}) : super(key: key, child: child);
/// A const-constructable default selection style that provides fallback
/// values.
///
/// Returned from [of] when the given [BuildContext] doesn't have an enclosing
/// default selection style.
///
/// This constructor creates a [DefaultTextStyle] with an invalid [child],
/// which means the constructed value cannot be incorporated into the tree.
const DefaultSelectionStyle.fallback({ Key? key })
: cursorColor = null,
selectionColor = null,
super(key: key, child: const _NullWidget());
/// The color of the text field's cursor.
///
/// The cursor indicates the current location of the text insertion point in
/// the field.
final Color? cursorColor;
/// The background color of selected text.
final Color? selectionColor;
/// The closest instance of this class that encloses the given context.
///
/// If no such instance exists, returns an instance created by
/// [DefaultSelectionStyle.fallback], which contains fallback values.
///
/// Typical usage is as follows:
///
/// ```dart
/// DefaultSelectionStyle style = DefaultSelectionStyle.of(context);
/// ```
static DefaultSelectionStyle of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<DefaultSelectionStyle>() ?? const DefaultSelectionStyle.fallback();
}
@override
Widget wrap(BuildContext context, Widget child) {
return DefaultSelectionStyle(
cursorColor: cursorColor,
selectionColor: selectionColor,
child: child
);
}
@override
bool updateShouldNotify(DefaultSelectionStyle oldWidget) {
return cursorColor != oldWidget.cursorColor ||
selectionColor != oldWidget.selectionColor;
}
}
class _NullWidget extends StatelessWidget {
const _NullWidget();
@override
Widget build(BuildContext context) {
throw FlutterError(
'A DefaultTextStyle constructed with DefaultTextStyle.fallback cannot be incorporated into the widget tree, '
'it is meant only to provide a fallback value returned by DefaultTextStyle.of() '
'when no enclosing default text style is present in a BuildContext.',
);
}
}
......@@ -20,6 +20,7 @@ import 'basic.dart';
import 'binding.dart';
import 'constants.dart';
import 'debug.dart';
import 'default_selection_style.dart';
import 'focus_manager.dart';
import 'focus_scope.dart';
import 'focus_traversal.dart';
......@@ -961,6 +962,9 @@ class EditableText extends StatefulWidget {
/// The color to use when painting the selection.
///
/// If this property is null, this widget gets the selection color from the
/// [DefaultSelectionStyle].
///
/// For [CupertinoTextField]s, the value is set to the ambient
/// [CupertinoThemeData.primaryColor] with 20% opacity. For [TextField]s, the
/// value is set to the ambient [TextSelectionThemeData.selectionColor].
......@@ -3250,6 +3254,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
assert(debugCheckHasMediaQuery(context));
super.build(context); // See AutomaticKeepAliveClientMixin.
final Color? effectiveSelectionColor = widget.selectionColor ?? DefaultSelectionStyle.of(context).selectionColor;
final TextSelectionControls? controls = widget.selectionControls;
return MouseRegion(
cursor: widget.mouseCursor ?? SystemMouseCursors.text,
......@@ -3311,7 +3317,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
minLines: widget.minLines,
expands: widget.expands,
strutStyle: widget.strutStyle,
selectionColor: widget.selectionColor,
selectionColor: effectiveSelectionColor,
textScaleFactor: widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
textAlign: widget.textAlign,
textDirection: _textDirection,
......
......@@ -34,6 +34,7 @@ export 'src/widgets/bottom_navigation_bar_item.dart';
export 'src/widgets/color_filter.dart';
export 'src/widgets/container.dart';
export 'src/widgets/debug.dart';
export 'src/widgets/default_selection_style.dart';
export 'src/widgets/default_text_editing_shortcuts.dart';
export 'src/widgets/desktop_text_selection_toolbar_layout_delegate.dart';
export 'src/widgets/dismissible.dart';
......
......@@ -17,6 +17,7 @@ import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle, Color;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior, PointerDeviceKind, kSecondaryMouseButton, kDoubleTapTimeout;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -406,6 +407,32 @@ void main() {
},
);
testWidgets(
'uses DefaultSelectionStyle for selection and cursor colors if provided',
(WidgetTester tester) async {
const Color selectionColor = Colors.black;
const Color cursorColor = Colors.white;
await tester.pumpWidget(
const CupertinoApp(
home: Center(
child: DefaultSelectionStyle(
selectionColor: selectionColor,
cursorColor: cursorColor,
child: CupertinoTextField(
autofocus: true,
)
),
),
),
);
await tester.pump();
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.widget.selectionColor, selectionColor);
expect(state.widget.cursorColor, cursorColor);
},
);
testWidgets(
'multi-lined text fields are intrinsically taller no-strut',
(WidgetTester tester) async {
......
......@@ -172,6 +172,7 @@ void main() {
' _InheritedTheme\n'
' Theme\n'
' AnimatedTheme\n'
' DefaultSelectionStyle\n'
' _ScaffoldMessengerScope\n'
' ScaffoldMessenger\n'
' Builder\n'
......
......@@ -286,6 +286,27 @@ void main() {
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
);
testWidgets('uses DefaultSelectionStyle for selection and cursor colors if provided', (WidgetTester tester) async {
const Color selectionColor = Colors.orange;
const Color cursorColor = Colors.red;
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: DefaultSelectionStyle(
selectionColor: selectionColor,
cursorColor: cursorColor,
child: TextField(autofocus: true),
),
),
),
);
await tester.pump();
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.widget.selectionColor, selectionColor);
expect(state.widget.cursorColor, cursorColor);
});
testWidgets('Activates the text field when receives semantics focus on Mac, Windows', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner!;
......
......@@ -262,4 +262,43 @@ void main() {
final RenderEditable renderSelectable = selectableTextState.renderEditable;
expect(renderSelectable.cursorColor, cursorColor.withAlpha(0));
});
testWidgets('TextSelectionThem overrides DefaultSelectionStyle', (WidgetTester tester) async {
const Color themeSelectionColor = Color(0xffaabbcc);
const Color themeCursorColor = Color(0x00ccbbaa);
const Color defaultSelectionColor = Color(0xffaa1111);
const Color defaultCursorColor = Color(0x00cc2222);
final Key defaultSelectionStyle = UniqueKey();
final Key themeStyle = UniqueKey();
// Test TextField's cursor color.
await tester.pumpWidget(
MaterialApp(
home: DefaultSelectionStyle(
selectionColor: defaultSelectionColor,
cursorColor: defaultCursorColor,
child: Container(
key: defaultSelectionStyle,
child: TextSelectionTheme(
data: const TextSelectionThemeData(
selectionColor: themeSelectionColor,
cursorColor: themeCursorColor,
),
child: Placeholder(
key: themeStyle,
),
),
)
),
),
);
final BuildContext defaultSelectionStyleContext = tester.element(find.byKey(defaultSelectionStyle));
DefaultSelectionStyle style = DefaultSelectionStyle.of(defaultSelectionStyleContext);
expect(style.selectionColor, defaultSelectionColor);
expect(style.cursorColor, defaultCursorColor);
final BuildContext themeStyleContext = tester.element(find.byKey(themeStyle));
style = DefaultSelectionStyle.of(themeStyleContext);
expect(style.selectionColor, themeSelectionColor);
expect(style.cursorColor, themeCursorColor);
});
}
......@@ -660,6 +660,36 @@ void main() {
expect(focusNode.hasFocus, isFalse);
});
testWidgets('use DefaultSelectionStyle for selection color', (WidgetTester tester) async {
const TextEditingValue value = TextEditingValue(
text: 'test test',
selection: TextSelection(affinity: TextAffinity.upstream, baseOffset: 5, extentOffset: 7),
);
const Color selectionColor = Colors.orange;
controller.value = value;
await tester.pumpWidget(
DefaultSelectionStyle(
selectionColor: selectionColor,
child: MediaQuery(
data: const MediaQueryData(),
child: Directionality(
textDirection: TextDirection.ltr,
child: EditableText(
controller: controller,
backgroundCursorColor: Colors.grey,
focusNode: focusNode,
keyboardType: TextInputType.multiline,
style: textStyle,
cursorColor: cursorColor,
),
),
)
),
);
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.renderEditable.selectionColor, selectionColor);
});
testWidgets('visiblePassword keyboard is requested when set explicitly', (WidgetTester tester) async {
await tester.pumpWidget(
MediaQuery(
......
......@@ -297,6 +297,27 @@ void main() {
expect(tester.testTextInput.hasAnyClients, false);
});
testWidgets('uses DefaultSelectionStyle for selection and cursor colors if provided', (WidgetTester tester) async {
const Color selectionColor = Colors.orange;
const Color cursorColor = Colors.red;
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: DefaultSelectionStyle(
selectionColor: selectionColor,
cursorColor: cursorColor,
child: SelectableText('text'),
),
),
),
);
await tester.pump();
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.widget.selectionColor, selectionColor);
expect(state.widget.cursorColor, cursorColor);
});
testWidgets('Selectable Text has adaptive size', (WidgetTester tester) async {
await tester.pumpWidget(
boilerplate(
......
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