1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'basic.dart';
6import 'framework.dart';
7import 'inherited_theme.dart';
8
9// Examples can assume:
10// late BuildContext context;
11
12/// The selection style to apply to descendant [EditableText] widgets which
13/// don't have an explicit style.
14///
15/// {@macro flutter.cupertino.CupertinoApp.defaultSelectionStyle}
16///
17/// {@macro flutter.material.MaterialApp.defaultSelectionStyle}
18///
19/// See also:
20/// * [TextSelectionTheme]: which also creates a [DefaultSelectionStyle] for
21/// the subtree.
22class DefaultSelectionStyle extends InheritedTheme {
23 /// Creates a default selection style widget that specifies the selection
24 /// properties for all widgets below it in the widget tree.
25 const DefaultSelectionStyle({
26 super.key,
27 this.cursorColor,
28 this.selectionColor,
29 this.mouseCursor,
30 required super.child,
31 });
32
33 /// A const-constructable default selection style that provides fallback
34 /// values (null).
35 ///
36 /// Returned from [of] when the given [BuildContext] doesn't have an enclosing
37 /// default selection style.
38 ///
39 /// This constructor creates a [DefaultTextStyle] with an invalid [child],
40 /// which means the constructed value cannot be incorporated into the tree.
41 const DefaultSelectionStyle.fallback({ super.key })
42 : cursorColor = null,
43 selectionColor = null,
44 mouseCursor = null,
45 super(child: const _NullWidget());
46
47 /// Creates a default selection style that overrides the selection styles in
48 /// scope at this point in the widget tree.
49 ///
50 /// Any Arguments that are not null replace the corresponding properties on the
51 /// default selection style for the [BuildContext] where the widget is inserted.
52 static Widget merge({
53 Key? key,
54 Color? cursorColor,
55 Color? selectionColor,
56 MouseCursor? mouseCursor,
57 required Widget child,
58 }) {
59 return Builder(
60 builder: (BuildContext context) {
61 final DefaultSelectionStyle parent = DefaultSelectionStyle.of(context);
62 return DefaultSelectionStyle(
63 key: key,
64 cursorColor: cursorColor ?? parent.cursorColor,
65 selectionColor: selectionColor ?? parent.selectionColor,
66 mouseCursor: mouseCursor ?? parent.mouseCursor,
67 child: child,
68 );
69 },
70 );
71 }
72
73 /// The default cursor and selection color (semi-transparent grey).
74 ///
75 /// This is the color that the [Text] widget uses when the specified selection
76 /// color is null.
77 static const Color defaultColor = Color(0x80808080);
78
79 /// The color of the text field's cursor.
80 ///
81 /// The cursor indicates the current location of the text insertion point in
82 /// the field.
83 final Color? cursorColor;
84
85 /// The background color of selected text.
86 final Color? selectionColor;
87
88 /// The [MouseCursor] for mouse pointers hovering over selectable Text widgets.
89 ///
90 /// If this property is null, [SystemMouseCursors.text] will be used.
91 final MouseCursor? mouseCursor;
92
93 /// The closest instance of this class that encloses the given context.
94 ///
95 /// If no such instance exists, returns an instance created by
96 /// [DefaultSelectionStyle.fallback], which contains fallback values.
97 ///
98 /// Typical usage is as follows:
99 ///
100 /// ```dart
101 /// DefaultSelectionStyle style = DefaultSelectionStyle.of(context);
102 /// ```
103 static DefaultSelectionStyle of(BuildContext context) {
104 return context.dependOnInheritedWidgetOfExactType<DefaultSelectionStyle>() ?? const DefaultSelectionStyle.fallback();
105 }
106
107 @override
108 Widget wrap(BuildContext context, Widget child) {
109 return DefaultSelectionStyle(
110 cursorColor: cursorColor,
111 selectionColor: selectionColor,
112 mouseCursor: mouseCursor,
113 child: child
114 );
115 }
116
117 @override
118 bool updateShouldNotify(DefaultSelectionStyle oldWidget) {
119 return cursorColor != oldWidget.cursorColor ||
120 selectionColor != oldWidget.selectionColor ||
121 mouseCursor != oldWidget.mouseCursor;
122 }
123}
124
125class _NullWidget extends StatelessWidget {
126 const _NullWidget();
127
128 @override
129 Widget build(BuildContext context) {
130 throw FlutterError(
131 'A DefaultSelectionStyle constructed with DefaultSelectionStyle.fallback cannot be incorporated into the widget tree, '
132 'it is meant only to provide a fallback value returned by DefaultSelectionStyle.of() '
133 'when no enclosing default selection style is present in a BuildContext.',
134 );
135 }
136}
137