Commit c362d8da authored by Will Larche's avatar Will Larche Committed by rami-a
parent f8e5b9c4
24e9ab38cea4fd85f11ee5a3a491576037294bb9 6fc7ec65d51116c3f83acb5251e57e779af2ebbb
...@@ -29,6 +29,7 @@ export 'src/material/button.dart'; ...@@ -29,6 +29,7 @@ export 'src/material/button.dart';
export 'src/material/button_bar.dart'; export 'src/material/button_bar.dart';
export 'src/material/button_theme.dart'; export 'src/material/button_theme.dart';
export 'src/material/card.dart'; export 'src/material/card.dart';
export 'src/material/card_theme.dart';
export 'src/material/checkbox.dart'; export 'src/material/checkbox.dart';
export 'src/material/checkbox_list_tile.dart'; export 'src/material/checkbox_list_tile.dart';
export 'src/material/chip.dart'; export 'src/material/chip.dart';
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'card_theme.dart';
import 'material.dart'; import 'material.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -65,25 +66,25 @@ import 'theme.dart'; ...@@ -65,25 +66,25 @@ import 'theme.dart';
class Card extends StatelessWidget { class Card extends StatelessWidget {
/// Creates a material design card. /// Creates a material design card.
/// ///
/// The [clipBehavior] and [elevation] arguments must not be null. /// The [elevation] must be null or non-negative.
/// Additionally, the [elevation] must be non-negative.
const Card({ const Card({
Key key, Key key,
this.color, this.color,
this.elevation = 1.0, this.elevation,
this.shape, this.shape,
this.margin = const EdgeInsets.all(4.0), this.margin,
this.clipBehavior = Clip.none, this.clipBehavior,
this.child, this.child,
this.semanticContainer = true, this.semanticContainer = true,
}) : assert(elevation != null && elevation >= 0.0), }) : assert(elevation == null || elevation >= 0.0),
super(key: key); super(key: key);
/// The card's background color. /// The card's background color.
/// ///
/// Defines the card's [Material.color]. /// Defines the card's [Material.color].
/// ///
/// The default color is defined by the ambient [Theme]: [ThemeData.cardColor]. /// If this property is null then [ThemeData.cardTheme.color] is used,
/// if that's null then [ThemeData.cardColor] is used.
final Color color; final Color color;
/// The z-coordinate at which to place this card. This controls the size of /// The z-coordinate at which to place this card. This controls the size of
...@@ -91,25 +92,30 @@ class Card extends StatelessWidget { ...@@ -91,25 +92,30 @@ class Card extends StatelessWidget {
/// ///
/// Defines the card's [Material.elevation]. /// Defines the card's [Material.elevation].
/// ///
/// The default elevation is 1.0. The value is always non-negative. /// If this property is null then [ThemeData.cardTheme.elevation] is used,
/// if that's null, the default value is 1.0.
final double elevation; final double elevation;
/// The shape of the card's [Material]. /// The shape of the card's [Material].
/// ///
/// Defines the card's [Material.shape]. /// Defines the card's [Material.shape].
/// ///
/// The default shape is a [RoundedRectangleBorder] with a circular corner /// If this property is null then [ThemeData.cardTheme.shape] is used.
/// radius of 4.0. /// If that's null then the shape will be a [RoundedRectangleBorder] with a
/// circular corner radius of 4.0.
final ShapeBorder shape; final ShapeBorder shape;
/// {@macro flutter.widgets.Clip} /// {@macro flutter.widgets.Clip}
/// If this property is null then [ThemeData.cardTheme.clipBehavior] is used.
/// If that's null then the behavior will be [Clip.none].
final Clip clipBehavior; final Clip clipBehavior;
/// The empty space that surrounds the card. /// The empty space that surrounds the card.
/// ///
/// Defines the card's outer [Container.margin]. /// Defines the card's outer [Container.margin].
/// ///
/// The default margin is 4.0 logical pixels on all sides: /// If this property is null then [ThemeData.cardTheme.margin] is used,
/// if that's null, the default margin is 4.0 logical pixels on all sides:
/// `EdgeInsets.all(4.0)`. /// `EdgeInsets.all(4.0)`.
final EdgeInsetsGeometry margin; final EdgeInsetsGeometry margin;
...@@ -131,20 +137,25 @@ class Card extends StatelessWidget { ...@@ -131,20 +137,25 @@ class Card extends StatelessWidget {
/// {@macro flutter.widgets.child} /// {@macro flutter.widgets.child}
final Widget child; final Widget child;
static const double _defaultElevation = 1.0;
static const Clip _defaultClipBehavior = Clip.none;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final CardTheme cardTheme = CardTheme.of(context);
return Semantics( return Semantics(
container: semanticContainer, container: semanticContainer,
child: Container( child: Container(
margin: margin ?? const EdgeInsets.all(4.0), margin: margin ?? cardTheme.margin ?? const EdgeInsets.all(4.0),
child: Material( child: Material(
type: MaterialType.card, type: MaterialType.card,
color: color ?? Theme.of(context).cardColor, color: color ?? cardTheme.color ?? Theme.of(context).cardColor,
elevation: elevation, elevation: elevation ?? cardTheme.elevation ?? _defaultElevation,
shape: shape ?? const RoundedRectangleBorder( shape: shape ?? cardTheme.shape ?? const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0)), borderRadius: BorderRadius.all(Radius.circular(4.0)),
), ),
clipBehavior: clipBehavior, clipBehavior: clipBehavior ?? cardTheme.clipBehavior ?? _defaultClipBehavior,
child: Semantics( child: Semantics(
explicitChildNodes: !semanticContainer, explicitChildNodes: !semanticContainer,
child: child, child: child,
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'theme.dart';
/// Defines default property values for descendant [Card] widgets.
///
/// Descendant widgets obtain the current [CardTheme] object using
/// `CardTheme.of(context)`. Instances of [CardTheme] can be
/// customized with [CardTheme.copyWith].
///
/// Typically a [CardTheme] is specified as part of the overall [Theme]
/// with [ThemeData.cardTheme].
///
/// All [CardTheme] properties are `null` by default. When null, the [Card]
/// will use the values from [ThemeData] if they exist, otherwise it will
/// provide its own defaults.
///
/// See also:
///
/// * [ThemeData], which describes the overall theme information for the
/// application.
class CardTheme extends Diagnosticable {
/// Creates a theme that can be used for [ThemeData.cardTheme].
///
/// The [elevation] must be null or non-negative.
const CardTheme({
this.clipBehavior,
this.color,
this.elevation,
this.margin,
this.shape,
}) : assert(elevation == null || elevation >= 0.0);
/// Default value for [Card.clipBehavior].
///
/// If null, [Card] uses [Clip.none].
final Clip clipBehavior;
/// Default value for [Card.color].
///
/// If null, [Card] uses [ThemeData.cardColor].
final Color color;
/// Default value for [Card.elevation].
///
/// If null, [Card] uses a default of 1.0.
final double elevation;
/// Default value for [Card.margin].
///
/// If null, [Card] uses a default margin of 4.0 logical pixels on all sides:
/// `EdgeInsets.all(4.0)`.
final EdgeInsetsGeometry margin;
/// Default value for [Card.shape].
///
/// If null, [Card] then uses a [RoundedRectangleBorder] with a circular
/// corner radius of 4.0.
final ShapeBorder shape;
/// Creates a copy of this object with the given fields replaced with the
/// new values.
CardTheme copyWith({
Clip clipBehavior,
Color color,
double elevation,
EdgeInsetsGeometry margin,
ShapeBorder shape,
}) {
return CardTheme(
clipBehavior: clipBehavior ?? this.clipBehavior,
color: color ?? this.color,
elevation: elevation ?? this.elevation,
margin: margin ?? this.margin,
shape: shape ?? this.shape,
);
}
/// The [ThemeData.cardTheme] property of the ambient [Theme].
static CardTheme of(BuildContext context) {
return Theme.of(context).cardTheme;
}
/// Linearly interpolate between two Card themes.
///
/// The argument `t` must not be null.
///
/// {@macro dart.ui.shadow.lerp}
static CardTheme lerp(CardTheme a, CardTheme b, double t) {
assert(t != null);
return CardTheme(
clipBehavior: t < 0.5 ? a?.clipBehavior : b?.clipBehavior,
color: Color.lerp(a?.color, b?.color, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
margin: EdgeInsetsGeometry.lerp(a?.margin, b?.margin, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
);
}
@override
int get hashCode {
return hashValues(
clipBehavior,
color,
elevation,
margin,
shape,
);
}
@override
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final CardTheme typedOther = other;
return typedOther.clipBehavior == clipBehavior
&& typedOther.color == color
&& typedOther.elevation == elevation
&& typedOther.margin == margin
&& typedOther.shape == shape;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('color', color, defaultValue: null));
properties.add(DiagnosticsProperty<double>('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
}
}
...@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart'; ...@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart';
import 'app_bar_theme.dart'; import 'app_bar_theme.dart';
import 'bottom_app_bar_theme.dart'; import 'bottom_app_bar_theme.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'card_theme.dart';
import 'chip_theme.dart'; import 'chip_theme.dart';
import 'color_scheme.dart'; import 'color_scheme.dart';
import 'colors.dart'; import 'colors.dart';
...@@ -149,6 +150,7 @@ class ThemeData extends Diagnosticable { ...@@ -149,6 +150,7 @@ class ThemeData extends Diagnosticable {
IconThemeData accentIconTheme, IconThemeData accentIconTheme,
SliderThemeData sliderTheme, SliderThemeData sliderTheme,
TabBarTheme tabBarTheme, TabBarTheme tabBarTheme,
CardTheme cardTheme,
ChipThemeData chipTheme, ChipThemeData chipTheme,
TargetPlatform platform, TargetPlatform platform,
MaterialTapTargetSize materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
...@@ -248,6 +250,7 @@ class ThemeData extends Diagnosticable { ...@@ -248,6 +250,7 @@ class ThemeData extends Diagnosticable {
tabBarTheme ??= const TabBarTheme(); tabBarTheme ??= const TabBarTheme();
appBarTheme ??= const AppBarTheme(); appBarTheme ??= const AppBarTheme();
bottomAppBarTheme ??= const BottomAppBarTheme(); bottomAppBarTheme ??= const BottomAppBarTheme();
cardTheme ??= const CardTheme();
chipTheme ??= ChipThemeData.fromDefaults( chipTheme ??= ChipThemeData.fromDefaults(
secondaryColor: primaryColor, secondaryColor: primaryColor,
brightness: brightness, brightness: brightness,
...@@ -296,6 +299,7 @@ class ThemeData extends Diagnosticable { ...@@ -296,6 +299,7 @@ class ThemeData extends Diagnosticable {
accentIconTheme: accentIconTheme, accentIconTheme: accentIconTheme,
sliderTheme: sliderTheme, sliderTheme: sliderTheme,
tabBarTheme: tabBarTheme, tabBarTheme: tabBarTheme,
cardTheme: cardTheme,
chipTheme: chipTheme, chipTheme: chipTheme,
platform: platform, platform: platform,
materialTapTargetSize: materialTapTargetSize, materialTapTargetSize: materialTapTargetSize,
...@@ -359,6 +363,7 @@ class ThemeData extends Diagnosticable { ...@@ -359,6 +363,7 @@ class ThemeData extends Diagnosticable {
@required this.accentIconTheme, @required this.accentIconTheme,
@required this.sliderTheme, @required this.sliderTheme,
@required this.tabBarTheme, @required this.tabBarTheme,
@required this.cardTheme,
@required this.chipTheme, @required this.chipTheme,
@required this.platform, @required this.platform,
@required this.materialTapTargetSize, @required this.materialTapTargetSize,
...@@ -407,6 +412,7 @@ class ThemeData extends Diagnosticable { ...@@ -407,6 +412,7 @@ class ThemeData extends Diagnosticable {
assert(accentIconTheme != null), assert(accentIconTheme != null),
assert(sliderTheme != null), assert(sliderTheme != null),
assert(tabBarTheme != null), assert(tabBarTheme != null),
assert(cardTheme != null),
assert(chipTheme != null), assert(chipTheme != null),
assert(platform != null), assert(platform != null),
assert(materialTapTargetSize != null), assert(materialTapTargetSize != null),
...@@ -604,6 +610,11 @@ class ThemeData extends Diagnosticable { ...@@ -604,6 +610,11 @@ class ThemeData extends Diagnosticable {
/// A theme for customizing the size, shape, and color of the tab bar indicator. /// A theme for customizing the size, shape, and color of the tab bar indicator.
final TabBarTheme tabBarTheme; final TabBarTheme tabBarTheme;
/// The colors and styles used to render [Card].
///
/// This is the value returned from [CardTheme.of].
final CardTheme cardTheme;
/// The colors and styles used to render [Chip], [ /// The colors and styles used to render [Chip], [
/// ///
/// This is the value returned from [ChipTheme.of]. /// This is the value returned from [ChipTheme.of].
...@@ -708,6 +719,7 @@ class ThemeData extends Diagnosticable { ...@@ -708,6 +719,7 @@ class ThemeData extends Diagnosticable {
IconThemeData accentIconTheme, IconThemeData accentIconTheme,
SliderThemeData sliderTheme, SliderThemeData sliderTheme,
TabBarTheme tabBarTheme, TabBarTheme tabBarTheme,
CardTheme cardTheme,
ChipThemeData chipTheme, ChipThemeData chipTheme,
TargetPlatform platform, TargetPlatform platform,
MaterialTapTargetSize materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
...@@ -760,6 +772,7 @@ class ThemeData extends Diagnosticable { ...@@ -760,6 +772,7 @@ class ThemeData extends Diagnosticable {
accentIconTheme: accentIconTheme ?? this.accentIconTheme, accentIconTheme: accentIconTheme ?? this.accentIconTheme,
sliderTheme: sliderTheme ?? this.sliderTheme, sliderTheme: sliderTheme ?? this.sliderTheme,
tabBarTheme: tabBarTheme ?? this.tabBarTheme, tabBarTheme: tabBarTheme ?? this.tabBarTheme,
cardTheme: cardTheme ?? this.cardTheme,
chipTheme: chipTheme ?? this.chipTheme, chipTheme: chipTheme ?? this.chipTheme,
platform: platform ?? this.platform, platform: platform ?? this.platform,
materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize, materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
...@@ -890,6 +903,7 @@ class ThemeData extends Diagnosticable { ...@@ -890,6 +903,7 @@ class ThemeData extends Diagnosticable {
accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t), accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t),
sliderTheme: SliderThemeData.lerp(a.sliderTheme, b.sliderTheme, t), sliderTheme: SliderThemeData.lerp(a.sliderTheme, b.sliderTheme, t),
tabBarTheme: TabBarTheme.lerp(a.tabBarTheme, b.tabBarTheme, t), tabBarTheme: TabBarTheme.lerp(a.tabBarTheme, b.tabBarTheme, t),
cardTheme: CardTheme.lerp(a.cardTheme, b.cardTheme, t),
chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t), chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t),
platform: t < 0.5 ? a.platform : b.platform, platform: t < 0.5 ? a.platform : b.platform,
materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize, materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize,
...@@ -950,6 +964,7 @@ class ThemeData extends Diagnosticable { ...@@ -950,6 +964,7 @@ class ThemeData extends Diagnosticable {
(otherData.accentIconTheme == accentIconTheme) && (otherData.accentIconTheme == accentIconTheme) &&
(otherData.sliderTheme == sliderTheme) && (otherData.sliderTheme == sliderTheme) &&
(otherData.tabBarTheme == tabBarTheme) && (otherData.tabBarTheme == tabBarTheme) &&
(otherData.cardTheme == cardTheme) &&
(otherData.chipTheme == chipTheme) && (otherData.chipTheme == chipTheme) &&
(otherData.platform == platform) && (otherData.platform == platform) &&
(otherData.materialTapTargetSize == materialTapTargetSize) && (otherData.materialTapTargetSize == materialTapTargetSize) &&
...@@ -1010,6 +1025,7 @@ class ThemeData extends Diagnosticable { ...@@ -1010,6 +1025,7 @@ class ThemeData extends Diagnosticable {
sliderTheme, sliderTheme,
hashValues( hashValues(
tabBarTheme, tabBarTheme,
cardTheme,
chipTheme, chipTheme,
platform, platform,
materialTapTargetSize, materialTapTargetSize,
...@@ -1066,6 +1082,7 @@ class ThemeData extends Diagnosticable { ...@@ -1066,6 +1082,7 @@ class ThemeData extends Diagnosticable {
properties.add(DiagnosticsProperty<IconThemeData>('accentIconTheme', accentIconTheme)); properties.add(DiagnosticsProperty<IconThemeData>('accentIconTheme', accentIconTheme));
properties.add(DiagnosticsProperty<SliderThemeData>('sliderTheme', sliderTheme)); properties.add(DiagnosticsProperty<SliderThemeData>('sliderTheme', sliderTheme));
properties.add(DiagnosticsProperty<TabBarTheme>('tabBarTheme', tabBarTheme)); properties.add(DiagnosticsProperty<TabBarTheme>('tabBarTheme', tabBarTheme));
properties.add(DiagnosticsProperty<CardTheme>('cardTheme', cardTheme));
properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme)); properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme));
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize)); properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize));
properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme)); properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme));
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('CardTheme copyWith, ==, hashCode basics', () {
expect(const CardTheme(), const CardTheme().copyWith());
expect(const CardTheme().hashCode, const CardTheme().copyWith().hashCode);
});
testWidgets('Passing no CardTheme returns defaults', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(
home: Scaffold(
body: Card()
),
));
final Container container = _getCardContainer(tester);
final Material material = _getCardMaterial(tester);
expect(material.clipBehavior, Clip.none);
expect(material.color, Colors.white);
expect(material.elevation, 1.0);
expect(container.margin, const EdgeInsets.all(4.0));
expect(material.shape, const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0)),
));
});
testWidgets('Card uses values from CardTheme', (WidgetTester tester) async {
final CardTheme cardTheme = _cardTheme();
await tester.pumpWidget(MaterialApp(
theme: ThemeData(cardTheme: cardTheme),
home: const Scaffold(
body: Card()
),
));
final Container container = _getCardContainer(tester);
final Material material = _getCardMaterial(tester);
expect(material.clipBehavior, cardTheme.clipBehavior);
expect(material.color, cardTheme.color);
expect(material.elevation, cardTheme.elevation);
expect(container.margin, 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 double elevation = 7.0;
const EdgeInsets 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,
elevation: elevation,
margin: margin,
shape: shape,
)
),
));
final Container container = _getCardContainer(tester);
final Material material = _getCardMaterial(tester);
expect(material.clipBehavior, clip);
expect(material.color, color);
expect(material.elevation, elevation);
expect(container.margin, margin);
expect(material.shape, shape);
});
testWidgets('CardTheme properties take priority over ThemeData properties', (WidgetTester tester) async {
final CardTheme 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('ThemeData properties are used when no CardTheme is set', (WidgetTester tester) async {
final ThemeData themeData = _themeData();
await tester.pumpWidget(MaterialApp(
theme: themeData,
home: const Scaffold(
body: Card()
),
));
final Material material = _getCardMaterial(tester);
expect(material.color, themeData.cardColor);
});
testWidgets('CardTheme customizes shape', (WidgetTester tester) async {
const CardTheme cardTheme = CardTheme(
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'),
skip: !Platform.isLinux,
);
});
}
CardTheme _cardTheme() {
return const CardTheme(
clipBehavior: Clip.antiAlias,
color: Colors.green,
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<Material>(
find.descendant(
of: find.byType(Card),
matching: find.byType(Material),
),
);
}
Container _getCardContainer(WidgetTester tester) {
return tester.widget<Container>(
find.descendant(
of: find.byType(Card),
matching: find.byType(Container),
),
);
}
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