home / skills / rodydavis / skills / snippets_flutter-markdown-view-material-3

snippets_flutter-markdown-view-material-3 skill

/skills/snippets_flutter-markdown-view-material-3

This skill helps you customize Flutter Markdown with Material 3 styles for consistent typography and interactive links across apps.

npx playbooks add skill rodydavis/skills --skill snippets_flutter-markdown-view-material-3

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.6 KB
---
name: flutter-markdown-view-with-material-3
description: Learn how to customize the Flutter Markdown widget using Material 3 text and color styles for a visually appealing and consistent design.
metadata:
  url: https://rodydavis.com/posts/snippets/flutter-markdown-view-material-3
  last_modified: Tue, 03 Feb 2026 20:04:30 GMT
---

# Flutter Markdown View with Material 3


## Overview 

How to style the [Flutter markdown](https://pub.dev/packages/flutter_markdown) widget with [Material 3](https://m3.material.io/) text and color styles:

```
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:go_router/go_router.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:url_launcher/url_launcher.dart';

class MarkdownView extends StatelessWidget {
  const MarkdownView({
    Key? key,
    required this.markdown,
    this.textScaleFactor = 1,
  }) : super(key: key);

  final String markdown;
  final double textScaleFactor;

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;
    return Markdown(
        data: markdown,
        selectable: true,
        softLineBreak: true,
        onTapLink: (text, link, _) {
          final url = link ?? '/';
          if (url.startsWith('http')) {
            launchUrl(Uri.parse(url));
          } else {
            context.push(url);
          }
        },
        extensionSet: md.ExtensionSet(
          md.ExtensionSet.gitHubFlavored.blockSyntaxes,
          [md.EmojiSyntax(), ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes],
        ),
        styleSheet: MarkdownStyleSheet(
            textScaleFactor: textScaleFactor,
            p: textTheme.bodyLarge!.copyWith(
              fontSize: 16,
              color: colors.onSurface.withOpacity(0.72),
            ),
            a: TextStyle(
              decoration: TextDecoration.underline,
              color: colors.onSurface.withOpacity(0.72),
            ),
            h1: textTheme.displaySmall!.copyWith(
              fontSize: 25,
              color: colors.onSurface,
            ),
            h2: textTheme.headlineLarge!.copyWith(
              fontSize: 20,
              color: colors.onSurface,
            ),
            h3: textTheme.headlineMedium!.copyWith(
              fontSize: 18,
              color: colors.onSurface,
            ),
            h4: textTheme.headlineSmall!.copyWith(
              fontSize: 16,
              color: colors.onSurface,
            ),
            h5: textTheme.titleLarge!.copyWith(
              fontSize: 16,
              color: colors.onSurface,
            ),
            h6: textTheme.titleMedium!.copyWith(
              fontSize: 16,
              color: colors.onSurface,
            ),
            listBullet: textTheme.bodyLarge!.copyWith(
              color: colors.onSurface,
            ),
            em: const TextStyle(fontStyle: FontStyle.italic),
            strong: const TextStyle(fontWeight: FontWeight.bold),
            blockquote: TextStyle(
              fontStyle: FontStyle.italic,
              fontWeight: FontWeight.w500,
              color: colors.onSurfaceVariant,
            ),
            blockquoteDecoration: BoxDecoration(
              color: colors.surfaceVariant,
              borderRadius: BorderRadius.circular(4),
            ),
            code: const TextStyle(fontFamily: 'monospace'),
            tableHead:
                const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
            tableBody:
                const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
            blockSpacing: 8,
            listIndent: 32,
            blockquotePadding: const EdgeInsets.all(8),
            h1Padding: const EdgeInsets.symmetric(vertical: 8),
            h2Padding: const EdgeInsets.symmetric(vertical: 8),
            h3Padding: const EdgeInsets.symmetric(vertical: 8),
            h4Padding: const EdgeInsets.symmetric(vertical: 8),
            h5Padding: const EdgeInsets.symmetric(vertical: 8),
            h6Padding: const EdgeInsets.symmetric(vertical: 8),
            codeblockPadding: const EdgeInsets.all(8),
            codeblockDecoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4),
              color: colors.surfaceVariant,
            ),
            horizontalRuleDecoration: BoxDecoration(
              border: Border(
                top: BorderSide(
                  color: colors.outline.withOpacity(0.4),
                  width: 1,
                ),
              ),
            )),
    );
  }
}
```

Overview

This skill shows how to style the Flutter Markdown widget using Material 3 text and color styles to achieve a consistent, modern look. It demonstrates mapping Material 3 TextTheme and ColorScheme values into MarkdownStyleSheet entries and adds sensible defaults for headings, lists, quotes, code blocks, and links. The result is a reusable Markdown view that respects theme changes and accessibility settings like text scale.

How this skill works

The widget builds a Markdown instance that uses Theme.of(context).textTheme and colorScheme to populate a MarkdownStyleSheet. It configures typography for paragraphs, headings, bullets, and tables, plus decorations for blockquotes and code blocks. Link taps are handled to open external URLs with url_launcher or navigate internally using go_router. GitHub-flavored markdown and emoji are enabled via markdown.ExtensionSet.

When to use it

  • Rendering user-authored or CMS Markdown inside a Material 3 app.
  • Displaying documentation, release notes, or help content with consistent app typography.
  • Embedding rich text that must support links, code blocks, tables, and emoji.
  • Needing theme-aware Markdown styling that adapts to light/dark modes and dynamic color schemes.

Best practices

  • Base all text styles on Theme.of(context).textTheme to stay consistent with Material 3 typography.
  • Use ColorScheme values (e.g., onSurface, surfaceVariant) for color choices to respect dynamic color and contrast.
  • Provide textScaleFactor and rely on accessible font scaling rather than hardcoding sizes when possible.
  • Enable GitHub-flavored markdown and common inline extensions (emoji, autolinks) for better compatibility.
  • Handle onTapLink to open external links safely and route internal links through the app router.

Example use cases

  • App help center: show step-by-step guides styled with the app’s Material 3 theme.
  • Release notes screen: render changelogs with headings, lists, and code blocks that match app typography.
  • User profiles or posts: safely display user-submitted markdown with link handling and emoji.
  • Documentation viewer: a lightweight in-app README or FAQ renderer that adapts to light/dark and dynamic color.

FAQ

How do I make links open in-app vs external browser?

In onTapLink, check if the link starts with http/https and use url_launcher for external URLs; otherwise route internally with your navigator/router.

Will this respect dark mode and dynamic color?

Yes. It sources colors from Theme.of(context).colorScheme so it updates automatically with dark mode or dynamic color changes.

How do I support syntax highlighting in code blocks?

The Markdown widget supports custom builders for code blocks. Wrap code blocks in a widget that applies a syntax highlighting package and use codeblockDecoration for background styling.