Flux Card
A composition-first, constraint-aware card layout engine for Flutter.
flux_card helps you build rich, reusable cards from a consistent set of primitives instead of
repeating one-off widget trees across your app.
It gives you:
- named content slots
- multiple layout modes
- overlays and underlays
- notch support
- built-in loading skeletons
- theme presets with
ThemeExtension
Version 0.1.0 is the first public release.
Preview

Why use flux_card?
Most apps start with a simple card or two, then quickly end up with many variations:
- product cards
- article cards
- compact list cards
- promo banners
- profile cards
- ticket / coupon cards
- cards with badges, ribbons, overlays, and decorative backgrounds
- cards with loading states that should still match the final layout
At that point, teams usually either duplicate UI or build fragile ad-hoc abstractions.
flux_card gives you a structured middle ground:
- flexible enough for custom layouts
- consistent enough to scale across a design system
- focused enough to stay pleasant to use
Features
FluxCardroot widget- Named slots:
media,header,body,footer - Layout modes:
column,row,responsive,inline FluxMedia,FluxSection, andFluxContenthelper widgetsFluxOverlayandFluxUnderlaywith slot targetingFluxOverlayBehavior.breakoutfor extruding overlays without disabling card clipping- Theme presets:
standard,compact,elevated,outlined - Loading support with
FluxCardSkeleton - Notch support with multiple notch styles
- Widgetbook examples and test coverage included
Installation
Add to your pubspec.yaml:
dependencies:
flux_card: ^0.1.0
Then import:
import 'package:flux_card/flux_card.dart';
Quick start
FluxCard(
media: FluxMedia(
aspectRatio: 16 / 9,
child: Ink.image(
image: const NetworkImage('https://example.com/image.jpg'),
fit: BoxFit.cover,
),
),
header: const FluxSection(
title: Text('Card title'),
subtitle: Text('Supporting text'),
padding: EdgeInsets.zero,
),
body: const Text('Body content goes here.'),
footer: FluxSection.footer(
padding: EdgeInsets.zero,
actions: [
FilledButton(
onPressed: null,
child: Text('Action'),
),
],
),
theme: FluxCardThemeData.elevated,
onTap: () {},
)
Core ideas
1. Slots
A FluxCard is built from four optional slots:
mediaheaderbodyfooter
Slots can contain any widget, but the package includes helper widgets for common patterns:
FluxMediaFluxSectionFluxContent
If a slot is null, it is omitted automatically.
2. Layers
Cards can have:
- underlays behind content
- overlays above content
This makes it easy to add:
- badges
- chips
- decorative gradients
- pricing ribbons
- texture / accent layers
- extruding promotional elements
3. Layout modes
You can switch between:
FluxLayoutMode.columnFluxLayoutMode.rowFluxLayoutMode.responsiveFluxLayoutMode.inline
4. Theme
FluxCardThemeData is a ThemeExtension, so cards can be themed:
- per card
- per screen
- app-wide
Widget overview
FluxCard
The root widget.
FluxCard({
FluxLayoutMode layout = FluxLayoutMode.column,
FluxMediaPosition mediaPosition = FluxMediaPosition.start,
FluxMediaSpan mediaSpan = FluxMediaSpan.all,
Widget? media,
Widget? header,
Widget? body,
Widget? footer,
List<Widget>? overlays,
List<Widget>? underlays,
Color? foregroundColor,
BoxDecoration? decoration,
FluxNotch? notch,
FluxDivider? divider,
double? width,
double? height,
bool fullWidth = false,
bool fullHeight = false,
FluxCardThemeData? theme,
Clip? clipBehavior,
String? semanticLabel,
VoidCallback? onTap,
VoidCallback? onLongPress,
bool loading = false,
Widget Function(BuildContext, Widget)? loadingWrapper,
})
Use FluxCard when you want a reusable card surface that can scale from simple content cards to
highly composed promo or commerce layouts.
FluxMedia
FluxMedia is a media-slot container for images or custom media content.
FluxMedia(
aspectRatio: 4 / 3,
child: Ink.image(
image: const NetworkImage(imageUrl),
fit: BoxFit.cover,
),
)
Supports:
aspectRatio- explicit
width/height borderRadiuscolor/gradientforegroundColor/foregroundGradient
FluxSection
FluxSection is a structured section widget for header/footer style content.
const FluxSection(
title: Text('Product name'),
subtitle: Text('Short supporting text'),
padding: EdgeInsets.zero,
)
Good for:
- title blocks
- metadata rows
- footer action areas
- structured content with leading / title / subtitle / trailing patterns
Also includes:
FluxSection.footer(...)
FluxContent
FluxContent is a flexible body wrapper.
Useful constructors:
FluxContent.column(...)FluxContent.row(...)FluxContent.wrap(...)
Use it for:
- grouped body text
- chip collections
- feature lists
- richer body layouts
FluxOverlay
FluxOverlay adds content above a selected slot or the whole card.
FluxOverlay(
targets: const {FluxTarget.media},
alignment: Alignment.topRight,
children: [
Chip(label: Text('New')),
],
)
Overlay behaviors
Use behavior to control how overlays are rendered:
FluxOverlayBehavior.containedkeeps the overlay inside the card layerFluxOverlayBehavior.breakoutallows the overlay to extend outside the card while the card itself remains clipped
Example:
FluxOverlay(
behavior: FluxOverlayBehavior.breakout,
targets: const {FluxTarget.media},
alignment: Alignment.bottomRight,
children: [
SizedBox(
width: 120,
height: 180,
child: Placeholder(),
),
],
)
This is the recommended way to build extruding overlays.
FluxUnderlay
FluxUnderlay adds decoration behind a slot or the whole card.
FluxUnderlay(
targets: const {FluxTarget.card},
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0x11000000), Color(0x00000000)],
),
),
)
Useful for:
- tinted surfaces
- subtle background gradients
- section accents
- decorative panels
FluxDivider
FluxDivider inserts widgets between named slot boundaries.
FluxDivider(
afterHeader: Divider(height: 1),
)
Useful for:
- ticket separators
- section separation
- visual rhythm between content blocks
FluxNotch
FluxNotch adds shaped cutouts to the card outline.
Supported styles
FluxNotch.ticket(...)FluxNotch.ticketFree(...)FluxNotch.vShape(...)FluxNotch.vShapeFree(...)FluxNotch.slant(...)FluxNotch.slantFree(...)
Example:
FluxCard(
notch: const FluxNotch.ticket(
boundary: FluxSlotBoundary.afterHeader,
notchDepth: 14,
),
divider: const FluxDivider(
afterHeader: Divider(height: 1),
),
theme: FluxCardThemeData.outlined,
header: const Text('Concert Ticket'),
body: const Text('Gate opens at 7:00 PM'),
)
Important
The card outline is controlled by the card theme / shape.
FluxNotch controls notch geometry and placement only.
FluxCardSkeleton
Built-in loading state that mirrors the card structure.
FluxCard(
loading: true,
media: FluxMedia(aspectRatio: 16 / 9, child: SizedBox()),
header: const FluxSection(
title: Text('Title'),
padding: EdgeInsets.zero,
),
body: const Text('Body'),
)
When to use it
Use FluxCardSkeleton when you want:
- a package-native loading state
- no extra dependency
- a skeleton that respects the slot layout of the final card
If your app already uses a dedicated loading package, bridge it with loadingWrapper.
FluxCard(
loading: isLoading,
loadingWrapper: (context, skeleton) {
return Skeletonizer(
enabled: true,
child: skeleton,
);
},
header: const FluxSection(
title: Text('Title'),
padding: EdgeInsets.zero,
),
)
Theming
FluxCardThemeData controls the visual defaults for cards.
Built-in presets:
FluxCardThemeData.standardFluxCardThemeData.compactFluxCardThemeData.elevatedFluxCardThemeData.outlined
Per-card example:
final cardTheme = FluxCardThemeData.elevated.copyWith(
padding: const EdgeInsets.all(20),
spacing: 16,
);
App-wide example:
MaterialApp(
theme: ThemeData(
extensions: [
FluxCardThemeData.elevated,
],
),
)
Layout modes
Column
Best for:
- product cards
- article cards
- promo banners
- profile cards
FluxCard(
layout: FluxLayoutMode.column,
...
)
Row
Best for:
- compact horizontal cards
- list rows
- side-by-side media/content cards
FluxCard(
layout: FluxLayoutMode.row,
mediaPosition: FluxMediaPosition.start,
...
)
Responsive
Switches between column and row based on the card theme breakpoint.
FluxCard(
layout: FluxLayoutMode.responsive,
fullWidth: true,
...
)
Tip
responsive works best when the card has a known width or uses fullWidth: true.
Inline
Useful for denser inline arrangements where the card behaves more like a structured content row.
Examples
Product card
FluxCard(
media: FluxMedia(
aspectRatio: 4 / 3,
child: Ink.image(
image: NetworkImage(imageUrl),
fit: BoxFit.cover,
),
),
overlays: [
FluxOverlay(
targets: const {FluxTarget.media},
alignment: Alignment.topLeft,
children: [
Chip(label: Text('Sale')),
],
),
],
header: const FluxSection(
title: Text('Product name'),
subtitle: Text('\$29.99'),
padding: EdgeInsets.zero,
),
footer: FluxSection.footer(
padding: EdgeInsets.zero,
actions: [
FilledButton(
onPressed: null,
child: Text('Add to cart'),
),
],
),
theme: FluxCardThemeData.elevated,
onTap: () {},
)
Breakout promo card
FluxCard(
media: FluxMedia(
aspectRatio: 16 / 9,
child: Ink.image(
image: NetworkImage(imageUrl),
fit: BoxFit.cover,
),
),
overlays: [
FluxOverlay(
behavior: FluxOverlayBehavior.breakout,
targets: const {FluxTarget.media},
alignment: Alignment.bottomRight,
children: [
SizedBox(
width: 120,
height: 160,
child: Placeholder(),
),
],
),
],
header: const FluxSection(
title: Text('Breakout overlay'),
subtitle: Text('Promo style card'),
padding: EdgeInsets.zero,
),
theme: FluxCardThemeData.elevated,
)
Ticket card
FluxCard(
notch: const FluxNotch.ticket(
boundary: FluxSlotBoundary.afterHeader,
notchDepth: 14,
),
divider: const FluxDivider(
afterHeader: Divider(height: 1),
),
theme: FluxCardThemeData.outlined,
header: const FluxSection(
title: Text('Concert Night'),
subtitle: Text('Saturday, 8:00 PM'),
padding: EdgeInsets.zero,
),
body: const Text('Gate opens at 7:00 PM. Row A, Seat 12.'),
)
Accessibility
semanticLabellets you describe the card for assistive technologies- cards automatically expose button semantics when
onTaporonLongPressis provided foregroundColorhelps propagate a readable default foreground color through slot content
Widgetbook and examples
The package includes:
- a Widgetbook project for interactive exploration
- an example app
- use cases for cards, overlays, underlays, themes, notches, and scrollable layouts
Recommended screenshot sections for this README:
- hero / marketing card
- advanced cards gallery
- notch styles
- breakout overlay example
Version 0.1.0 notes
0.1.0 is the first public release and focuses on:
- core card composition primitives
- layered slot rendering
- theme-driven styling
- loading skeleton support
- notch-based card shapes
- Widgetbook examples and package tests
Roadmap ideas
Possible future directions:
- more advanced card presets built on top of
FluxCard - more notch styles
- richer Widgetbook showcases
- higher-level convenience factories for common card patterns
License
MIT