Widget Card in Flutter: Properties, Examples, and Material 3 Variants

- Andrés Cruz - ES En español

Widget Card in Flutter: Properties, Examples, and Material 3 Variants

The Card widget in Flutter is a Material Design visual component used to display grouped information inside a card: a container with rounded corners, a uniform background, and a subtle shadow that separates it from the background. It can hold any type of widget—images, text, buttons, forms—and is one of the most common elements in modern mobile and web applications thanks to the visual clarity it brings.

If you come from native Android development, as I did, you will immediately recognize its kinship with the classic CardView: same philosophy, same essence. The Card arrived in Flutter as part of the Material Design ecosystem and has since become one of those widgets you start using "because you have to"… and end up putting on all your screens.

In this guide, I'll explain what the Card widget is, how it works, what its main properties are, and how to take advantage of it with real-world examples. I also include some tricks I learned through trial and error—like that moment when I discovered that a Card only accepts one child and I had to completely rethink my layout .

Previously we saw how to use button widgets: Floating Action Button, FlatButton, MaterialButton, and IconButton. Now it's this widget's turn.

What is a Card in Flutter and what is it for

A Card is a Material Design container designed to display grouped information inside a card with well-defined visual characteristics:

  • Slightly rounded corners
  • Configurable shadow or elevation
  • Uniform background (white by default, customizable)
  • Clear and delimited touch area

Its main advantage is that it allows you to build clean and hierarchical interfaces, avoiding screens where everything seems crammed together. A well-used Card guides the user's eyes to the content that matters.

Relationship with Material Design

The design of the Card in Flutter faithfully follows the official Material Design guidelines:

  • The elevation indicates hierarchy and importance within the interface.
  • The shadow visually separates it from the background, adding depth.
  • The rounded corners soften the overall look of the UI.
  • The new Material 3 variantselevated, filled, and outlined—expand possibilities without requiring additional code.

Elevation, shadow, and rounded corners

The elevation property is what generates the shadow and gives the feeling of depth. In my tests, elevating a Card too much—above 10 or 12—makes it appear artificially floating over the background. I usually work with values between 4 and 8, unless I want to highlight a specific component. You can also customize the shadow color using the shadowColor property.

Rounded corners are controlled using the shape property, with RoundedRectangleBorder:

shape: RoundedRectangleBorder(
  borderRadius: BorderRadius.circular(20),
)

The higher the BorderRadius.circular() value, the more pronounced the rounding is. A value of 20.0 already gives the Card a fairly rounded look.

Main properties of the Card widget in Flutter

The Card widget exposes several properties worth knowing to adapt it to any design. Here I explain them grouped by functionality:

Color, elevation, and shadow

  • color: defines the background color of the Card. By default, it uses the surface color of the active theme.
  • elevation: controls how much shadow it casts, which determines its visual "height" above the background.
  • shadowColor: customizes the color of the shadow. The default value is semitransparent black.

Shape and border customization

With the shape property, you decide the border shape of the Card. You can use:

  • RoundedRectangleBorder → rounded corners (the most common)
  • CircleBorder → full circular shape
  • StadiumBorder → "pill" shaped borders on the ends

If you come from Android, you will see that it is almost identical to the old CardView from the support library.

Margin, padding, and clipBehavior

  • margin: outer space around the Card, which separates it from other elements.
  • padding: typically not defined directly on the Card, but on the inner container widget (a Padding or Container). This gives you more control over spacing.
  • clipBehavior: controls whether inner widgets can overflow beyond the edges of the Card. If you have an image extending past the rounded corners, adding clipBehavior: Clip.antiAlias resolves it immediately.

child: how to organize multiple widgets inside a Card

A Card accepts only a single child. If you need to place multiple elements inside, you must use a container widget that supports multiple children. The most common options are:

  • Column → to stack widgets vertically
  • Row → to align widgets horizontally
  • ListTile → to display structured list-style information
  • Container → to have total control over layout and spacing

Remember: a Card only accepts one child. This is the first thing to internalize when starting to work with this widget.

How to build a basic Card in Flutter step by step

To create a Card in Flutter, you simply instantiate the widget:

Card()

As you might guess, an empty Card doesn't render anything visible. You need to define at least its child property so it has content:

Card(
  child: Center(
    child: Text("Hello Card"),
  ),
)

With this, you already have a functional basic Card: centered text inside a card with default shadow and rounded corners.

Adding Column or Row for multiple elements

When you need to combine an image, text, and a button—which is the most common structure—a Column is your best ally. Here is a complete example with those three elements:

Card(
  child: Column(
    children: [
      Image.asset('assets/codeigniter.png'),
      Text("CodeIgniter Course"),
      IconButton(
        icon: Icon(Icons.access_alarms),
        onPressed: () {},
      )
    ],
  ),
)

Notice that the Card has only one child—the Column—and it is responsible for managing all internal widgets. This pattern is the most common when working with content cards.

Defining images in our Card widgets

Although with this we have practically nothing, since an empty container is of no use to us; we can add all the elements we want, or any other widget through the child property, but we can ONLY add ONE WIDGET, and nothing more:

Card(
  child: Center(
	child: MaterialButton(
  	minWidth: 200.0,
  	height: 40.0,
  	onPressed: () {},
  	color: Colors.lightBlue,
  	child: Text('Material Button',
      	style: TextStyle(color: Colors.white)),
	),
  ),
),

As you can see, here we have several important properties to specify size using width and height, in addition to adding a button with text just as we saw in the previous post on Button widgets in Flutter: Raised, Flat, Material, Icon, and Floating Action.

Flutter Card with a button

Although we can add any type of widget, there are some that really come in handy when working with Cards in Flutter.

As you see, something quite boring and basic, but of course we can use any other widget, and there are widgets that will help us in this task, for example a column widget with which we can add many other widgets:

body: Card(
  child: Center(
	child: Column(
  	children: <Widget>[
    	Image.asset('assets/codeigniter.png'),
    	Text("CodeIgniter Course"),
    	IconButton(
      	icon: Icon(Icons.access_alarms),
      	tooltip: "Message",
    	),
  	],
	),
  ),
),

As you can appreciate in this example, the ideal is to combine widgets; take the features of one widget and add them together; as mentioned before, Cards in Flutter can only contain one widget; therefore, it is almost mandatory to use another widget like Column, which allows us to add as many elements as we want.

Card with text, image, and button inside a column

https://kodestat.gitbook.io/flutter/37-flutter-using-cards

Padding and margins in Flutter Cards

Of course, like any container or layout we use in our applications, it needs a margin; there are also properties for this purpose, for which we can use the padding property as shown below:

Container(
 padding: new EdgeInsets.all(32.0),
 height: 350,
 child: Card(
   color: Color.fromRGBO(84, 197, 248, 1),
   child: Center(
     child: Column(
       children: <Widget>[
         Image.asset('assets/codeigniter.png'),
         Text("CodeIgniter Course"),
         IconButton(
           icon: Icon(Icons.access_alarms),
           tooltip: "Message",
         ),
       ],
     ),
   ),
 ),
)

Here, on this occasion, we varied a few things; first, we defined another type of layout, which is the container, and as you might guess from its name, it allows containing an element through its child attribute; among those elements, we place our Card; and not only that, in this same container, we defined our internal spacing using the padding property; we also took the opportunity to define the height so it doesn't occupy the entire screen:

Card with padding

Rounded corners for Cards

If you come from native Android development, you'll know that one of the main features of CardView is precisely that it has rounded corners by default; here we can also create rounded corners on our Cards using the shape property:

Container(
 padding: new EdgeInsets.all(32.0),
 height: 350,
 child: Card(
   shape: RoundedRectangleBorder(
   borderRadius: BorderRadius.circular(20.0),
 ),
   color: Color.fromRGBO(84, 197, 248, 1),
   child: Center(
     child: Column(
       children: <Widget>[
         Image.asset('assets/codeigniter.png'),
         Text("CodeIgniter Course"),
         IconButton(
           icon: Icon(Icons.access_alarms),
           tooltip: "Message",
         ),
       ],
     ),
   ),
 ),
),
Card with more rounded corners

Finally, here is the full code of our application as well as the link to the official documentation:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   // TODO: implement build
   return MaterialApp(
     home: Scaffold(
       //  backgroundColor: Color.fromRGBO(84, 197, 248, 1),
       appBar: AppBar(
         title: Text('Card'),
       ),
       body: Container(
         padding: new EdgeInsets.all(32.0),
         height: 350,
         child: Card(
           shape: RoundedRectangleBorder(
           borderRadius: BorderRadius.circular(20.0),
         ),
           color: Color.fromRGBO(84, 197, 248, 1),
           child: Center(
             child: Column(
               children: <Widget>[
                 Image.asset('assets/codeigniter.png'),
                 Text("Curso en CodeIgniter"),
                 IconButton(
                   icon: Icon(Icons.access_alarms),
                   tooltip: "Mensaje",
                 ),
               ],
             ),
           ),
         ),
       ),
     ),
   );
 }
}

Creating our first card or Card in Flutter

Cards in Flutter are a fundamental element in Material Design that allow us to create applications with great style. Cards are among the most eye-catching elements in Material Design, and using them is very simple: they allow adapting or placing any type of Widget inside. In this post, we'll give you several examples of how to use them.

We want to create a classic card: an image, a text block, and an image—the typical card used in Material Design:

Classic Material Design Card with Flutter

Nothing unusual for now; we'll start step by step, going from the most basic setup to achieving what we want. To reach this goal, we need to use a type of widget known as Card. First, we need a card using the Flutter widget class called Card:

Card()

But just that won't work on its own; we need to define a property called child, which, as with many other container widgets, specifies the content. So for now, I want a card with text, as simple as that:

return Card(child: Text("Hello World"));

We get:

Card with text

Adding Text and images in the Card Widget

But as you can see, we cannot add our promotional image yet because the child property accepts one and only one Widget. In these cases, we must apply another type of container that accepts a list of widgets; Column fits perfectly for this task, which we also covered in previous posts. Now our code looks like this, allowing us to add our image:

Card(
       child: Column(
     children: <Widget>[
       Image.asset('assets/curso.png'),
       Text("Hello World"),
     ],
   ));

We get:

Classic Material Design Card with Flutter without space

And this is why I mentioned earlier that inside a Card we can add whatever we want, since it is a simple container with a predefined design.

Okay, we're doing much better! By adding a couple more widgets—a container where we place a list of widgets and pass our image stored in the assets folder and a text—we achieved the previous presentation. But we can still get more out of our card; as you can see, our text is too close to the container edges, which doesn't look great, and now our container has lost its rounded corners:

Card(
       child: Column(
     children: <Widget>[
       Image.asset('assets/curso.png'),
       Container(
         padding: EdgeInsets.all(10),
         child: Text("Hello World"),
       )
     ],
   ));
Classic Material Design Card with Flutter

Changing Card borders in Flutter

With this, we managed to give our text more space; for that, we used a container widget and applied an inner margin or padding. Now let's solve the pointed corners issue; basically, we need to use clipBehavior: Clip.antiAlias, on our card, setting it up so that anything overflowing the card boundaries gets clipped:

   return Card(
       clipBehavior: Clip.antiAlias,
       child: Column(
         children: <Widget>[
           Image.asset('assets/curso.png'),
           Container(
             padding: EdgeInsets.all(10),
             child: Text("Hello World"),
           )
         ],
       ));
card with rounded corners

To vary the roundness of the corners, you can do the following:

Card(
       clipBehavior: Clip.antiAlias,
       shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
       child: Column(
         children: <Widget>[
           Image.asset('assets/curso.png'),
           Container(
             padding: EdgeInsets.all(10),
             child: Text("Hello World"),
           )
         ],
       ));
modified card with very rounded corners

Cards with buttons

We'll also show you what you can do here; for this example, we used a few more widgets like ListTile, which allows creating a list item—ideal for displaying information in the style shown for this image. We also used elevation to adjust the shadow; in short, to place buttons side by side, we use a Row, and wrap all of it in a Column to display all content or other widgets vertically:

Card(
     elevation: 5,
     shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)),
     child: Column(
       children: <Widget>[
         ListTile(
          // leading: Icon(Icons.photo_album, color: Colors.blue),
           title: Text("Title"),
           subtitle: Text(
               " here we are working on the flutter card and blah blah"),
         ),
         Row(
           mainAxisAlignment: MainAxisAlignment.end,
           children: <Widget>[
             FlatButton(
                 child: Text("Ok"),
                 onPressed: () {
                   Text("Ok");
                 }),
             FlatButton(
                 child: Text("Cancel"),
                 onPressed: () {
                   Text("Cancel");
                 })
           ],
         )
       ],
     ),
   );
Card with buttons

Card with image, text, and button

A typical design for an article, profile, or product:

Card(
 elevation: 8,
 shadowColor: Colors.black54,
 child: Padding(
   padding: EdgeInsets.all(16),
   child: Column(
     children: [
       Image.asset('assets/card-sample.png'),
       SizedBox(height: 12),
       Text("Full Card Example"),
       ElevatedButton(
         onPressed: () {},
         child: Text("Action"),
       )
     ],
   ),
 ),
)

ListTile-style Card (Material.io style)

Practical for notifications, settings, or lists:

Card(
 child: ListTile(
   leading: Icon(Icons.info),
   title: Text("Card Title"),
   subtitle: Text("Secondary text"),
 ),
)

Tappable cards: InkWell and press actions

For catalog screens or navigation:

Card(
 child: InkWell(
   onTap: () {},
   child: Padding(
     padding: EdgeInsets.all(16),
     child: Text("Tap me"),
   ),
 ),
)

Current Card variants in Material 3

Ideal for content blocks with a solid background.

Card.filled(
 child: Padding(
   padding: EdgeInsets.all(16),
   child: Text("Filled Card"),
 ),
)

Outlined Card

Perfect for minimalist cards without shadow.

Card.outlined(
 child: Padding(
   padding: EdgeInsets.all(16),
   child: Text("Outlined Card"),
 ),
)

Common errors when working with the Card widget

  • Creating an empty Card and thinking "it doesn't work".
    • Without a child, you won't see anything on screen. It is the most common mistake when starting out.
  • Using an elevation that is too high.
    • Values above 12 make the Card look artificially floating. It looks unprofessional.
  • Not adding internal padding.
    • Content stays glued to the edges and looks visually sloppy. Always use at least EdgeInsets.all(16) as a minimum internal padding.
  • Widgets overflowing the borders without clipBehavior.
    • Images are the main culprits. Add clipBehavior: Clip.antiAlias to the Card so clipping respects the rounded corners.

Images inside the Card widget

The Card accepts any widget as a child, including images. However, since it only supports a single child, if you want to combine an image with text or other elements, you need an intermediate container widget.

Cards with buttons using ListTile and Row

Card(
  elevation: 5,
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)),
  child: Column(
    children: <Widget>[
      ListTile(
        title: Text("Title"),
        subtitle: Text("Here we are working on the Flutter card"),
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          TextButton(
            child: Text("Ok"),
            onPressed: () {},
          ),
          TextButton(
            child: Text("Cancel"),
            onPressed: () {},
          )
        ],
      )
    ],
  ),
)

The SizedBox with height: 12 acts as a separator between the image and text, giving that breathing room that makes the design breathe. It is a very simple yet effective pattern.

Note: FlatButton was deprecated in Flutter 2.0. The official replacement is TextButton, which maintains the same behavior but aligns with current Material Design guidelines.

Tappable cards: InkWell and press actions

Card(
 child: InkWell(
   onTap: () {},
   child: Padding(
     padding: EdgeInsets.all(16),
     child: Text("Tap me"),
   ),
 ),
)

You can also use GestureDetector if you prefer detecting more specific gestures (double tap, drag, etc.), although for most cases, InkWell is the most idiomatic choice in Flutter.

Card variants in Material 3

Starting with Material 3, the Card widget in Flutter includes three official variants covering the most common use cases without needing additional configuration:

Card.filled

Ideal for content blocks that need to stand out with a solid background, without shadow. Uses the active theme's surface color:

Card.filled(
 child: Padding(
   padding: EdgeInsets.all(16),
   child: Text("Filled Card"),
 ),
)

Card.outlined

Card.outlined(
 child: Padding(
   padding: EdgeInsets.all(16),
   child: Text("Outlined Card"),
 ),
)

In summary, the three Material 3 variants are:

  • Card() → with shadow (elevated, the classic variant)
  • Card.filled() → solid background without shadow
  • Card.outlined() → border only, without shadow or prominent fill

Best practices for designing clean and professional cards

Hierarchy and content organization

A good visual structure for content inside a Card follows this progression:

Image → Title → Description → Action

This sequence naturally guides the user's eye: first it captures visual attention, then the title and description contextualize it, and finally the action tells them what they can do.

Visual hierarchy and typography

  • Use soft shadows: elevation values between 2 and 6 are sufficient in most cases.
  • Don't mix too many text styles inside a single card: a title, a subtitle, and perhaps a label or price. Nothing more.
  • Generous spacing: a minimum of 16px of internal padding is the Material Design recommendation. Below that, the content feels cramped.

Shadow, contrast, and accessibility

  • Check the contrast between the text and the background color of the Card, especially if you use a custom color. Tools like WebAIM's Contrast Checker help you verify it.
  • Avoid very heavy shadows: the Card can look visually aggressive.
  • Keep rounded corners consistent across the entire application; mixing different radii without clear criteria breaks the visual cohesion of the design.

Frequently asked questions about the Card widget in Flutter

  • How do I add multiple widgets inside a Card?
    • By using a Column, Row, ListTile, or Container as the child. Remember that the Card only accepts one widget directly.
  • How do I make a Card touchable?
    • Wrap it with InkWell (for the Material Design ripple effect) or with GestureDetector (for more specific gestures).
  • What is the difference between Card, Card.filled, and Card.outlined?
    • They are the three Material 3 variants. The first one has a shadow (elevated), the second uses a solid background without shadow, and the third only displays a border without shadow or prominent fill.
  • How do I change the shape of the Card?
    • Using the shape property. For example: shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)).
  • How do I customize the background color of the Card?
    • Using the color property: color: Colors.blue.shade50 or any Color type value you need.
  • How do I prevent the image from overflowing the rounded corners?
    • Add clipBehavior: Clip.antiAlias to the Card. That automatically clips any content that protrudes past the rounded border.

Conclusion

The Card widget in Flutter is extremely versatile: you can use it for lists, forms, product catalogs, user profiles, settings panels, and virtually any content block you want to present in an orderly and visually clear manner.

By simply mastering its main properties —color, shape, elevation, shadowColor, and clipBehavior— and combining it with Column, images, and buttons, you can build clean, professional interfaces that adapt perfectly to any type of application.

Best of all: its behavior is consistent across Android, iOS, and web. A single implementation, three platforms covered.

The next step is to explore the Material 3 variants (Card.filled and Card.outlined) to give your interfaces a more modern look with minimal effort.

Next widget: the Slider widget for defining ranges in Flutter.

Learn how to use the Card widget in Flutter with real-world code examples. Master elevation, border radius, color, clipBehavior, and the new Card.filled and Card.outlined variants from Material 3.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

I agree to receive announcements of interest about this Blog.