Custom widgets guide

Learn how to create and use custom Flutter widgets in SuperDeck presentations

Custom widgets guide

Render custom Flutter widgets from Markdown. Register widgets in DeckOptions.widgets, then use them in slides.md.

Markdown syntax

Two equivalent forms:

  • Shorthand (recommended) - @twitter { ... }
  • Explicit - @widget { name: "twitter" ... }

All properties pass to WidgetDefinition.parse() as arguments.

Create a WidgetDefinition

Simple: map args (no validation)

import 'package:flutter/widgets.dart';
import 'package:superdeck/superdeck.dart';

class TwitterWidgetDefinition extends WidgetDefinition<Map<String, Object?>> {
  const TwitterWidgetDefinition();

  @override
  Map<String, Object?> parse(Map<String, Object?> args) => args;

  @override
  Widget build(BuildContext context, Map<String, Object?> args) {
    final username = args['username'] as String? ?? '';
    final tweetId = args['tweetId'] as String? ?? '';
    return Text('Twitter: @$username ($tweetId)');
  }
}

Recommended: typed args with validation

Validate and convert the raw map to a typed args object in parse(). Use Ack (from superdeck_core) for schema validation.

import 'package:flutter/widgets.dart';
import 'package:superdeck/superdeck.dart';

class TwitterArgs {
  const TwitterArgs({required this.username, required this.tweetId});

  final String username;
  final String tweetId;

  static final schema = Ack.object({
    'username': Ack.string().notEmpty(),
    'tweetId': Ack.string().notEmpty(),
  });

  static TwitterArgs parse(Map<String, Object?> map) {
    schema.parse(map);
    return TwitterArgs(
      username: map['username'] as String,
      tweetId: map['tweetId'] as String,
    );
  }
}

class TwitterDefinition extends WidgetDefinition<TwitterArgs> {
  const TwitterDefinition();

  @override
  TwitterArgs parse(Map<String, Object?> args) => TwitterArgs.parse(args);

  @override
  Widget build(BuildContext context, TwitterArgs args) {
    return Text('Twitter: @${args.username} (${args.tweetId})');
  }
}

Register widgets in DeckOptions

import 'package:flutter/widgets.dart';
import 'package:superdeck/superdeck.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SuperDeckApp.initialize();

  runApp(
    SuperDeckApp(
      options: DeckOptions(
        widgets: const {
          'twitter': TwitterWidgetDefinition(),
        },
      ),
    ),
  );
}

Use widgets in slides.md

Shorthand:

@twitter {
  username: "flutterdev"
  tweetId: "1234567890"
}

Explicit:

@widget {
  name: "twitter"
  username: "flutterdev"
  tweetId: "1234567890"
}

Access block and slide context

In WidgetDefinition.build():

  • BlockConfiguration.of(context) - Block size, alignment, slide spec
  • SlideConfiguration.of(context) - Slide index, options, metadata
@override
Widget build(BuildContext context, TwitterArgs args) {
  final block = BlockConfiguration.of(context);
  final slide = SlideConfiguration.of(context);

  return SizedBox(
    width: block.size.width,
    height: block.size.height,
    child: Text('Slide ${slide.slideIndex + 1}: @${args.username}'),
  );
}

Troubleshooting

  • "Widget not found" - Verify the name is registered in DeckOptions.widgets
  • Parse/build errors - SuperDeck renders error details with stack trace on slide

Notes

  • Built-in widgets (image, dartpad, qrcode) are always available
  • Override built-in widgets by registering the same name