Unverified Commit e153883d authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Provide a default IconTheme in CupertinoTheme (#30521)

-* Inserted an `IconTheme` widget under `CupertinoTheme` to provide a default icon color when using `CupertinoTheme`.
* Changed `CupertinoTheme` to a `StatelessWidget`, to match the implementation of `Theme`
* Changed the nesting order of `Theme`'s sub widgets, to let `Theme.iconTheme` take precedence, so that `Theme`'s behavior is kept as is.
parent 9e41f395
...@@ -24,38 +24,68 @@ const Color _kDefaultBarDarkBackgroundColor = Color(0xB7212121); ...@@ -24,38 +24,68 @@ const Color _kDefaultBarDarkBackgroundColor = Color(0xB7212121);
/// [CupertinoTheme.of]. An [InheritedWidget] dependency is created when /// [CupertinoTheme.of]. An [InheritedWidget] dependency is created when
/// an ancestor [CupertinoThemeData] is retrieved via [CupertinoTheme.of]. /// 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: /// See also:
/// ///
/// * [CupertinoThemeData], specifies the theme's visual styling. /// * [CupertinoThemeData], specifies the theme's visual styling.
/// * [CupertinoApp], which will automatically add a [CupertinoTheme]. /// * [CupertinoApp], which will automatically add a [CupertinoTheme].
/// * [Theme], a Material theme which will automatically add a [CupertinoTheme] /// * [Theme], a Material theme which will automatically add a [CupertinoTheme]
/// with a [CupertinoThemeData] derived from the Material [ThemeData]. /// with a [CupertinoThemeData] derived from the Material [ThemeData].
class CupertinoTheme extends InheritedWidget { class CupertinoTheme extends StatelessWidget {
/// Creates a [CupertinoTheme] to change descendant Cupertino widgets' styling. /// Creates a [CupertinoTheme] to change descendant Cupertino widgets' styling.
/// ///
/// The [data] and [child] parameters must not be null. /// The [data] and [child] parameters must not be null.
const CupertinoTheme({ const CupertinoTheme({
Key key, Key key,
@required this.data, @required this.data,
@required Widget child, @required this.child,
}) : assert(child != null), }) : assert(child != null),
assert(data != null), assert(data != null),
super(key: key, child: child); super(key: key);
/// The [CupertinoThemeData] styling for this theme. /// The [CupertinoThemeData] styling for this theme.
final CupertinoThemeData data; final CupertinoThemeData data;
@override
bool updateShouldNotify(CupertinoTheme oldWidget) => data != oldWidget.data;
/// Retrieve the [CupertinoThemeData] from an ancestor [CupertinoTheme] widget. /// Retrieve the [CupertinoThemeData] from an ancestor [CupertinoTheme] widget.
/// ///
/// Returns a default [CupertinoThemeData] if no [CupertinoTheme] widgets /// Returns a default [CupertinoThemeData] if no [CupertinoTheme] widgets
/// exist in the ancestry tree. /// exist in the ancestry tree.
static CupertinoThemeData of(BuildContext context) { static CupertinoThemeData of(BuildContext context) {
final CupertinoTheme theme = context.inheritFromWidgetOfExactType(CupertinoTheme); final _InheritedCupertinoTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedCupertinoTheme);
return theme?.data ?? const CupertinoThemeData(); return inheritedTheme?.theme?.data ?? const CupertinoThemeData();
} }
/// 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]. /// Styling specifications for a [CupertinoTheme].
......
...@@ -142,18 +142,18 @@ class Theme extends StatelessWidget { ...@@ -142,18 +142,18 @@ class Theme extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _InheritedTheme( return _InheritedTheme(
theme: this, theme: this,
child: IconTheme( child: CupertinoTheme(
data: data.iconTheme, // We're using a MaterialBasedCupertinoThemeData here instead of a
child: CupertinoTheme( // CupertinoThemeData because it defers some properties to the Material
// We're using a MaterialBasedCupertinoThemeData here instead of a // ThemeData.
// CupertinoThemeData because it defers some properties to the Material data: MaterialBasedCupertinoThemeData(
// ThemeData. materialTheme: data,
data: MaterialBasedCupertinoThemeData( ),
materialTheme: data, child: IconTheme(
), data: data.iconTheme,
child: child, child: child,
), ),
), )
); );
} }
......
...@@ -247,20 +247,20 @@ void main() { ...@@ -247,20 +247,20 @@ void main() {
); );
DefaultTextStyle textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first); DefaultTextStyle textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first);
IconTheme iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1))); IconThemeData iconTheme = IconTheme.of(tester.element(find.byIcon(const IconData(1))));
expect(textStyle.style.color, CupertinoColors.black); expect(textStyle.style.color, CupertinoColors.black);
expect(iconTheme.data.color, CupertinoColors.activeOrange); expect(iconTheme.color, CupertinoColors.activeOrange);
await tester.tap(find.widgetWithIcon(IconTheme, const IconData(1))); await tester.tap(find.byIcon(const IconData(1)));
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first); textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first);
iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1))); iconTheme = IconTheme.of(tester.element(find.byIcon(const IconData(1))));
expect(textStyle.style.color, CupertinoColors.activeOrange); expect(textStyle.style.color, CupertinoColors.activeOrange);
expect(iconTheme.data.color, CupertinoColors.black); expect(iconTheme.color, CupertinoColors.black);
}, },
); );
......
...@@ -9,11 +9,13 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -9,11 +9,13 @@ import 'package:flutter_test/flutter_test.dart';
int buildCount; int buildCount;
CupertinoThemeData actualTheme; CupertinoThemeData actualTheme;
IconThemeData actualIconTheme;
final Widget singletonThemeSubtree = Builder( final Widget singletonThemeSubtree = Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
buildCount++; buildCount++;
actualTheme = CupertinoTheme.of(context); actualTheme = CupertinoTheme.of(context);
actualIconTheme = IconTheme.of(context);
return const Placeholder(); return const Placeholder();
}, },
); );
...@@ -28,6 +30,16 @@ Future<CupertinoThemeData> testTheme(WidgetTester tester, CupertinoThemeData the ...@@ -28,6 +30,16 @@ Future<CupertinoThemeData> testTheme(WidgetTester tester, CupertinoThemeData the
return actualTheme; return actualTheme;
} }
Future<IconThemeData> testIconTheme(WidgetTester tester, CupertinoThemeData theme) async {
await tester.pumpWidget(
CupertinoTheme(
data: theme,
child: singletonThemeSubtree,
),
);
return actualIconTheme;
}
void main() { void main() {
setUp(() { setUp(() {
buildCount = 0; buildCount = 0;
...@@ -123,4 +135,24 @@ void main() { ...@@ -123,4 +135,24 @@ void main() {
expect(theme.scaffoldBackgroundColor, CupertinoColors.black); expect(theme.scaffoldBackgroundColor, CupertinoColors.black);
}, },
); );
testWidgets("Theme has default IconThemeData, which is derived from the theme's primary color", (WidgetTester tester) async {
const Color primaryColor = CupertinoColors.destructiveRed;
const CupertinoThemeData themeData = CupertinoThemeData(primaryColor: primaryColor);
final IconThemeData resultingIconTheme = await testIconTheme(tester, themeData);
expect(resultingIconTheme.color, themeData.primaryColor);
});
testWidgets('IconTheme.of creates a dependency on iconTheme', (WidgetTester tester) async {
IconThemeData iconTheme = await testIconTheme(tester, const CupertinoThemeData(primaryColor: CupertinoColors.destructiveRed));
expect(buildCount, 1);
expect(iconTheme.color, CupertinoColors.destructiveRed);
iconTheme = await testIconTheme(tester, const CupertinoThemeData(primaryColor: CupertinoColors.activeOrange));
expect(buildCount, 2);
expect(iconTheme.color, CupertinoColors.activeOrange);
});
} }
...@@ -417,28 +417,26 @@ void main() { ...@@ -417,28 +417,26 @@ void main() {
group('Cupertino theme', () { group('Cupertino theme', () {
int buildCount; int buildCount;
CupertinoThemeData actualTheme; CupertinoThemeData actualTheme;
IconThemeData actualIconTheme;
final Widget singletonThemeSubtree = Builder( final Widget singletonThemeSubtree = Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
buildCount++; buildCount++;
actualTheme = CupertinoTheme.of(context); actualTheme = CupertinoTheme.of(context);
actualIconTheme = IconTheme.of(context);
return const Placeholder(); return const Placeholder();
}, },
); );
Future<CupertinoThemeData> testTheme(WidgetTester tester, ThemeData theme) async { Future<CupertinoThemeData> testTheme(WidgetTester tester, ThemeData theme) async {
await tester.pumpWidget( await tester.pumpWidget(Theme(data: theme, child: singletonThemeSubtree));
Theme(
data: theme,
child: singletonThemeSubtree,
),
);
return actualTheme; return actualTheme;
} }
setUp(() { setUp(() {
buildCount = 0; buildCount = 0;
actualTheme = null; actualTheme = null;
actualIconTheme = null;
}); });
testWidgets('Default theme has defaults', (WidgetTester tester) async { testWidgets('Default theme has defaults', (WidgetTester tester) async {
...@@ -509,6 +507,20 @@ void main() { ...@@ -509,6 +507,20 @@ void main() {
expect(theme.primaryColor, Colors.orange); expect(theme.primaryColor, Colors.orange);
}); });
testWidgets("CupertinoThemeData does not override material theme's icon theme",
(WidgetTester tester) async {
const Color materialIconColor = Colors.blue;
const Color cupertinoIconColor = Colors.black;
await testTheme(tester, ThemeData(
iconTheme: const IconThemeData(color: materialIconColor),
cupertinoOverrideTheme: const CupertinoThemeData(primaryColor: cupertinoIconColor)
));
expect(buildCount, 1);
expect(actualIconTheme.color, materialIconColor);
});
testWidgets( testWidgets(
'Changing cupertino theme override triggers rebuilds', 'Changing cupertino theme override triggers rebuilds',
(WidgetTester tester) async { (WidgetTester tester) async {
......
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