Skip to content

Commit

Permalink
Normalize ThemeData.cardTheme (flutter#153254)
Browse files Browse the repository at this point in the history
Following flutter#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 flutter#91772
  • Loading branch information
QuncCccccc authored Oct 4, 2024
1 parent 85abc1a commit 534adfb
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 22 deletions.
2 changes: 1 addition & 1 deletion packages/flutter/lib/src/material/card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter/lib/src/material/card_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 26 additions & 8 deletions packages/flutter/lib/src/material/theme_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1466,7 +1475,7 @@ class ThemeData with Diagnosticable {
BottomNavigationBarThemeData? bottomNavigationBarTheme,
BottomSheetThemeData? bottomSheetTheme,
ButtonThemeData? buttonTheme,
CardTheme? cardTheme,
Object? cardTheme,
CheckboxThemeData? checkboxTheme,
ChipThemeData? chipTheme,
DataTableThemeData? dataTableTheme,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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));
Expand Down
126 changes: 120 additions & 6 deletions packages/flutter/test/material/card_theme_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter/test/material/search_anchor_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter/test/material/theme_data_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter/test/widgets/inherited_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand All @@ -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())
Expand Down

0 comments on commit 534adfb

Please sign in to comment.