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 variants—
elevated,filled, andoutlined—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 theCard. 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 shapeStadiumBorder→ "pill" shaped borders on the ends
If you come from Android, you will see that it is almost identical to the old
CardViewfrom the support library.
Margin, padding, and clipBehavior
margin: outer space around theCard, which separates it from other elements.padding: typically not defined directly on theCard, but on the inner container widget (aPaddingorContainer). This gives you more control over spacing.clipBehavior: controls whether inner widgets can overflow beyond the edges of theCard. If you have an image extending past the rounded corners, addingclipBehavior: Clip.antiAliasresolves 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 verticallyRow→ to align widgets horizontallyListTile→ to display structured list-style informationContainer→ to have total control over layout and spacing
Remember: a
Cardonly accepts onechild. 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.

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.

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:

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",
),
],
),
),
),
),
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:

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:

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:

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"),
)
],
));
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"),
)
],
));
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"),
)
],
));
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 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
Cardand thinking "it doesn't work".- Without a
child, you won't see anything on screen. It is the most common mistake when starting out.
- Without a
- Using an
elevationthat is too high.- Values above
12make theCardlook artificially floating. It looks unprofessional.
- Values above
- 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.
- Content stays glued to the edges and looks visually sloppy. Always use at least
- Widgets overflowing the borders without
clipBehavior.- Images are the main culprits. Add
clipBehavior: Clip.antiAliasto theCardso clipping respects the rounded corners.
- Images are the main culprits. Add
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 shadowCard.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:
elevationvalues between2and6are 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
16pxof internalpaddingis 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 customcolor. Tools like WebAIM's Contrast Checker help you verify it. - Avoid very heavy shadows: the
Cardcan 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, orContaineras thechild. Remember that theCardonly accepts one widget directly.
- By using a
- ❓ How do I make a Card touchable?
- Wrap it with
InkWell(for the Material Design ripple effect) or withGestureDetector(for more specific gestures).
- Wrap it with
- ❓ What is the difference between
Card,Card.filled, andCard.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
shapeproperty. For example:shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)).
- Using the
- ❓ How do I customize the background color of the Card?
- Using the
colorproperty:color: Colors.blue.shade50or anyColortype value you need.
- Using the
- ❓ How do I prevent the image from overflowing the rounded corners?
- Add
clipBehavior: Clip.antiAliasto theCard. That automatically clips any content that protrudes past the rounded border.
- Add
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.