Creating and Using Plugins in Flutter Deck
Overview
Plugins in flutter_deck provide an elegant way to globally inject side-effects, state management, or custom overlay UI components into a presentation. By extending FlutterDeckPlugin, you can hook into the presentation lifecycle. Plugins typically perform operations such as wrapping the entire slide deck inside an InheritedWidget / Provider so that all slides can access the state, handling setup and teardown logic, or extending the presenter's control menu with custom interactive buttons.
Plugins are designed to be reusable components that can be shared across multiple slide decks (e.g., by publishing to pub.dev).
When to Use This Skill
- When you want to add custom control actions or tools available in the presenter's menu (e.g., an Autoplay toggle, a custom laser pointer, a "Clear Screen" tool).
- When you need to listen to global presentation lifecycle events or initialize services distinct to the
flutter_deckruntime. - When you need to share complex state (e.g., fetching network data, websockets, dynamic theme logic) across multiple slides.
Implementation Checklist
- Create a new class extending
FlutterDeckPlugin. - Determine if initialization or cleanup is needed. If so, override
init()anddispose(). - If you want the plugin to be controllable by the presenter, override
buildControls()usingFlutterDeckPluginMenuItemBuilder. - If the plugin provides data or state to the slides, override
wrap()to wrap thechildin anInheritedWidgetor specializedProvider. - Instantiate and add the new plugin to the
pluginslist array inFlutterDeckApp.
Registering Plugins
Plugins are passed as a list to the FlutterDeckApp.
class MyDeck extends StatelessWidget {
const MyDeck({super.key});
@override
Widget build(BuildContext context) {
return FlutterDeckApp(
configuration: const FlutterDeckConfiguration(
// ...
),
plugins: [
MyPlugin(),
],
slides: [
// ...
],
);
}
}
Creating a Custom Plugin
To create a plugin, extend the FlutterDeckPlugin class. This class has four methods: init, dispose, buildControls, and wrap.
import 'package:flutter/material.dart';
import 'package:flutter_deck/flutter_deck.dart';
class MyPlugin extends FlutterDeckPlugin {
const MyPlugin();
@override
void init(FlutterDeck flutterDeck) {
print('MyPlugin initialized');
}
@override
void dispose() {
print('MyPlugin disposed');
}
@override
List<Widget> buildControls(BuildContext context, FlutterDeckPluginMenuItemBuilder menuItemBuilder) {
return [
menuItemBuilder(
context,
label: 'Show message',
icon: const Icon(Icons.message),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Hello from MyPlugin!'),
),
);
},
),
];
}
@override
Widget wrap(BuildContext context, Widget child) {
return SomeProvider(
child: child,
);
}
}
Accessing Plugins and Deck State
You can access the global FlutterDeck instance through the init parameter, or anywhere in the widget tree via context.flutterDeck. The instance exposes a stable read/listen API:
Navigation & current state
goToSlide(int slideNumber)/goToStep(int stepNumber)— programmatic navigation.slideNumber/stepNumber— getters for the current position.configuration— the current slide'sFlutterDeckSlideConfiguration(already merged with global).globalConfiguration— the originalFlutterDeckConfigurationfromFlutterDeckApp.speakerInfo— the optionalFlutterDeckSpeakerInfo.
Reactive notifiers (subscribe with addListener, read .value)
controlsNotifier— toolbar / shortcut state.drawerNotifier— navigation drawer open/closed.localizationNotifier— current locale.markerNotifier— marker tool active state and strokes.themeNotifier— currentThemeModeandFlutterDeckThemeData.presenterController— presenter view connection state.
Plugin discovery
maybeGetPlugin<T extends FlutterDeckPlugin>()— find another registered plugin by type, e.g.flutterDeck.maybeGetPlugin<FlutterDeckAutoplayPlugin>(). Returnsnullif absent.
If your plugin provides state via the wrap method using an InheritedWidget, slides can use standard Flutter conventions (e.g., SomeProvider.of(context)) to access their data.
Built-in and First-party Plugins
flutter_deck ships one built-in plugin and several first-party packages in this monorepo. Reach for these before writing your own:
FlutterDeckAutoplayPlugin(in core) — adds an autoplay submenu to the presenter controls. Canonical reference for a real plugin: shows lifecycle, nested submenu viamenuItemBuilder, and notifier subscription.flutter_deck_web_client—FlutterDeckWebClient(), required for the presenter view on web. Pass it toFlutterDeckApp.client.flutter_deck_pdf_export—FlutterDeckPdfExportPlugin(), exports the deck to PDF.flutter_deck_pptx_export—FlutterDeckPptxExportPlugin(), exports the deck to PPTX.
Register them on FlutterDeckApp.plugins in the order you want their wrap() calls to compose (earlier plugins end up outermost):
FlutterDeckApp(
plugins: [
FlutterDeckAutoplayPlugin(),
FlutterDeckPdfExportPlugin(),
FlutterDeckPptxExportPlugin(),
],
// ...
);
Gotchas
FlutterDeckPluginisabstract. You must extend it —FlutterDeckPlugin()cannot be instantiated directly.- All four hooks (
init,dispose,buildControls,wrap) have safe no-op defaults. Override only what your plugin actually needs. init(FlutterDeck flutterDeck)runs once when the deck is created anddispose()runs once when it's torn down. Do not place per-slide logic in either — react to slide changes viaflutterDecklisteners instead.- When multiple plugins each override
wrap(), they compose inFlutterDeckApp.pluginslist order. The first plugin in the list ends up outermost in the widget tree — order matters when wrappers depend on each other. - Inside slides, prefer
context.flutterDeckover capturing theFlutterDeckreference frominit— it's the canonical way and survives hot reload. buildControlsitems are added to the existing presenter menu — return menu items viamenuItemBuilderto match the built-in look. Returning raw widgets bypasses styling.
Source: mkobuolys/flutter_deck — distributed by TomeVault.