Normalize ThemeData.cardTheme
(#153254)
Following https://github.com/flutter/flutter/pull/151914, this PR is to normalize `ThemeData.cardTheme`; change the `CardTheme cardTheme` property to `CardThemeData cardTheme` in `ThemeData`. In `ThemeData()` and `ThemeData.copyWith()`, the `cardTheme` parameter type is changed to `Object?` to accept both `CardTheme` and `CardThemeData` so that we won't cause immediate breaking change and make sure rolling is smooth. Once all component themes are normalized, these `Object?` types should be changed to `xxxThemeData`. There's no way to create a dart fix because we can't add a "@deprecated" label for `CardTheme` because `CardTheme` is a new InheritedWidget subclass now. Addresses the "theme normalization" sub project within https://github.com/flutter/flutter/issues/91772
This commit is contained in:
parent
85abc1a3a4
commit
534adfbe02
@ -215,7 +215,7 @@ class Card extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final CardTheme cardTheme = CardTheme.of(context);
|
||||
final CardThemeData cardTheme = CardTheme.of(context);
|
||||
final CardThemeData defaults;
|
||||
if (Theme.of(context).useMaterial3) {
|
||||
defaults = switch (_variant) {
|
||||
|
@ -156,9 +156,9 @@ class CardTheme extends InheritedWidget with Diagnosticable {
|
||||
}
|
||||
|
||||
/// The [ThemeData.cardTheme] property of the ambient [Theme].
|
||||
static CardTheme of(BuildContext context) {
|
||||
static CardThemeData of(BuildContext context) {
|
||||
final CardTheme? cardTheme = context.dependOnInheritedWidgetOfExactType<CardTheme>();
|
||||
return cardTheme ?? Theme.of(context).cardTheme;
|
||||
return cardTheme?.data ?? Theme.of(context).cardTheme;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -315,7 +315,8 @@ class ThemeData with Diagnosticable {
|
||||
BottomNavigationBarThemeData? bottomNavigationBarTheme,
|
||||
BottomSheetThemeData? bottomSheetTheme,
|
||||
ButtonThemeData? buttonTheme,
|
||||
CardTheme? cardTheme,
|
||||
// TODO(QuncCccccc): Change the parameter type to CardThemeData
|
||||
Object? cardTheme,
|
||||
CheckboxThemeData? checkboxTheme,
|
||||
ChipThemeData? chipTheme,
|
||||
DataTableThemeData? dataTableTheme,
|
||||
@ -493,7 +494,15 @@ class ThemeData with Diagnosticable {
|
||||
bottomAppBarTheme ??= const BottomAppBarTheme();
|
||||
bottomNavigationBarTheme ??= const BottomNavigationBarThemeData();
|
||||
bottomSheetTheme ??= const BottomSheetThemeData();
|
||||
cardTheme ??= const CardTheme();
|
||||
// TODO(QuncCccccc): Clean it up once the type of `cardTheme` is changed to `CardThemeData`
|
||||
if (cardTheme != null) {
|
||||
if (cardTheme is CardTheme) {
|
||||
cardTheme = cardTheme.data;
|
||||
} else if (cardTheme is! CardThemeData) {
|
||||
throw ArgumentError('cardTheme must be either a CardThemeData or a CardTheme');
|
||||
}
|
||||
}
|
||||
cardTheme ??= const CardThemeData();
|
||||
checkboxTheme ??= const CheckboxThemeData();
|
||||
chipTheme ??= const ChipThemeData();
|
||||
dataTableTheme ??= const DataTableThemeData();
|
||||
@ -594,7 +603,7 @@ class ThemeData with Diagnosticable {
|
||||
bottomNavigationBarTheme: bottomNavigationBarTheme,
|
||||
bottomSheetTheme: bottomSheetTheme,
|
||||
buttonTheme: buttonTheme,
|
||||
cardTheme: cardTheme,
|
||||
cardTheme: cardTheme as CardThemeData,
|
||||
checkboxTheme: checkboxTheme,
|
||||
chipTheme: chipTheme,
|
||||
dataTableTheme: dataTableTheme,
|
||||
@ -1266,7 +1275,7 @@ class ThemeData with Diagnosticable {
|
||||
/// The colors and styles used to render [Card].
|
||||
///
|
||||
/// This is the value returned from [CardTheme.of].
|
||||
final CardTheme cardTheme;
|
||||
final CardThemeData cardTheme;
|
||||
|
||||
/// A theme for customizing the appearance and layout of [Checkbox] widgets.
|
||||
final CheckboxThemeData checkboxTheme;
|
||||
@ -1466,7 +1475,7 @@ class ThemeData with Diagnosticable {
|
||||
BottomNavigationBarThemeData? bottomNavigationBarTheme,
|
||||
BottomSheetThemeData? bottomSheetTheme,
|
||||
ButtonThemeData? buttonTheme,
|
||||
CardTheme? cardTheme,
|
||||
Object? cardTheme,
|
||||
CheckboxThemeData? checkboxTheme,
|
||||
ChipThemeData? chipTheme,
|
||||
DataTableThemeData? dataTableTheme,
|
||||
@ -1521,6 +1530,15 @@ class ThemeData with Diagnosticable {
|
||||
}) {
|
||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||
|
||||
// TODO(QuncCccccc): Clean it up once the type of `cardTheme` is changed to `CardThemeData`
|
||||
if (cardTheme != null) {
|
||||
if (cardTheme is CardTheme) {
|
||||
cardTheme = cardTheme.data;
|
||||
} else if (cardTheme is! CardThemeData) {
|
||||
throw ArgumentError('cardTheme must be either a CardThemeData or a CardTheme');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(QuncCccccc): Clean this up once the type of `dialogTheme` is changed to `DialogThemeData`
|
||||
if (dialogTheme != null) {
|
||||
if (dialogTheme is DialogTheme) {
|
||||
@ -1585,7 +1603,7 @@ class ThemeData with Diagnosticable {
|
||||
bottomNavigationBarTheme: bottomNavigationBarTheme ?? this.bottomNavigationBarTheme,
|
||||
bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme,
|
||||
buttonTheme: buttonTheme ?? this.buttonTheme,
|
||||
cardTheme: cardTheme ?? this.cardTheme,
|
||||
cardTheme: cardTheme as CardThemeData? ?? this.cardTheme,
|
||||
checkboxTheme: checkboxTheme ?? this.checkboxTheme,
|
||||
chipTheme: chipTheme ?? this.chipTheme,
|
||||
dataTableTheme: dataTableTheme ?? this.dataTableTheme,
|
||||
@ -1778,7 +1796,7 @@ class ThemeData with Diagnosticable {
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData.lerp(a.bottomNavigationBarTheme, b.bottomNavigationBarTheme, t),
|
||||
bottomSheetTheme: BottomSheetThemeData.lerp(a.bottomSheetTheme, b.bottomSheetTheme, t)!,
|
||||
buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme,
|
||||
cardTheme: CardTheme.lerp(a.cardTheme, b.cardTheme, t),
|
||||
cardTheme: CardThemeData.lerp(a.cardTheme, b.cardTheme, t),
|
||||
checkboxTheme: CheckboxThemeData.lerp(a.checkboxTheme, b.checkboxTheme, t),
|
||||
chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t)!,
|
||||
dataTableTheme: DataTableThemeData.lerp(a.dataTableTheme, b.dataTableTheme, t),
|
||||
@ -2076,7 +2094,7 @@ class ThemeData with Diagnosticable {
|
||||
properties.add(DiagnosticsProperty<BottomNavigationBarThemeData>('bottomNavigationBarTheme', bottomNavigationBarTheme, defaultValue: defaultData.bottomNavigationBarTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<BottomSheetThemeData>('bottomSheetTheme', bottomSheetTheme, defaultValue: defaultData.bottomSheetTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<ButtonThemeData>('buttonTheme', buttonTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<CardTheme>('cardTheme', cardTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<CardThemeData>('cardTheme', cardTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<CheckboxThemeData>('checkboxTheme', checkboxTheme, defaultValue: defaultData.checkboxTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<DataTableThemeData>('dataTableTheme', dataTableTheme, defaultValue: defaultData.dataTableTheme, level: DiagnosticLevel.debug));
|
||||
|
@ -106,7 +106,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('Card uses values from CardTheme', (WidgetTester tester) async {
|
||||
final CardTheme cardTheme = _cardTheme();
|
||||
final CardThemeData cardTheme = _cardTheme();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(cardTheme: cardTheme),
|
||||
@ -163,7 +163,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('CardTheme properties take priority over ThemeData properties', (WidgetTester tester) async {
|
||||
final CardTheme cardTheme = _cardTheme();
|
||||
final CardThemeData cardTheme = _cardTheme();
|
||||
final ThemeData themeData = _themeData().copyWith(cardTheme: cardTheme);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
@ -192,7 +192,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('Material3 - CardTheme customizes shape', (WidgetTester tester) async {
|
||||
const CardTheme cardTheme = CardTheme(
|
||||
const CardThemeData cardTheme = CardThemeData(
|
||||
color: Colors.white,
|
||||
shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(7))),
|
||||
elevation: 1.0,
|
||||
@ -220,6 +220,120 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Card properties are taken over the theme values', (WidgetTester tester) async {
|
||||
const Clip themeClipBehavior = Clip.antiAlias;
|
||||
const Color themeColor = Colors.red;
|
||||
const Color themeShadowColor = Colors.orange;
|
||||
const double themeElevation = 10.0;
|
||||
const EdgeInsets themeMargin = EdgeInsets.all(12.0);
|
||||
const ShapeBorder themeShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0)));
|
||||
|
||||
const Clip clipBehavior = Clip.hardEdge;
|
||||
const Color color = Colors.yellow;
|
||||
const Color shadowColor = Colors.green;
|
||||
const double elevation = 20.0;
|
||||
const EdgeInsets margin = EdgeInsets.all(18.0);
|
||||
const ShapeBorder shape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(25.0)));
|
||||
|
||||
final ThemeData themeData = ThemeData(
|
||||
cardTheme: const CardThemeData(
|
||||
clipBehavior: themeClipBehavior,
|
||||
color: themeColor,
|
||||
shadowColor: themeShadowColor,
|
||||
elevation: themeElevation,
|
||||
margin: themeMargin,
|
||||
shape: themeShape,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: const Scaffold(
|
||||
body: Card(
|
||||
clipBehavior: clipBehavior,
|
||||
color: color,
|
||||
shadowColor: shadowColor,
|
||||
elevation: elevation,
|
||||
margin: margin,
|
||||
shape: shape,
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
final Padding cardMargin = _getCardPadding(tester);
|
||||
final Material material = _getCardMaterial(tester);
|
||||
|
||||
expect(material.clipBehavior, clipBehavior);
|
||||
expect(material.color, color);
|
||||
expect(material.shadowColor, shadowColor);
|
||||
expect(material.elevation, elevation);
|
||||
expect(material.shape, shape);
|
||||
expect(cardMargin.padding, margin);
|
||||
});
|
||||
|
||||
testWidgets('Local CardTheme can override global CardTheme', (WidgetTester tester) async {
|
||||
const Clip globalClipBehavior = Clip.antiAlias;
|
||||
const Color globalColor = Colors.red;
|
||||
const Color globalShadowColor = Colors.orange;
|
||||
const double globalElevation = 10.0;
|
||||
const EdgeInsets globalMargin = EdgeInsets.all(12.0);
|
||||
const ShapeBorder globalShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0)));
|
||||
|
||||
const Clip localClipBehavior = Clip.hardEdge;
|
||||
const Color localColor = Colors.yellow;
|
||||
const Color localShadowColor = Colors.green;
|
||||
const double localElevation = 20.0;
|
||||
const EdgeInsets localMargin = EdgeInsets.all(18.0);
|
||||
const ShapeBorder localShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(25.0)));
|
||||
|
||||
final ThemeData themeData = ThemeData(
|
||||
cardTheme: const CardThemeData(
|
||||
clipBehavior: globalClipBehavior,
|
||||
color: globalColor,
|
||||
shadowColor: globalShadowColor,
|
||||
elevation: globalElevation,
|
||||
margin: globalMargin,
|
||||
shape: globalShape,
|
||||
),
|
||||
);
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: themeData,
|
||||
home: const Scaffold(
|
||||
body: CardTheme(
|
||||
data: CardThemeData(
|
||||
clipBehavior: localClipBehavior,
|
||||
color: localColor,
|
||||
shadowColor: localShadowColor,
|
||||
elevation: localElevation,
|
||||
margin: localMargin,
|
||||
shape: localShape,
|
||||
),
|
||||
child: Card(
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
final Padding cardMargin = _getCardPadding(tester);
|
||||
final Material material = _getCardMaterial(tester);
|
||||
|
||||
expect(material.clipBehavior, localClipBehavior);
|
||||
expect(material.color, localColor);
|
||||
expect(material.shadowColor, localShadowColor);
|
||||
expect(material.elevation, localElevation);
|
||||
expect(material.shape, localShape);
|
||||
expect(cardMargin.padding, localMargin);
|
||||
});
|
||||
|
||||
group('Material 2', () {
|
||||
// These tests are only relevant for Material 2. Once Material 2
|
||||
// support is deprecated and the APIs are removed, these tests
|
||||
@ -262,7 +376,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('Material2 - CardTheme customizes shape', (WidgetTester tester) async {
|
||||
const CardTheme cardTheme = CardTheme(
|
||||
const CardThemeData cardTheme = CardThemeData(
|
||||
color: Colors.white,
|
||||
shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(7))),
|
||||
elevation: 1.0,
|
||||
@ -292,8 +406,8 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
CardTheme _cardTheme() {
|
||||
return const CardTheme(
|
||||
CardThemeData _cardTheme() {
|
||||
return const CardThemeData(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
color: Colors.green,
|
||||
shadowColor: Colors.red,
|
||||
|
@ -2707,7 +2707,7 @@ void main() {
|
||||
backgroundColor: const Color(0xffffff00)
|
||||
),
|
||||
),
|
||||
cardTheme: const CardTheme(color: Color(0xff00ffff)),
|
||||
cardTheme: const CardThemeData(color: Color(0xff00ffff)),
|
||||
);
|
||||
Widget buildSearchAnchor() {
|
||||
return MaterialApp(
|
||||
|
@ -888,7 +888,7 @@ void main() {
|
||||
bottomSheetTheme: const BottomSheetThemeData(backgroundColor: Colors.black),
|
||||
buttonBarTheme: const ButtonBarThemeData(alignment: MainAxisAlignment.start),
|
||||
buttonTheme: const ButtonThemeData(colorScheme: ColorScheme.dark()),
|
||||
cardTheme: const CardTheme(color: Colors.black),
|
||||
cardTheme: const CardThemeData(color: Colors.black),
|
||||
checkboxTheme: const CheckboxThemeData(),
|
||||
chipTheme: chipTheme,
|
||||
dataTableTheme: const DataTableThemeData(),
|
||||
@ -1002,7 +1002,7 @@ void main() {
|
||||
bottomSheetTheme: const BottomSheetThemeData(backgroundColor: Colors.white),
|
||||
buttonBarTheme: const ButtonBarThemeData(alignment: MainAxisAlignment.end),
|
||||
buttonTheme: const ButtonThemeData(colorScheme: ColorScheme.light()),
|
||||
cardTheme: const CardTheme(color: Colors.white),
|
||||
cardTheme: const CardThemeData(color: Colors.white),
|
||||
checkboxTheme: const CheckboxThemeData(),
|
||||
chipTheme: otherChipTheme,
|
||||
dataTableTheme: const DataTableThemeData(),
|
||||
|
@ -60,7 +60,7 @@ class ThemedCard extends SingleChildRenderObjectWidget {
|
||||
|
||||
@override
|
||||
RenderPhysicalShape createRenderObject(BuildContext context) {
|
||||
final CardThemeData cardTheme = CardTheme.of(context).data;
|
||||
final CardThemeData cardTheme = CardTheme.of(context);
|
||||
|
||||
return RenderPhysicalShape(
|
||||
clipper: ShapeBorderClipper(shape: cardTheme.shape ?? const RoundedRectangleBorder()),
|
||||
@ -73,7 +73,7 @@ class ThemedCard extends SingleChildRenderObjectWidget {
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderPhysicalShape renderObject) {
|
||||
final CardThemeData cardTheme = CardTheme.of(context).data;
|
||||
final CardThemeData cardTheme = CardTheme.of(context);
|
||||
|
||||
renderObject
|
||||
..clipper = ShapeBorderClipper(shape: cardTheme.shape ?? const RoundedRectangleBorder())
|
||||
|
Loading…
x
Reference in New Issue
Block a user