In Flutter, how would you build a coupon card with a gradient, rounded corners, an edge-to-edge image and a dashed border, and where do BoxDecoration's limits force other tools?
answer
- gradient and borderRadius in BoxDecoration
- decorations never clip the child
- Card's clipBehavior defaults to Clip.none
- BorderStyle has only none and solid
- uniform border colors with borderRadius
basics
~20 sBoxDecoration supplies the gradient, radius and shadow, but it never clips the child and cannot dash a border. Clip the image with ClipRRect or clipBehavior (Card defaults to Clip.none), and draw the dashed outline with a custom painter or a package.
solid answer
~40 sI start with a `BoxDecoration`: `gradient: LinearGradient(...)`, `borderRadius: BorderRadius.circular(16)` and a soft `boxShadow`, painted by a `DecoratedBox` or `Container`. The image then bleeds over the rounded corners, because decorations paint behind the child and never clip it; I add `clipBehavior: Clip.antiAlias` on the `Container`, or a `ClipRRect` with the same radius. If I use a Material 3 `Card` instead, I must set `clipBehavior` there too, since it defaults to `Clip.none`. The dashed edge is where `BoxDecoration` stops: `BorderStyle` has only `none` and `solid`, so a dashed or perforated outline needs a custom painter or a package. Two assertions to know: a `borderRadius` with a `Border` whose sides have different colors, and `BoxShape.circle` combined with a `borderRadius`.
go deeper
Know BoxDecoration's main fields, gradient, borderRadius, boxShadow and border, and that a decoration does not clip its child.
Explain the clipping options, Card's Clip.none default, and the assertions for circle with radius and non-uniform border colors.
Build the card so shadows survive clipping, keep clipping minimal for raster cost, and plan a painter or ShapeBorder for the dashed edge.
Decide when a shared coupon component justifies a custom ShapeBorder versus a package, weighing maintenance against visual fidelity.
## What BoxDecoration can paint `BoxDecoration` is the everyday way to style a rectangle in Flutter. Its constructor takes `color`, `image`, `border`, `borderRadius`, `boxShadow`, `gradient`, `backgroundBlendMode` and `shape` (default `BoxShape.rectangle`). Paint order, bottom to top: 1. `boxShadow` outside the box. 2. `color`, then `gradient` on top of it. 3. `image` (a `DecorationImage`). 4. `border`. For the coupon's surface this is enough: ```dart import 'package:flutter/material.dart'; class CouponCard extends StatelessWidget { const CouponCard({super.key, required this.image, required this.body}); final ImageProvider image; final Widget body; @override Widget build(BuildContext context) { return Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: <Color>[Color(0xFFFFF3E0), Color(0xFFFFCC80)], ), borderRadius: BorderRadius.circular(16), boxShadow: const <BoxShadow>[ BoxShadow(color: Color(0x33000000), blurRadius: 12, offset: Offset(0, 4)), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Image(image: image, height: 120, fit: BoxFit.cover), Padding(padding: const EdgeInsets.all(16), child: body), ], ), ); } } ``` ## Limit 1: decorations do not clip A decoration paints **behind** the child (or in front, with `DecorationPosition.foreground`), but the child is laid out and painted in the full rectangle. Without clipping, the image's square corners poke out of the rounded card. Options: - **`Container.clipBehavior`** (it requires a `decoration`): clips the child to the decoration's shape, as above. - **`ClipRRect`** with the same `borderRadius`, whose `clipBehavior` defaults to `Clip.antiAlias`. - **`Card(clipBehavior: Clip.antiAlias)`** when using Material's card. In the example, the shadow still shows because `Container` puts the clip inside the decoration, so only the child is clipped. Clipping has a raster cost, so clip only where content actually reaches the corners. ## Limit 2: Card defaults Material 3's `Card` (and `Card.filled`, `Card.outlined`) resolves `clipBehavior` from the widget, then `CardThemeData`, then the default **`Clip.none`**. Its Material 3 defaults are an elevation of `1.0`, a margin of `4.0` on every side and a 12-pixel corner radius. Developers who switch from a `Container` to a `Card` for its elevation often rediscover the bleeding corners for exactly this reason. ## Limit 3: no dashed borders `BorderSide.style` is a `BorderStyle`, and `BorderStyle` has two values: **`none` and `solid`**. There is no dashed or dotted style in `BoxDecoration`, `Border` or `ShapeDecoration`. A coupon's dashed or perforated edge therefore needs one of: - A custom painter that walks the rounded rectangle's path and draws dash segments. - A custom `ShapeBorder` subclass that paints dashes, usable in `ShapeDecoration` or a `Card`'s `shape`. - A pub package that implements one of the above. How to write that painter is a painting topic of its own; the interview point is knowing that the built-in decoration cannot do it, rather than hunting for a `dashed` flag. ## Assertions that catch mixed settings | Combination | Result in debug mode | |---|---| | `Container(color: ..., decoration: ...)` | Assertion: provide one or the other | | `BoxDecoration(shape: BoxShape.circle, borderRadius: ...)` | Assertion: a circle cannot have a border radius | | `borderRadius` with a `Border` whose sides differ in color | Error: a borderRadius can only be given on borders with uniform colors | | `backgroundBlendMode` without `color` or `gradient` | Assertion | | `Container(clipBehavior: ...)` without `decoration` | Assertion | ## Gradients and shadows in detail `LinearGradient`, `RadialGradient` and `SweepGradient` all work in `BoxDecoration.gradient`, and each takes optional `stops` to control where colors change. A gradient paints over `color`, so setting both is allowed but only the gradient shows where it is opaque. `BoxShadow` takes `color`, `offset`, `blurRadius` and `spreadRadius`; several shadows can be layered to approximate a designer's elevation. Colors in a shared coupon component should come from the theme or a `ThemeExtension` rather than hex literals, so brands and dark mode apply. ## Choosing the container widget - **`DecoratedBox`**: just the paint, `const`-constructible. - **`Container`**: paint plus padding, size, margin and clipping in one place, as in the example. - **`Card`**: Material elevation, surface tint and theme integration via `CardThemeData`; remember its clip default. - **`ClipRRect`**: clipping only, around whatever you already have. ## Diagnosing a card that "looks wrong" 1. Corners of an image show: add clipping. 2. Shadow missing: check whether a parent clips the whole card, or whether `Card` elevation was set to 0. 3. Border radius ignored on one side: check for a non-uniform `Border` or a `BorderRadius.only` with missing corners. 4. Dashed edge requested: plan a painter or a `ShapeBorder` rather than adjusting `BorderSide`.
- In Flutter, why does clipping the coupon with an outer ClipRRect hide its BoxShadow while Container.clipBehavior does not?A `ClipRRect` around the decorated box clips everything it paints, including the shadow drawn outside the rounded rectangle. `Container.clipBehavior` inserts the clip inside the `DecoratedBox`, so only the child is clipped and the shadow stays visible. Put an outer clip only around content that has no shadow.
- In Flutter, can a Card itself have a dashed outline?Not with a built-in option: `Card.shape` takes a `ShapeBorder`, and the stock borders use `BorderSide`, whose `BorderStyle` is only `none` or `solid`. You would pass a custom `ShapeBorder` subclass that paints dashes, or draw the dashes with a painter layered over the card.
saying these in an interview costs you the question
- Believing BoxDecoration's borderRadius clips the child image
- Looking for a dashed value in BorderStyle
- Assuming Material 3 Card clips its content by default
- Wrapping a shadowed card in ClipRRect and wondering where the shadow went
- Combining BoxShape.circle with a borderRadius