Master Markdown and Multi-line Selection in Flutter: A Step-by-Step Tutorial

[

goodspeed

](https://medium.com/@amazing_gs?source=post_page---byline--0660dc34ec27---------------------------------------)

Recently, I have been developing Moli AI and it requires support for markdown rendering and multi-line copying. This article primarily covers the following topics:

How to use markdown in Flutter.

How to customize markdown styles.

How to enable multi-line selection in markdown.

flutter_markdown

flutter_markdown is a markdown renderer provided by the official Flutter community. It supports creating rich text output from simple Markdown tagged formatted plain text data, including text styles, tables, links, and more.

Overview:

  • The flutter_markdown package allows you to render Markdown text into rich text in Flutter.
  • It is built on the Dart markdown package and parses Markdown into an abstract syntax tree (AST).
  • Markdown allows injecting HTML into the source text, but the flutter_markdown package does not support inline HTML.
  • By default, the flutter_markdown package uses the GitHub Flavored Markdown specification.

How to use:

  1. Add the flutter_markdown: ^0.6.18+3 dependency in your pubspec.yaml file.

dependencies:
flutter:
sdk: flutter
flutter_markdown: ^0.6.18+3

Then add the following code:

import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';

void main() {

runApp(const MyApp());}

class MyApp extends StatelessWidget {

const MyApp({super.key}); Widget build(BuildContext context) {

return MaterialApp(

title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),

useMaterial3: true,

),

home: const MyHomePage(title: 'Flutter Markdown Page'),

); }}

class MyHomePage extends StatefulWidget {

const MyHomePage({super.key, required this.title});

final String title;

State<MyHomePage> createState() => _MyHomePageState();}

class _MyHomePageState extends State<MyHomePage> {

Widget build(BuildContext context) {

var markdown =

"""Here's a simple Go program that prints "Hello, world!" to the console😀:```gopackage mainimport "fmt"func main() { fmt.Println("Hello, world!")}```Save the above code in a file with a `.go` extension, for example `hello.go`. Then, you can run the program by executing the following command in your terminal:```shellgo run hello.go```The output will be:```Hello, world!```

"""

;

return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Markdown(

selectable: true,

data: markdown, ), )); }

}

The resulting output can be seen in the image below. Markdown is rendered correctly, but the code does not have any special styles and multi-line selection is not supported.

Now, let’s see how to customize code styles.

How to customize styles:

Use the styleSheet property to customize the default styles of Markdown. You can use the TextStyle object to specify font, color, size, and other properties.

Here is the modified code where the color of the code block has been changed. It sets the font size of h1 headings to 24 pixels, the font size of code to 14 pixels, and the color to green.

body: Center( child: Markdown(

selectable: true,

data: markdown, styleSheet: MarkdownStyleSheet(

h1: const TextStyle(fontSize: 24, color: Colors.blue),

code: const TextStyle(fontSize: 14, color: Colors.green), ), ),

));

Let’s further optimize the styles:

body: Center( child: Markdown(

selectable: true,

data: markdown, styleSheet: MarkdownStyleSheet(

h1: const TextStyle(fontSize: 24, color: Colors.blue),

code: const TextStyle(
fontSize: 14, color: Colors.white, backgroundColor: Colors.grey),

codeblockPadding: const EdgeInsets.all(8),

codeblockDecoration: BoxDecoration(

borderRadius: BorderRadius.circular(4),

color: Colors.grey, ), ), ),

)

Here, I have used codeblockPadding and codeblockDecoration.

  • codeblockPadding controls the padding of the code block. You can use the EdgeInsets object to specify horizontal, vertical, left, right, top, and bottom padding properties.

MarkdownStyleSheet(
codeblockPadding: EdgeInsets.all(8),
)

This sets the padding of the code block to 8 pixels.

  • codeblockDecoration controls the decoration of the code block. You can use the BoxDecoration object to specify background color, borders, corner radius, and other properties.

MarkdownStyleSheet( codeblockDecoration: BoxDecoration(

borderRadius: BorderRadius.circular(4),

color: Colors.grey, ),

)

This sets the background color of the code block to gray and adds rounded borders.

However, multi-line copying is still not supported. This may be because flutter_markdown uses many separate widgets to render Markdown, which may not be compatible with each other.

How to enable multi-line selection:

To solve the issue of multi-line selection, we can use the flutter_markdown_selectionarea package.

To use it just replace flutter_markdown dependency in your pubspec.yaml with

flutter_markdown_selectionarea: ^0.6.17+1

and then change all imports from

import 'package:flutter_markdown/flutter_markdown.dart';

to

import 'package:flutter_markdown_selectionarea/flutter_markdown.dart';

and then you will be able to make multiple paragraphs selectable by using SelectionArea widget:

SelectionArea(
child: MarkdownBody(data: text),

Here is the modified code:

body: Center( child: SelectionArea( child: Markdown( data: markdown, styleSheet: MarkdownStyleSheet(

h1: const TextStyle(fontSize: 24, color: Colors.blue),

code: const TextStyle(
fontSize: 14, color: Colors.white, backgroundColor: Colors.grey),

codeblockPadding: const EdgeInsets.all(8),

codeblockDecoration: BoxDecoration(

borderRadius: BorderRadius.circular(4),

color: Colors.grey, ), ), ), ),

));

Now, multi-line selection is supported.

One More Thing

Although multi-line selection is supported, you may notice that the color of the selected code block does not change, which can be confusing and make it uncertain whether it is actually selected.

To address this, I used Flutter Markdown’s MarkdownElementBuilder to customize the rendering of Markdown.

To use MarkdownElementBuilder, you need to create a subclass of MarkdownElementBuilder and implement the visitElementAfter() method. In the visitElementAfter() method, you can generate custom Flutter components based on the type of Markdown element.

Here is a simple example:

class CodeElementBuilder extends MarkdownElementBuilder {

bool isCodeBlock(md.Element element) {

if (element.attributes['class'] != null) {
return true;
} else if (element.textContent.contains("\n")) {
return true; }

return false;

}

Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) {

if (!isCodeBlock(element)) {
return Container(
padding: const EdgeInsets.all(2), child: Text( element.textContent, style: TextStyle(

fontSize: 16,

fontStyle: FontStyle.italic, color: preferredStyle!.color), ), );

} else {

return Container(
margin: const EdgeInsets.all(10), child: Text( element.textContent,

style: const TextStyle(fontSize: 16, color: Colors.white),

), ); } }

}

body: Center( child: SelectionArea( child: Markdown( data: markdown, builders: {

'code': CodeElementBuilder(),

}, styleSheet: MarkdownStyleSheet(

h1: const TextStyle(fontSize: 24, color: Colors.blue),

code: const TextStyle(
fontSize: 14, color: Colors.pink, backgroundColor: Colors.grey),

codeblockPadding: const EdgeInsets.all(8),

codeblockDecoration: BoxDecoration(

borderRadius: BorderRadius.circular(4),

color: Colors.grey, ), ), ), ),

)

From the image below, you can clearly see which content is selected.

🎉🎉🎉

Remaining issues:

  1. How to highlight code blocks.
  2. How to select code blocks after highlighting.

Relevant links :

inicio - Wiki
Copyright © 2011-2025 iteam. Current version is 2.144.0. UTC+08:00, 2025-07-05 07:31
浙ICP备14020137号-1 $mapa de visitantes$