// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file is run as part of a reduced test set in CI on Mac and Windows // machines. @Tags(['reduced-test-set']) library; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { test('CardThemeData copyWith, ==, hashCode basics', () { expect(const CardThemeData(), const CardThemeData().copyWith()); expect(const CardThemeData().hashCode, const CardThemeData().copyWith().hashCode); }); test('CardThemeData lerp special cases', () { expect(CardThemeData.lerp(null, null, 0), const CardThemeData()); const theme = CardThemeData(); expect(identical(CardThemeData.lerp(theme, theme, 0.5), theme), true); }); test('CardThemeData defaults', () { const cardThemeData = CardThemeData(); expect(cardThemeData.clipBehavior, null); expect(cardThemeData.color, null); expect(cardThemeData.elevation, null); expect(cardThemeData.margin, null); expect(cardThemeData.shadowColor, null); expect(cardThemeData.shape, null); expect(cardThemeData.surfaceTintColor, null); const cardTheme = CardTheme(data: CardThemeData(), child: SizedBox()); expect(cardTheme.clipBehavior, null); expect(cardTheme.color, null); expect(cardTheme.elevation, null); expect(cardTheme.margin, null); expect(cardTheme.shadowColor, null); expect(cardTheme.shape, null); expect(cardTheme.surfaceTintColor, null); }); testWidgets('Default CardThemeData debugFillProperties', (WidgetTester tester) async { final builder = DiagnosticPropertiesBuilder(); const CardThemeData().debugFillProperties(builder); final List description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description, []); }); testWidgets('CardThemeData implements debugFillProperties', (WidgetTester tester) async { final builder = DiagnosticPropertiesBuilder(); const CardThemeData( clipBehavior: Clip.antiAlias, color: Colors.amber, elevation: 10.5, margin: EdgeInsets.all(20.5), shadowColor: Colors.green, surfaceTintColor: Colors.purple, shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.5))), ).debugFillProperties(builder); final List description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description[0], 'clipBehavior: Clip.antiAlias'); expect(description[1], 'color: MaterialColor(primary value: ${const Color(0xffffc107)})'); expect(description[2], 'shadowColor: MaterialColor(primary value: ${const Color(0xff4caf50)})'); expect( description[3], 'surfaceTintColor: MaterialColor(primary value: ${const Color(0xff9c27b0)})', ); expect(description[4], 'elevation: 10.5'); expect(description[5], 'margin: EdgeInsets.all(20.5)'); expect( description[6], 'shape: BeveledRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(20.5))', ); }); testWidgets('Material3 - Passing no CardTheme returns defaults', (WidgetTester tester) async { final theme = ThemeData(); await tester.pumpWidget( MaterialApp( theme: theme, home: const Scaffold(body: Card()), ), ); final Padding padding = _getCardPadding(tester); final Material material = _getCardMaterial(tester); expect(material.clipBehavior, Clip.none); expect(material.color, theme.colorScheme.surfaceContainerLow); expect(material.shadowColor, theme.colorScheme.shadow); expect(material.surfaceTintColor, Colors.transparent); // Default primary color expect(material.elevation, 1.0); expect(padding.padding, const EdgeInsets.all(4.0)); expect( material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0))), ); }); testWidgets('Card uses values from CardTheme', (WidgetTester tester) async { final CardThemeData cardTheme = _cardTheme(); await tester.pumpWidget( MaterialApp( theme: ThemeData(cardTheme: cardTheme), home: const Scaffold(body: Card()), ), ); final Padding padding = _getCardPadding(tester); final Material material = _getCardMaterial(tester); expect(material.clipBehavior, cardTheme.clipBehavior); expect(material.color, cardTheme.color); expect(material.shadowColor, cardTheme.shadowColor); expect(material.surfaceTintColor, cardTheme.surfaceTintColor); expect(material.elevation, cardTheme.elevation); expect(padding.padding, cardTheme.margin); expect(material.shape, cardTheme.shape); }); testWidgets('Card widget properties take priority over theme', (WidgetTester tester) async { const Clip clip = Clip.hardEdge; const Color color = Colors.orange; const Color shadowColor = Colors.pink; const elevation = 7.0; const margin = EdgeInsets.all(3.0); const ShapeBorder shape = RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(9.0)), ); await tester.pumpWidget( MaterialApp( theme: _themeData().copyWith(cardTheme: _cardTheme()), home: const Scaffold( body: Card( clipBehavior: clip, color: color, shadowColor: shadowColor, elevation: elevation, margin: margin, shape: shape, ), ), ), ); final Padding padding = _getCardPadding(tester); final Material material = _getCardMaterial(tester); expect(material.clipBehavior, clip); expect(material.color, color); expect(material.shadowColor, shadowColor); expect(material.elevation, elevation); expect(padding.padding, margin); expect(material.shape, shape); }); testWidgets('CardTheme properties take priority over ThemeData properties', ( WidgetTester tester, ) async { final CardThemeData cardTheme = _cardTheme(); final ThemeData themeData = _themeData().copyWith(cardTheme: cardTheme); await tester.pumpWidget( MaterialApp( theme: themeData, home: const Scaffold(body: Card()), ), ); final Material material = _getCardMaterial(tester); expect(material.color, cardTheme.color); }); testWidgets('Material3 - ThemeData properties are used when no CardTheme is set', ( WidgetTester tester, ) async { final themeData = ThemeData(); await tester.pumpWidget( MaterialApp( theme: themeData, home: const Scaffold(body: Card()), ), ); final Material material = _getCardMaterial(tester); expect(material.color, themeData.colorScheme.surfaceContainerLow); }); testWidgets('Material3 - CardTheme customizes shape', (WidgetTester tester) async { const cardTheme = CardThemeData( color: Colors.white, shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(7))), elevation: 1.0, ); final Key painterKey = UniqueKey(); await tester.pumpWidget( MaterialApp( theme: ThemeData(cardTheme: cardTheme), home: Scaffold( body: RepaintBoundary( key: painterKey, child: Center( child: Card(child: SizedBox.fromSize(size: const Size(200, 300))), ), ), ), ), ); await expectLater(find.byKey(painterKey), matchesGoldenFile('card_theme.custom_shape.png')); }); 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 themeElevation = 10.0; const 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 elevation = 20.0; const margin = EdgeInsets.all(18.0); const ShapeBorder shape = RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), ); final 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 globalElevation = 10.0; const 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 localElevation = 20.0; const localMargin = EdgeInsets.all(18.0); const ShapeBorder localShape = RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), ); final 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 // can be deleted. testWidgets('Material2 - ThemeData properties are used when no CardTheme is set', ( WidgetTester tester, ) async { final themeData = ThemeData(useMaterial3: false); await tester.pumpWidget( MaterialApp( theme: themeData, home: const Scaffold(body: Card()), ), ); final Material material = _getCardMaterial(tester); expect(material.color, themeData.cardColor); }); testWidgets('Material2 - Passing no CardTheme returns defaults', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( theme: ThemeData(useMaterial3: false), home: const Scaffold(body: Card()), ), ); final Padding padding = _getCardPadding(tester); final Material material = _getCardMaterial(tester); expect(material.clipBehavior, Clip.none); expect(material.color, Colors.white); expect(material.shadowColor, Colors.black); expect(material.surfaceTintColor, null); expect(material.elevation, 1.0); expect(padding.padding, const EdgeInsets.all(4.0)); expect( material.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))), ); }); testWidgets('Material2 - CardTheme customizes shape', (WidgetTester tester) async { const cardTheme = CardThemeData( color: Colors.white, shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(7))), elevation: 1.0, ); final Key painterKey = UniqueKey(); await tester.pumpWidget( MaterialApp( theme: ThemeData(cardTheme: cardTheme, useMaterial3: false), home: Scaffold( body: RepaintBoundary( key: painterKey, child: Center( child: Card(child: SizedBox.fromSize(size: const Size(200, 300))), ), ), ), ), ); await expectLater( find.byKey(painterKey), matchesGoldenFile('card_theme.custom_shape_m2.png'), ); }); }); } CardThemeData _cardTheme() { return const CardThemeData( clipBehavior: Clip.antiAlias, color: Colors.green, shadowColor: Colors.red, surfaceTintColor: Colors.purple, elevation: 6.0, margin: EdgeInsets.all(7.0), shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))), ); } ThemeData _themeData() { return ThemeData(cardColor: Colors.pink); } Material _getCardMaterial(WidgetTester tester) { return tester.widget( find.descendant(of: find.byType(Card), matching: find.byType(Material)), ); } Padding _getCardPadding(WidgetTester tester) { return tester.widget( find.descendant(of: find.byType(Card), matching: find.byType(Padding)), ); }