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);
/// [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 InheritedWidget {
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 Widget child,
@required this.child,
}) : assert(child != null),
assert(data != null),
super(key: key, child: child);
super(key: key);
/// The [CupertinoThemeData] styling for this theme.
final CupertinoThemeData data;
@override
bool updateShouldNotify(CupertinoTheme oldWidget) => data != oldWidget.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 CupertinoTheme theme = context.inheritFromWidgetOfExactType(CupertinoTheme);
return theme?.data ?? const CupertinoThemeData();
final _InheritedCupertinoTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedCupertinoTheme);
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].
......
......@@ -142,18 +142,18 @@ class Theme extends StatelessWidget {
Widget build(BuildContext context) {
return _InheritedTheme(
theme: this,
child: IconTheme(
data: data.iconTheme,
child: CupertinoTheme(
// We're using a MaterialBasedCupertinoThemeData here instead of a
// CupertinoThemeData because it defers some properties to the Material
// ThemeData.
data: MaterialBasedCupertinoThemeData(
materialTheme: data,
),
child: CupertinoTheme(
// We're using a MaterialBasedCupertinoThemeData here instead of a
// CupertinoThemeData because it defers some properties to the Material
// ThemeData.
data: MaterialBasedCupertinoThemeData(
materialTheme: data,
),
child: IconTheme(
data: data.iconTheme,
child: child,
),
),
)
);
}
......
......@@ -247,20 +247,20 @@ void main() {
);
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(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(const Duration(milliseconds: 500));
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(iconTheme.data.color, CupertinoColors.black);
expect(iconTheme.color, CupertinoColors.black);
},
);
......
......@@ -9,11 +9,13 @@ import 'package:flutter_test/flutter_test.dart';
int buildCount;
CupertinoThemeData actualTheme;
IconThemeData actualIconTheme;
final Widget singletonThemeSubtree = Builder(
builder: (BuildContext context) {
buildCount++;
actualTheme = CupertinoTheme.of(context);
actualIconTheme = IconTheme.of(context);
return const Placeholder();
},
);
......@@ -28,6 +30,16 @@ Future<CupertinoThemeData> testTheme(WidgetTester tester, CupertinoThemeData the
return actualTheme;
}
Future<IconThemeData> testIconTheme(WidgetTester tester, CupertinoThemeData theme) async {
await tester.pumpWidget(
CupertinoTheme(
data: theme,
child: singletonThemeSubtree,
),
);
return actualIconTheme;
}
void main() {
setUp(() {
buildCount = 0;
......@@ -123,4 +135,24 @@ void main() {
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() {
group('Cupertino theme', () {
int buildCount;
CupertinoThemeData actualTheme;
IconThemeData actualIconTheme;
final Widget singletonThemeSubtree = Builder(
builder: (BuildContext context) {
buildCount++;
actualTheme = CupertinoTheme.of(context);
actualIconTheme = IconTheme.of(context);
return const Placeholder();
},
);
Future<CupertinoThemeData> testTheme(WidgetTester tester, ThemeData theme) async {
await tester.pumpWidget(
Theme(
data: theme,
child: singletonThemeSubtree,
),
);
await tester.pumpWidget(Theme(data: theme, child: singletonThemeSubtree));
return actualTheme;
}
setUp(() {
buildCount = 0;
actualTheme = null;
actualIconTheme = null;
});
testWidgets('Default theme has defaults', (WidgetTester tester) async {
......@@ -509,6 +507,20 @@ void main() {
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(
'Changing cupertino theme override triggers rebuilds',
(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