card.dart 5.72 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/widgets.dart';
6

7
import 'card_theme.dart';
8
import 'material.dart';
Hans Muller's avatar
Hans Muller committed
9
import 'theme.dart';
10

11 12
/// A material design card: a panel with slightly rounded corners and an
/// elevation shadow.
13 14 15
///
/// 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.
16
///
17 18 19 20 21 22
/// 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)
///
23
/// {@tool dartpad}
24 25
/// This sample shows creation of a [Card] widget that shows album information
/// and two actions.
26
///
27
/// ** See code in examples/api/lib/material/card/card.0.dart **
28
/// {@end-tool}
29
///
30 31 32
/// 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.
///
33
/// {@tool dartpad}
34 35 36 37
/// 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.
///
38
/// ** See code in examples/api/lib/material/card/card.1.dart **
39 40
/// {@end-tool}
///
41 42
/// See also:
///
43
///  * [ListTile], to display icons and text in a card.
44
///  * [showDialog], to display a modal card.
45
///  * <https://material.io/design/components/cards.html>
46
class Card extends StatelessWidget {
47
  /// Creates a material design card.
48
  ///
49 50
  /// The [elevation] must be null or non-negative. The [borderOnForeground]
  /// must not be null.
51
  const Card({
52
    Key? key,
53
    this.color,
54
    this.shadowColor,
55
    this.elevation,
Hans Muller's avatar
Hans Muller committed
56
    this.shape,
57
    this.borderOnForeground = true,
58 59
    this.margin,
    this.clipBehavior,
60
    this.child,
61
    this.semanticContainer = true,
62
  }) : assert(elevation == null || elevation >= 0.0),
63
       assert(borderOnForeground != null),
64
       super(key: key);
65

Hans Muller's avatar
Hans Muller committed
66
  /// The card's background color.
67
  ///
Hans Muller's avatar
Hans Muller committed
68 69
  /// Defines the card's [Material.color].
  ///
70 71
  /// If this property is null then [CardTheme.color] of [ThemeData.cardTheme]
  /// is used. If that's null then [ThemeData.cardColor] is used.
72
  final Color? color;
73

74 75 76
  /// The color to paint the shadow below the card.
  ///
  /// If null then the ambient [CardTheme]'s shadowColor is used.
77 78
  /// If that's null too, then the overall theme's [ThemeData.shadowColor]
  /// (default black) is used.
79
  final Color? shadowColor;
80

81 82
  /// The z-coordinate at which to place this card. This controls the size of
  /// the shadow below the card.
83
  ///
Hans Muller's avatar
Hans Muller committed
84 85
  /// Defines the card's [Material.elevation].
  ///
86 87
  /// If this property is null then [CardTheme.elevation] of
  /// [ThemeData.cardTheme] is used. If that's null, the default value is 1.0.
88
  final double? elevation;
89

Hans Muller's avatar
Hans Muller committed
90 91 92 93
  /// The shape of the card's [Material].
  ///
  /// Defines the card's [Material.shape].
  ///
94 95 96
  /// 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.
97
  final ShapeBorder? shape;
Hans Muller's avatar
Hans Muller committed
98

99 100 101 102 103 104
  /// 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;

105
  /// {@macro flutter.material.Material.clipBehavior}
106
  ///
107 108
  /// If this property is null then [CardTheme.clipBehavior] of
  /// [ThemeData.cardTheme] is used. If that's null then the behavior will be [Clip.none].
109
  final Clip? clipBehavior;
110

111 112 113 114
  /// The empty space that surrounds the card.
  ///
  /// Defines the card's outer [Container.margin].
  ///
115 116 117
  /// 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)`.
118
  final EdgeInsetsGeometry? margin;
119

120 121 122
  /// Whether this widget represents a single semantic container, or if false
  /// a collection of individual semantic nodes.
  ///
123
  /// Defaults to true.
124 125 126 127 128 129 130 131 132
  ///
  /// 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;

Hans Muller's avatar
Hans Muller committed
133 134
  /// The widget below this widget in the tree.
  ///
135
  /// {@macro flutter.widgets.ProxyWidget.child}
136
  final Widget? child;
Hans Muller's avatar
Hans Muller committed
137

138 139
  static const double _defaultElevation = 1.0;

140
  @override
141
  Widget build(BuildContext context) {
142
    final ThemeData theme = Theme.of(context);
143 144
    final CardTheme cardTheme = CardTheme.of(context);

145
    return Semantics(
146
      container: semanticContainer,
147
      child: Container(
148
        margin: margin ?? cardTheme.margin ?? const EdgeInsets.all(4.0),
149
        child: Material(
150
          type: MaterialType.card,
151 152
          shadowColor: shadowColor ?? cardTheme.shadowColor ?? theme.shadowColor,
          color: color ?? cardTheme.color ?? theme.cardColor,
153 154
          elevation: elevation ?? cardTheme.elevation ?? _defaultElevation,
          shape: shape ?? cardTheme.shape ?? const RoundedRectangleBorder(
155
            borderRadius: BorderRadius.all(Radius.circular(4.0)),
Hans Muller's avatar
Hans Muller committed
156
          ),
157
          borderOnForeground: borderOnForeground,
158
          clipBehavior: clipBehavior ?? cardTheme.clipBehavior ?? Clip.none,
159 160 161 162
          child: Semantics(
            explicitChildNodes: !semanticContainer,
            child: child,
          ),
Hans Muller's avatar
Hans Muller committed
163
        ),
164
      ),
165 166 167
    );
  }
}