9

I am using flutter_markdown package to add markdown support to my app. There is an option for syntaxHighlighter. I found a syntax highlight library how can I use this to help to write my SyntaxHighlighter class

2 Answers 2

16

I was able to come up with a solution using flutter_highlighter, flutter_markdown and I used Google Fonts for the monospace font.

First you need to create an Element builder to manipulate the code block in the Markdown widget.

import 'package:flutter_highlighter/flutter_highlighter.dart';
import 'package:flutter_highlighter/themes/atom-one-dark.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:google_fonts/google_fonts.dart';

class CodeElementBuilder extends MarkdownElementBuilder {
@override
Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) {
var language = '';

if (element.attributes['class'] != null) {
  String lg = element.attributes['class'] as String;
  language = lg.substring(9);
}
return SizedBox(
  width:
      MediaQueryData.fromWindow(WidgetsBinding.instance!.window).size.width,
  child: HighlightView(
    // The original code to be highlighted
    element.textContent,

    // Specify language
    // It is recommended to give it a value for performance
    language: language,

    // Specify highlight theme
    // All available themes are listed in `themes` folder
    theme: MediaQueryData.fromWindow(WidgetsBinding.instance!.window)
                .platformBrightness ==
            Brightness.light
        ? atomOneLightTheme
        : atomOneDarkTheme,

    // Specify padding
    padding: const EdgeInsets.all(8),

    // Specify text style
    textStyle: GoogleFonts.robotoMono(),
     ),
   );
  }
}

Next you need to add the Element builder from above into the builders within the Markdown:

 Markdown(
  key: const Key("defaultmarkdownformatter"),
  data: codeText, 
  selectable: true,
  padding: const EdgeInsets.all(10),
  builders: {
    'code': CodeElementBuilder(),
    
  }),

And you should be good to go.

Example

Sign up to request clarification or add additional context in comments.

5 Comments

How do I manupulate the code block decoration? I want to wrap the entire block with a custom widget (animations).
This should be the accepted answer. Clean!
Awesome! Really clean
accepted answer, worked for me, nice and clean.
Thank you for your contribution, I am not sure why it doesn't work for me with macOS build. Do I use proper Markdown syntax, wrapping codeblock with three backticks? Do I need to add language to it, such as ``` java
5

I just wrote an issue :)

Please check Feature: Get Syntax Language when customizing properties syntaxHighlighter #355.

This is content of that issue.

Related Problem

Issue #227 StackOverflow - How to add code syntax highlighter to flutter markdown

Handle the Syntax Language Highlighting.

Possible solution

depends on highlight and of course flutter_markdown.

First, before pass the data to Markdown get a syntax language using RegExp.

Second, marking the language with a identical LANG_MARKER such as L@NG!.

this is an example code.

  String parseSyntaxLanguage(String data) {
    String parsed = data;
    RegExp codeSign = new RegExp(r'`{3} *');
    RegExp pattern = new RegExp(r'`{3} *[\w]+');
    for (RegExpMatch match in pattern.allMatches(this.data)) {
      String lang = match.group(0).split(codeSign)[1]; // get a syntax language
      parsed = parsed.replaceFirst(match.group(0), '```\n$LANG_MARKER$lang$LANG_MARKER');
    } // CAUTION "\n" newline is required!
    return parsed;
  }

Third, Make a syntax highlighter extends SyntaxHighlighter

this is an example code.

import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:highlight/highlight.dart' show highlight, Node;

class LangSyntaxHighlighter extends SyntaxHighlighter {
  final String language;
  final String _rootKey = 'root';
  final String _defaultLang = 'dart';
  final String _defaultFontFamily = 'monospace';
  final Color _defaultFontColor = Color(0xfffdfeff);
  final Map<String, TextStyle> theme; // User's code theme

  LangSyntaxHighlighter(this.theme);

  @override
  TextSpan format(String source) {
    String lang;
    if (source.startsWith(LANG_MARKER)) {
      int idx = source.indexOf(LANG_MARKER, LANG_MARKER.length);
      lang = source.substring(LANG_MARKER.length, idx);
      source = source.substring(idx + LANG_MARKER.length);
    }
    TextStyle _textStyle = TextStyle(
      fontFamily: this._defaultFontFamily,
      color: this.theme[this._rootKey].color ?? this._defaultFontColor,
    );
    return TextSpan(
      style: _textStyle,
      children: this._convert(highlight
          .parse(source, autoDetection: true, language: lang ?? this._defaultLang)
          .nodes),
    );
  }

  List<TextSpan> _convert(List<Node> nodes) {
    List<TextSpan> spans = [];
    var currentSpans = spans;
    List<List<TextSpan>> stack = [];

    _traverse(Node node) {
      if (node.value != null) {
        currentSpans.add(node.className == null
            ? TextSpan(text: node.value)
            : TextSpan(text: node.value, style: theme[node.className]));
      } else if (node.children != null) {
        List<TextSpan> tmp = [];
        currentSpans.add(TextSpan(children: tmp, style: theme[node.className]));
        stack.add(currentSpans);
        currentSpans = tmp;

        node.children.forEach((n) {
          _traverse(n);
          if (n == node.children.last)
            currentSpans = stack.isEmpty ? spans : stack.removeLast();
        });
      }
    }

    for (var node in nodes) _traverse(node);
    return spans;
  }
}

Last, combine the syntax highlighter and parser.

Widget build(BuildContext context) {
    Future<Widget> parseMarkdown = new Future(() {
      return MarkdownBody(
          data: parseSyntaxLanguage(this.data), // markdown source
          syntaxHighlighter: LangSyntaxHighlighter(),
          styleSheet: MarkdownStyleSheet(
    
         ...



I'm not sure if this is suitable for feature request. However, I hope this issue helps someone :)

1 Comment

Thanks for the solution but how can we use this with the existing textfield widget to parse and highlight the code in realtime like an IDE/ code editor.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.