Creating Slides in Flutter Deck
Overview
The flutter_deck framework provides several built-in FlutterDeckSlide factories — title, image, split, quote, bigFact, blank, template, custom — plus full support for hand-rolled slides. As an architectural best practice, each slide should live in its own Dart file so the deck scales as the presentation grows.
There are three idiomatic ways to define a slide. Pick the one that fits the slide's complexity:
- Subclass
FlutterDeckSlideWidget— the canonical pattern when a slide owns its own widget tree, configuration, and helpers. Most slides use this. - Use a
FlutterDeckSlide.<factory>constructor directly with aconfiguration:argument — works inline insideFlutterDeckApp.slidesfor terminal slides like a thank-you screen, no class definition needed. - Wrap any existing widget with the
Widget.withSlideConfiguration(...)extension — useful when an existing widget (a counter demo, a stateful widget tree) just needs slide chrome attached.
When to Use This Skill
- When you are adding a new concept, topic, or section to a presentation and need a new slide.
- When you are refactoring raw Flutter widgets into structured presentation slides.
- When you need to choose the right layout (e.g., deciding between a
QuoteSlide, anImageSlide, or aSplitSlide). - When a slide has multi-step animations or bullet points appearing sequentially (using the
stepsproperty).
Implementation Checklist
- Determine the content focus of the slide (title, image, quote, etc.).
- Create a new, separate Dart file for the slide.
- Choose the appropriate
FlutterDeckSlidefactory constructor (e.g.,FlutterDeckSlide.title,FlutterDeckSlide.template). - Define the
FlutterDeckSlideConfigurationfor the slide, ensuring it has a uniqueroute. - Set
stepsin the configuration if you want the slide to require multiple clicks before moving to the next slide. - Add any images used in the slide to
preloadImagesin the configuration for smoother transitions. - Implement the UI utilizing the builder provided by the chosen template.
- Add the fully implemented slide to the
slideslist inFlutterDeckApp.
Standard Slide Templates
You can create slides using the following factory constructors on FlutterDeckSlide:
Template Slide: The base custom layout allowing precise control over header, footer, background, and content placement.
FlutterDeckSlide.template( backgroundBuilder: (context) => FlutterDeckBackground.solid( Theme.of(context).colorScheme.background, ), contentBuilder: (context) => const ColoredBox( color: Colors.red, child: Text('Content goes here...'), ), footerBuilder: (context) => ColoredBox( color: Theme.of(context).colorScheme.secondary, child: const Text('Footer goes here...'), ), headerBuilder: (context) => ColoredBox( color: Theme.of(context).colorScheme.primary, child: const Text('Header goes here...'), ), )Title Slide: A slide with a title and an optional subtitle.
FlutterDeckSlide.title( title: 'My Presentation', subtitle: 'By John Doe', )Image Slide: A slide that displays an image.
FlutterDeckSlide.image( imageBuilder: (context) => Image.asset('assets/my_image.png'), label: 'Architecture Diagram', )Split Slide: A slide split into two columns (usually text on left, widget/image on right).
FlutterDeckSlide.split( leftBuilder: (context) => const Text('Left Side'), rightBuilder: (context) => const Text('Right Side'), )Quote Slide: A slide with a large quote and optional attribution.
FlutterDeckSlide.quote( quote: '"Flutter is awesome"', attribution: '- Everyone', )Big Fact Slide: A slide featuring a large bold text fact and a subtitle.
FlutterDeckSlide.bigFact( title: '100%', subtitle: 'Of developers love this framework', )Blank Slide: A completely empty slide where you provide the full widget tree.
FlutterDeckSlide.blank( builder: (context) => const Center(child: Text('Custom Content')), )Custom Slide: For fully customized slides building on top of
FlutterDeckSlideBaseor returning your ownFlutterDeckSlide.custom.FlutterDeckSlide.custom( builder: (context) => const CustomSlideWidget(), )
Three Patterns for Defining a Slide
Pattern 1: Subclass FlutterDeckSlideWidget (canonical)
class MySlide extends FlutterDeckSlideWidget {
const MySlide()
: super(
configuration: const FlutterDeckSlideConfiguration(route: '/my'),
);
@override
FlutterDeckSlide build(BuildContext context) =>
FlutterDeckSlide.blank(builder: (_) => const Center(child: Text('Hi')));
}
Pattern 2: Use a factory constructor directly
Every FlutterDeckSlide.<factory> accepts a configuration: argument, so a one-off slide can live inline in FlutterDeckApp.slides:
slides: [
FlutterDeckSlide.title(
configuration: const FlutterDeckSlideConfiguration(
route: '/end',
title: 'Thank you!',
footer: FlutterDeckFooterConfiguration(showFooter: false),
),
title: 'Thank you! 👋',
subtitle: 'Questions?',
),
],
Pattern 3: withSlideConfiguration extension on any Widget
An existing widget (e.g. a stateful demo) can become a slide without subclassing — call .withSlideConfiguration(...) on it. The extension wraps it via FlutterDeckSlide.custom.
slides: [
const CounterDemo().withSlideConfiguration(
const FlutterDeckSlideConfiguration(
route: '/counter',
title: 'Live counter demo',
),
),
],
FlutterDeckApp.slides is typed List<Widget>, so all three patterns can be mixed in the same list.
Sequential Reveals: steps
When a slide should consume multiple "next" presses before advancing — bullets appearing one at a time, an animated diagram building up — set steps: N on the configuration and read the current step at build time.
The framework provides two helper widgets:
FlutterDeckSlideStepsBuilder({required builder})— rebuilds whenever the step changes. The builder receives(BuildContext context, int stepNumber).FlutterDeckSlideStepsListener({required listener, required child})— fire-and-forget callback when the step changes (for animations, side effects); does not rebuild its child.
@override
FlutterDeckSlide build(BuildContext context) {
return FlutterDeckSlide.blank(
builder: (context) => FlutterDeckSlideStepsBuilder(
builder: (context, step) => Column(
children: [
const Text('Point one'),
if (step >= 2) const Text('Point two'),
if (step >= 3) const Text('Point three'),
],
),
),
);
}
stepNumber starts at 1. Combine with steps: 3 in the configuration so the slide consumes all three taps before advancing.
Configuring a Slide
Each slide must have a configuration.
const _speakerNotes = '''
Don't forget to talk about this slide.
Keep multiple points organized here for the presenter view.
''';
class MySlide extends FlutterDeckSlideWidget {
const MySlide()
: super(
configuration: const FlutterDeckSlideConfiguration(
route: '/my-slide',
title: 'My Slide Title',
speakerNotes: _speakerNotes,
hidden: false,
initial: false,
steps: 2,
preloadImages: {'assets/my-image.png'}
),
);
@override
FlutterDeckSlide build(BuildContext context) {
return FlutterDeckSlide.image(
imageBuilder: (context) => Image.asset('assets/my-image.png')
);
}
}
Gotchas
- A slide is only rendered if it's added to
FlutterDeckApp.slides. Defining aFlutterDeckSlideWidgetsubclass alone does nothing. - Every slide must have a unique
routestarting with/. Duplicate routes cause a startup failure. stepsdefaults to1(one tap advances). Setsteps: Nfor sequential reveals; the slide consumesNnext-key presses before moving on.preloadImagesaccepts both asset paths and remote URLs. Use it for every image that would otherwise visibly pop in mid-presentation.FlutterDeckSlide.splithas its own theme channel (FlutterDeckSplitSlideThemeData) — color it via the slide'stheme:parameter, not the global theme, when only that slide needs different split colors.- The "one slide per file" rule is a project convention enforced by maintainability, not the framework. Skipping it works mechanically but breaks barrel imports and code review fast.
FlutterDeckSlide.templateis the right escape hatch when the built-in factories don't fit — it lets you supplyheaderBuilder,footerBuilder,backgroundBuilder, andcontentBuilderindependently. Reach for.blankonly when you also want to skip the standard chrome.
Source: mkobuolys/flutter_deck — distributed by TomeVault.