1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
// Copyright 2018 The Chromium 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 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'text_theme.dart';
export 'package:flutter/services.dart' show Brightness;
// Values derived from https://developer.apple.com/design/resources/.
const Color _kDefaultBarLightBackgroundColor = Color(0xCCF8F8F8);
const Color _kDefaultBarDarkBackgroundColor = Color(0xB7212121);
/// Applies a visual styling theme to descendant Cupertino widgets.
///
/// Affects the color and text styles of Cupertino widgets whose styling
/// are not overridden when constructing the respective widgets instances.
///
/// Descendant widgets can retrieve the current [CupertinoThemeData] by calling
/// [CupertinoTheme.of]. An [InheritedWidget] dependency is created when
/// an ancestor [CupertinoThemeData] is retrieved via [CupertinoTheme.of].
///
/// The [CupertinoTheme] widget implies an [IconTheme] widget, whose
/// [IconTheme.data] has the same color as [CupertinoThemeData.primaryColor]
///
/// See also:
///
/// * [CupertinoThemeData], specifies the theme's visual styling.
/// * [CupertinoApp], which will automatically add a [CupertinoTheme].
/// * [Theme], a Material theme which will automatically add a [CupertinoTheme]
/// with a [CupertinoThemeData] derived from the Material [ThemeData].
class CupertinoTheme extends StatelessWidget {
/// Creates a [CupertinoTheme] to change descendant Cupertino widgets' styling.
///
/// The [data] and [child] parameters must not be null.
const CupertinoTheme({
Key key,
@required this.data,
@required this.child,
}) : assert(child != null),
assert(data != null),
super(key: key);
/// The [CupertinoThemeData] styling for this theme.
final CupertinoThemeData data;
/// Retrieve the [CupertinoThemeData] from an ancestor [CupertinoTheme] widget.
///
/// Returns a default [CupertinoThemeData] if no [CupertinoTheme] widgets
/// exist in the ancestry tree.
static CupertinoThemeData of(BuildContext context) {
final _InheritedCupertinoTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedCupertinoTheme);
return (inheritedTheme?.theme?.data ?? const CupertinoThemeData()).resolveFrom(context, nullOk: true);
}
/// Retrieve the [Brightness] value from the closest ancestor [CupertinoTheme]
/// widget.
///
/// If no ancestral [CupertinoTheme] widget with explicit brightness value could
/// be found, the method will resort to the closest ancestor [MediaQuery] widget.
///
/// Throws an exception if no such [CupertinoTheme] or [MediaQuery] widgets exist
/// in the ancestry tree, unless [nullOk] is set to true.
static Brightness brightnessOf(BuildContext context, { bool nullOk = false }) {
final _InheritedCupertinoTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedCupertinoTheme);
return inheritedTheme?.theme?.data?._brightness ?? MediaQuery.of(context, nullOk: nullOk)?.platformBrightness;
}
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.child}
final Widget child;
@override
Widget build(BuildContext context) {
return _InheritedCupertinoTheme(
theme: this,
child: IconTheme(
data: IconThemeData(color: data.primaryColor),
child: child,
)
);
}
}
class _InheritedCupertinoTheme extends InheritedWidget {
const _InheritedCupertinoTheme({
Key key,
@required this.theme,
@required Widget child,
}) : assert(theme != null),
super(key: key, child: child);
final CupertinoTheme theme;
@override
bool updateShouldNotify(_InheritedCupertinoTheme old) => theme.data != old.theme.data;
}
/// Styling specifications for a [CupertinoTheme].
///
/// All constructor parameters can be null, in which case a
/// [CupertinoColors.activeBlue] based default iOS theme styling is used.
///
/// Parameters can also be partially specified, in which case some parameters
/// will cascade down to other dependent parameters to create a cohesive
/// visual effect. For instance, if a [primaryColor] is specified, it would
/// cascade down to affect some fonts in [textTheme] if [textTheme] is not
/// specified.
///
/// See also:
///
/// * [CupertinoTheme], in which this [CupertinoThemeData] is inserted.
/// * [ThemeData], a Material equivalent that also configures Cupertino
/// styling via a [CupertinoThemeData] subclass [MaterialBasedCupertinoThemeData].
@immutable
class CupertinoThemeData extends Diagnosticable {
/// Create a [CupertinoTheme] styling specification.
///
/// Unspecified parameters default to a reasonable iOS default style.
const CupertinoThemeData({
Brightness brightness,
Color primaryColor,
Color primaryContrastingColor,
CupertinoTextThemeData textTheme,
Color barBackgroundColor,
Color scaffoldBackgroundColor,
}) : this.raw(
brightness,
primaryColor,
primaryContrastingColor,
textTheme,
barBackgroundColor,
scaffoldBackgroundColor,
);
/// Same as the default constructor but with positional arguments to avoid
/// forgetting any and to specify all arguments.
///
/// Used by subclasses to get the superclass's defaulting behaviors.
@protected
const CupertinoThemeData.raw(
this._brightness,
this._primaryColor,
this._primaryContrastingColor,
this._textTheme,
this._barBackgroundColor,
this._scaffoldBackgroundColor,
);
bool get _isLight => brightness == Brightness.light;
/// The general brightness theme of the [CupertinoThemeData].
///
/// Affects all other theme properties when unspecified. Defaults to
/// [Brightness.light].
///
/// If coming from a Material [Theme] and unspecified, [brightness] will be
/// derived from the Material [ThemeData]'s `brightness`.
Brightness get brightness => _brightness ?? Brightness.light;
final Brightness _brightness;
/// A color used on interactive elements of the theme.
///
/// This color is generally used on text and icons in buttons and tappable
/// elements. Defaults to [CupertinoColors.activeBlue] or
/// [CupertinoColors.activeOrange] when [brightness] is light or dark.
///
/// If coming from a Material [Theme] and unspecified, [primaryColor] will be
/// derived from the Material [ThemeData]'s `colorScheme.primary`. However, in
/// iOS styling, the [primaryColor] is more sparsely used than in Material
/// Design where the [primaryColor] can appear on non-interactive surfaces like
/// the [AppBar] background, [TextField] borders etc.
Color get primaryColor {
return _primaryColor ??
(_isLight ? CupertinoColors.activeBlue : CupertinoColors.activeOrange);
}
final Color _primaryColor;
/// A color used for content that must contrast against a [primaryColor] background.
///
/// For example, this color is used for a [CupertinoButton]'s text and icons
/// when the button's background is [primaryColor].
///
/// If coming from a Material [Theme] and unspecified, [primaryContrastingColor]
/// will be derived from the Material [ThemeData]'s `colorScheme.onPrimary`.
Color get primaryContrastingColor {
return _primaryContrastingColor ??
(_isLight ? CupertinoColors.white : CupertinoColors.black);
}
final Color _primaryContrastingColor;
/// Text styles used by Cupertino widgets.
///
/// Derived from [brightness] and [primaryColor] if unspecified, including
/// [brightness] and [primaryColor] of a Material [ThemeData] if coming
/// from a Material [Theme].
CupertinoTextThemeData get textTheme {
return _textTheme ?? CupertinoTextThemeData(
brightness: brightness,
primaryColor: primaryColor,
);
}
final CupertinoTextThemeData _textTheme;
/// Background color of the top nav bar and bottom tab bar.
///
/// Defaults to a light gray or a dark gray translucent color depending
/// on the [brightness].
Color get barBackgroundColor {
return _barBackgroundColor ??
(_isLight ? _kDefaultBarLightBackgroundColor : _kDefaultBarDarkBackgroundColor);
}
final Color _barBackgroundColor;
/// Background color of the scaffold.
///
/// Defaults to white or black depending on the [brightness].
Color get scaffoldBackgroundColor {
return _scaffoldBackgroundColor ??
(_isLight ? CupertinoColors.white : CupertinoColors.black);
}
final Color _scaffoldBackgroundColor;
/// Return an instance of the [CupertinoThemeData] whose property getters
/// only return the construction time specifications with no derived values.
///
/// Used in Material themes to let unspecified properties fallback to Material
/// theme properties instead of iOS defaults.
CupertinoThemeData noDefault() {
return _NoDefaultCupertinoThemeData(
_brightness,
_primaryColor,
_primaryContrastingColor,
_textTheme,
_barBackgroundColor,
_scaffoldBackgroundColor,
);
}
/// Return a new `CupertinoThemeData` whose colors are from this `CupertinoThemeData`,
/// but resolved aginst the given [BuildContext].
///
/// It will be called in [CupertinoTheme.of].
@protected
CupertinoThemeData resolveFrom(BuildContext context, { bool nullOk = false }) {
Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk);
return copyWith(
primaryColor: convertColor(primaryColor),
primaryContrastingColor: convertColor(primaryContrastingColor),
textTheme: textTheme?.resolveFrom(context, nullOk: nullOk),
barBackgroundColor: convertColor(barBackgroundColor),
scaffoldBackgroundColor: convertColor(scaffoldBackgroundColor),
);
}
/// Create a copy of [CupertinoThemeData] with specified attributes overridden.
///
/// Only the current instance's specified attributes are copied instead of
/// derived values. For instance, if the current [primaryColor] is implied
/// to be [CupertinoColors.activeOrange] due to the current [brightness],
/// copying with a different [brightness] will also change the copy's
/// implied [primaryColor].
CupertinoThemeData copyWith({
Brightness brightness,
Color primaryColor,
Color primaryContrastingColor,
CupertinoTextThemeData textTheme,
Color barBackgroundColor,
Color scaffoldBackgroundColor,
}) {
return CupertinoThemeData(
brightness: brightness ?? _brightness,
primaryColor: primaryColor ?? _primaryColor,
primaryContrastingColor: primaryContrastingColor ?? _primaryContrastingColor,
textTheme: textTheme ?? _textTheme,
barBackgroundColor: barBackgroundColor ?? _barBackgroundColor,
scaffoldBackgroundColor: scaffoldBackgroundColor ?? _scaffoldBackgroundColor,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
const CupertinoThemeData defaultData = CupertinoThemeData();
properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
properties.add(ColorProperty('primaryColor', primaryColor, defaultValue: defaultData.primaryColor));
properties.add(ColorProperty('primaryContrastingColor', primaryContrastingColor, defaultValue: defaultData.primaryContrastingColor));
properties.add(DiagnosticsProperty<CupertinoTextThemeData>('textTheme', textTheme, defaultValue: defaultData.textTheme));
properties.add(ColorProperty('barBackgroundColor', barBackgroundColor, defaultValue: defaultData.barBackgroundColor));
properties.add(ColorProperty('scaffoldBackgroundColor', scaffoldBackgroundColor, defaultValue: defaultData.scaffoldBackgroundColor));
}
}
class _NoDefaultCupertinoThemeData extends CupertinoThemeData {
const _NoDefaultCupertinoThemeData(
this.brightness,
this.primaryColor,
this.primaryContrastingColor,
this.textTheme,
this.barBackgroundColor,
this.scaffoldBackgroundColor,
) : super.raw(
brightness,
primaryColor,
primaryContrastingColor,
textTheme,
barBackgroundColor,
scaffoldBackgroundColor,
);
@override
final Brightness brightness;
@override
final Color primaryColor;
@override
final Color primaryContrastingColor;
@override
final CupertinoTextThemeData textTheme;
@override
final Color barBackgroundColor;
@override
final Color scaffoldBackgroundColor;
@override
_NoDefaultCupertinoThemeData resolveFrom(BuildContext context, { bool nullOk = false }) {
Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context, nullOk: nullOk);
return _NoDefaultCupertinoThemeData(
brightness,
convertColor(primaryColor),
convertColor(primaryContrastingColor),
textTheme?.resolveFrom(context, nullOk: nullOk),
convertColor(barBackgroundColor),
convertColor(scaffoldBackgroundColor),
);
}
@override
CupertinoThemeData copyWith({
Brightness brightness,
Color primaryColor,
Color primaryContrastingColor,
CupertinoTextThemeData textTheme,
Color barBackgroundColor ,
Color scaffoldBackgroundColor
}) {
return _NoDefaultCupertinoThemeData(
brightness ?? this.brightness,
primaryColor ?? this.primaryColor,
primaryContrastingColor ?? this.primaryContrastingColor,
textTheme ?? this.textTheme,
barBackgroundColor ?? this.barBackgroundColor,
scaffoldBackgroundColor ?? this.scaffoldBackgroundColor,
);
}
}