blob: bf2169b72fd1098229933b439cc753ebe7e3f702 [file] [log] [blame] [edit]
// 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.
import 'package:flutter/widgets.dart';
import 'card_theme.dart';
import 'material.dart';
import 'theme.dart';
/// A Material Design card: a panel with slightly rounded corners and an
/// elevation shadow.
///
/// A card is a sheet of [Material] used to represent some related information,
/// for example an album, a geographical location, a meal, contact details, etc.
///
/// This is what it looks like when run:
///
/// ![A card with a slight shadow, consisting of two rows, one with an icon and
/// some text describing a musical, and the other with buttons for buying
/// tickets or listening to the show.](https://flutter.github.io/assets-for-api-docs/assets/material/card.png)
///
/// {@tool dartpad}
/// This sample shows creation of a [Card] widget that shows album information
/// and two actions.
///
/// ** See code in examples/api/lib/material/card/card.0.dart **
/// {@end-tool}
///
/// Sometimes the primary action area of a card is the card itself. Cards can be
/// one large touch target that shows a detail screen when tapped.
///
/// {@tool dartpad}
/// This sample shows creation of a [Card] widget that can be tapped. When
/// tapped this [Card]'s [InkWell] displays an "ink splash" that fills the
/// entire card.
///
/// ** See code in examples/api/lib/material/card/card.1.dart **
/// {@end-tool}
///
/// Material Design 3 introduced new types of cards. These can
/// be produced by configuring the [Card] widget's properties.
/// [Card] widget.
/// {@tool dartpad}
/// This sample shows creation of [Card] widgets for elevated, filled and
/// outlined types, as described in: https://m3.material.io/components/cards/overview
///
/// ** See code in examples/api/lib/material/card/card.2.dart **
/// {@end-tool}
///
/// See also:
///
/// * [ListTile], to display icons and text in a card.
/// * [showDialog], to display a modal card.
/// * <https://material.io/design/components/cards.html>
/// * <https://m3.material.io/components/cards>
class Card extends StatelessWidget {
/// Creates a Material Design card.
///
/// The [elevation] must be null or non-negative. The [borderOnForeground]
/// must not be null.
const Card({
super.key,
this.color,
this.shadowColor,
this.surfaceTintColor,
this.elevation,
this.shape,
this.borderOnForeground = true,
this.margin,
this.clipBehavior,
this.child,
this.semanticContainer = true,
}) : assert(elevation == null || elevation >= 0.0);
/// The card's background color.
///
/// Defines the card's [Material.color].
///
/// If this property is null then the ambient [CardTheme.color] is used. If that is null,
/// and [ThemeData.useMaterial3] is true, then [ColorScheme.surface] of
/// [ThemeData.colorScheme] is used. Otherwise, [ThemeData.cardColor] is used.
final Color? color;
/// The color to paint the shadow below the card.
///
/// If null then the ambient [CardTheme]'s shadowColor is used.
/// If that's null too, then the overall theme's [ThemeData.shadowColor]
/// (default black) is used.
final Color? shadowColor;
/// The color used as an overlay on [color] to indicate elevation.
///
/// If this is null, no overlay will be applied. Otherwise this color
/// will be composited on top of [color] with an opacity related
/// to [elevation] and used to paint the background of the card.
///
/// The default is null.
///
/// See [Material.surfaceTintColor] for more details on how this
/// overlay is applied.
final Color? surfaceTintColor;
/// The z-coordinate at which to place this card. This controls the size of
/// the shadow below the card.
///
/// Defines the card's [Material.elevation].
///
/// If this property is null then [CardTheme.elevation] of
/// [ThemeData.cardTheme] is used. If that's null, the default value is 1.0.
final double? elevation;
/// The shape of the card's [Material].
///
/// Defines the card's [Material.shape].
///
/// If this property is null then [CardTheme.shape] of [ThemeData.cardTheme]
/// is used. If that's null then the shape will be a [RoundedRectangleBorder]
/// with a circular corner radius of 4.0.
final ShapeBorder? shape;
/// Whether to paint the [shape] border in front of the [child].
///
/// The default value is true.
/// If false, the border will be painted behind the [child].
final bool borderOnForeground;
/// {@macro flutter.material.Material.clipBehavior}
///
/// If this property is null then [CardTheme.clipBehavior] of
/// [ThemeData.cardTheme] is used. If that's null then the behavior will be [Clip.none].
final Clip? clipBehavior;
/// The empty space that surrounds the card.
///
/// Defines the card's outer [Container.margin].
///
/// If this property is null then [CardTheme.margin] of
/// [ThemeData.cardTheme] is used. If that's null, the default margin is 4.0
/// logical pixels on all sides: `EdgeInsets.all(4.0)`.
final EdgeInsetsGeometry? margin;
/// Whether this widget represents a single semantic container, or if false
/// a collection of individual semantic nodes.
///
/// Defaults to true.
///
/// Setting this flag to true will attempt to merge all child semantics into
/// this node. Setting this flag to false will force all child semantic nodes
/// to be explicit.
///
/// This flag should be false if the card contains multiple different types
/// of content.
final bool semanticContainer;
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final Widget? child;
@override
Widget build(BuildContext context) {
final CardTheme cardTheme = CardTheme.of(context);
final CardTheme defaults = Theme.of(context).useMaterial3 ? _CardDefaultsM3(context) : _CardDefaultsM2(context);
return Semantics(
container: semanticContainer,
child: Container(
margin: margin ?? cardTheme.margin ?? defaults.margin!,
child: Material(
type: MaterialType.card,
color: color ?? cardTheme.color ?? defaults.color,
shadowColor: shadowColor ?? cardTheme.shadowColor ?? defaults.shadowColor,
surfaceTintColor: surfaceTintColor ?? cardTheme.surfaceTintColor ?? defaults.surfaceTintColor,
elevation: elevation ?? cardTheme.elevation ?? defaults.elevation!,
shape: shape ?? cardTheme.shape ?? defaults.shape,
borderOnForeground: borderOnForeground,
clipBehavior: clipBehavior ?? cardTheme.clipBehavior ?? defaults.clipBehavior!,
child: Semantics(
explicitChildNodes: !semanticContainer,
child: child,
),
),
),
);
}
}
// Hand coded defaults based on Material Design 2.
class _CardDefaultsM2 extends CardTheme {
const _CardDefaultsM2(this.context)
: super(
clipBehavior: Clip.none,
elevation: 1.0,
margin: const EdgeInsets.all(4.0),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0)),
)
);
final BuildContext context;
@override
Color? get color => Theme.of(context).cardColor;
@override
Color? get shadowColor => Theme.of(context).shadowColor;
}
// BEGIN GENERATED TOKEN PROPERTIES - Card
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_152
class _CardDefaultsM3 extends CardTheme {
const _CardDefaultsM3(this.context)
: super(
clipBehavior: Clip.none,
elevation: 1.0,
margin: const EdgeInsets.all(4.0),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0))),
);
final BuildContext context;
@override
Color? get color => Theme.of(context).colorScheme.surface;
@override
Color? get shadowColor => Theme.of(context).colorScheme.shadow;
@override
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
}
// END GENERATED TOKEN PROPERTIES - Card