
This PR is to introduce 19 new color roles and deprecate 3 color roles in `ColorScheme`. **Tone-based surface colors** (7 colors): * surfaceBright * surfaceDim * surfaceContainer * surfaceContainerLowest * surfaceContainerLow * surfaceContainerHigh * surfaceContainerHighest **Accent color add-ons** (12 colors): * primary/secondary/tertiary-Fixed * primary/secondary/tertiary-FixedDim * onPrimary/onSecondary/onTertiary-Fixed * onPrimary/onSecondary/onTertiary-FixedVariant **Deprecated colors**: * background -> replaced with surface * onBackground -> replaced with onSurface * surfaceVariant -> replaced with surfaceContainerHighest Please checkout this [design doc](https://docs.google.com/document/d/1ODqivpM_6c490T4j5XIiWCDKo5YqHy78YEFqDm4S8h4/edit?usp=sharing) for more information:) 
373 lines
12 KiB
Dart
373 lines
12 KiB
Dart
// 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 'color_scheme.dart';
|
|
import 'colors.dart';
|
|
import 'material.dart';
|
|
import 'theme.dart';
|
|
|
|
enum _CardVariant { elevated, filled, outlined }
|
|
|
|
/// 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:
|
|
///
|
|
/// 
|
|
///
|
|
/// {@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. The default [Card] is the
|
|
/// elevated card. To create a filled card, use [Card.filled]; to create a outlined
|
|
/// card, use [Card.outlined].
|
|
/// {@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.
|
|
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),
|
|
_variant = _CardVariant.elevated;
|
|
|
|
/// Create a filled variant of Card.
|
|
///
|
|
/// Filled cards provide subtle separation from the background. This has less
|
|
/// emphasis than elevated(default) or outlined cards.
|
|
const Card.filled({
|
|
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),
|
|
_variant = _CardVariant.filled;
|
|
|
|
/// Create an outlined variant of Card.
|
|
///
|
|
/// Outlined cards have a visual boundary around the container. This can
|
|
/// provide greater emphasis than the other types.
|
|
const Card.outlined({
|
|
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),
|
|
_variant = _CardVariant.outlined;
|
|
|
|
/// 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.surfaceContainerLow] 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.
|
|
///
|
|
/// This is not recommended for use. [Material 3 spec](https://m3.material.io/styles/color/the-color-system/color-roles)
|
|
/// introduced a set of tone-based surfaces and surface containers in its [ColorScheme],
|
|
/// which provide more flexibility. The intention is to eventually remove surface tint color from
|
|
/// the framework.
|
|
///
|
|
/// 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 [Colors.transparent].
|
|
///
|
|
/// 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 12.0 and if [ThemeData.useMaterial3] is
|
|
/// false, then the circular corner radius will be 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;
|
|
|
|
final _CardVariant _variant;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final CardTheme cardTheme = CardTheme.of(context);
|
|
final CardTheme defaults;
|
|
if (Theme.of(context).useMaterial3) {
|
|
switch (_variant) {
|
|
case _CardVariant.elevated:
|
|
defaults = _CardDefaultsM3(context);
|
|
case _CardVariant.filled:
|
|
defaults = _FilledCardDefaultsM3(context);
|
|
case _CardVariant.outlined:
|
|
defaults = _OutlinedCardDefaultsM3(context);
|
|
}
|
|
} else {
|
|
defaults = _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.
|
|
|
|
class _CardDefaultsM3 extends CardTheme {
|
|
_CardDefaultsM3(this.context)
|
|
: super(
|
|
clipBehavior: Clip.none,
|
|
elevation: 1.0,
|
|
margin: const EdgeInsets.all(4.0),
|
|
);
|
|
|
|
final BuildContext context;
|
|
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
|
|
|
@override
|
|
Color? get color => _colors.surfaceContainerLow;
|
|
|
|
@override
|
|
Color? get shadowColor => _colors.shadow;
|
|
|
|
@override
|
|
Color? get surfaceTintColor => Colors.transparent;
|
|
|
|
@override
|
|
ShapeBorder? get shape =>const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0)));
|
|
}
|
|
|
|
// END GENERATED TOKEN PROPERTIES - Card
|
|
|
|
// BEGIN GENERATED TOKEN PROPERTIES - FilledCard
|
|
|
|
// 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.
|
|
|
|
class _FilledCardDefaultsM3 extends CardTheme {
|
|
_FilledCardDefaultsM3(this.context)
|
|
: super(
|
|
clipBehavior: Clip.none,
|
|
elevation: 0.0,
|
|
margin: const EdgeInsets.all(4.0),
|
|
);
|
|
|
|
final BuildContext context;
|
|
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
|
|
|
@override
|
|
Color? get color => _colors.surfaceContainerHighest;
|
|
|
|
@override
|
|
Color? get shadowColor => _colors.shadow;
|
|
|
|
@override
|
|
Color? get surfaceTintColor => Colors.transparent;
|
|
|
|
@override
|
|
ShapeBorder? get shape =>const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0)));
|
|
}
|
|
|
|
// END GENERATED TOKEN PROPERTIES - FilledCard
|
|
|
|
// BEGIN GENERATED TOKEN PROPERTIES - OutlinedCard
|
|
|
|
// 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.
|
|
|
|
class _OutlinedCardDefaultsM3 extends CardTheme {
|
|
_OutlinedCardDefaultsM3(this.context)
|
|
: super(
|
|
clipBehavior: Clip.none,
|
|
elevation: 0.0,
|
|
margin: const EdgeInsets.all(4.0),
|
|
);
|
|
|
|
final BuildContext context;
|
|
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
|
|
|
@override
|
|
Color? get color => _colors.surface;
|
|
|
|
@override
|
|
Color? get shadowColor => _colors.shadow;
|
|
|
|
@override
|
|
Color? get surfaceTintColor => Colors.transparent;
|
|
|
|
@override
|
|
ShapeBorder? get shape =>
|
|
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0))).copyWith(
|
|
side: BorderSide(color: _colors.outlineVariant)
|
|
);
|
|
}
|
|
|
|
// END GENERATED TOKEN PROPERTIES - OutlinedCard
|