mb_rich_editor 1.2.1 copy "mb_rich_editor: ^1.2.1" to clipboard
mb_rich_editor: ^1.2.1 copied to clipboard

A highly customizable WebView-based rich text editor for Flutter with pre-built toolbar.

MB Rich Editor #

A highly customizable WebView-based rich text editor for Flutter with integrated toolbar, emoji picker, and mention support. Ported from the popular richeditor-android library.

Features #

Text Formatting #

  • Bold, Italic, , Strikethrough
  • Subscript and Superscript
  • Headings (H1-H6)
  • Font sizes (1-7 scale)

Block Elements #

  • Ordered and unordered lists
  • Blockquotes
  • Indentation (increase/decrease)

Text Alignment #

  • Left, Center, Right alignment

Colors & Styling #

  • Text color
  • Text background color (highlight)
  • Editor background color
  • Custom font size
  • Custom padding

Media Insertion #

  • Images (with width/height control)
  • Videos (with width/height control)
  • Audio files
  • YouTube embeds
  • Hyperlinks
  • Checkboxes (todo items)

Emoji Support #

  • Built-in emoji picker with customizable UI
  • Support for custom emoji sources (JSON, API, Unicode)
  • Emoji search and categories
  • Recent and frequently used emojis

Mention Support #

  • @-mention trigger with customizable suggestions
  • Multiple mention providers (static, API, Firestore)
  • Custom mention formats (text, link, custom HTML)
  • Avatar and role display in suggestions

Built-in Toolbar #

  • Pre-built customizable toolbar with 20+ buttons
  • Multiple preset configurations (minimal, basic, default, full)
  • Custom button styling and layouts
  • Active state tracking for formatting buttons

Editor Control #

  • Undo/Redo
  • Focus/blur control
  • Enable/disable editing
  • Clear formatting

Real-time Callbacks #

  • Text change listener (returns HTML)
  • Decoration state listener (tracks active formatting)
  • Ready state listener
  • Emoji selection callback
  • Mention trigger/hide/select callbacks

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  mb_rich_editor: ^0.0.1

Then run:

flutter pub get

Usage #

Basic Example #

import 'package:mb_rich_editor/mb_rich_editor.dart';

class MyEditor extends StatefulWidget {
  @override
  State<MyEditor> createState() => _MyEditorState();
}

class _MyEditorState extends State<MyEditor> {
  final controller = RichEditorController();

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        RichEditor(
          controller: controller,
          height: 300,
          placeholder: 'Start typing...',
          onTextChange: (html) {
            print('HTML changed: $html');
          },
          onDecorationChange: (states) {
            print('Active states: $states');
          },
        ),

        // Control buttons
        Row(
          children: [
            IconButton(
              icon: Icon(Icons.format_bold),
              onPressed: () => controller.setBold(),
            ),
            IconButton(
              icon: Icon(Icons.format_italic),
              onPressed: () => controller.setItalic(),
            ),
            IconButton(
              icon: Icon(Icons.undo),
              onPressed: () => controller.undo(),
            ),
          ],
        ),
      ],
    );
  }
}

Using the Built-in Toolbar #

The package includes a highly customizable toolbar widget:

// Basic toolbar with default options
MBRichEditorToolbar(
  controller: controller,
)

// Customized toolbar
MBRichEditorToolbar(
  controller: controller,
  options: MBToolbarOptions(
    buttons: [
      ToolbarButtonDefinition.bold,
      ToolbarButtonDefinition.italic,
      ToolbarButtonDefinition.underline,
      ToolbarButtonDefinition.bullets,
      ToolbarButtonDefinition.numbers,
      ToolbarButtonDefinition.emojiPicker,
    ],
    horizontal: true,
    decoration: BoxDecoration(
      color: Colors.grey[100],
      border: Border(bottom: BorderSide(color: Colors.grey[300]!)),
    ),
    iconSize: 24,
    showLabels: false,
  ),
  onEmojiPicker: () {
    // Show emoji picker
  },
)

Toolbar Presets

// Minimal - only bold, italic, underline
MBRichEditorToolbar(
  controller: controller,
  options: MBToolbarOptions.minimal,
)

// Basic - text formatting
MBRichEditorToolbar(
  controller: controller,
  options: MBToolbarOptions.basic,
)

// Default - text + blocks + alignment + controls
MBRichEditorToolbar(
  controller: controller,
  options: MBToolbarOptions.defaultOptions,
)

// Full - all available buttons
MBRichEditorToolbar(
  controller: controller,
  options: MBToolbarOptions.full,
)

Available Toolbar Buttons

// Text Formatting
ToolbarButtonDefinition.bold
ToolbarButtonDefinition.italic
ToolbarButtonDefinition.underline
ToolbarButtonDefinition.strikeThrough
ToolbarButtonDefinition.subscript
ToolbarButtonDefinition.superscript

// Headings
ToolbarButtonDefinition.heading1
ToolbarButtonDefinition.heading2
ToolbarButtonDefinition.heading3
ToolbarButtonDefinition.heading4
ToolbarButtonDefinition.heading5
ToolbarButtonDefinition.heading6

// Blocks
ToolbarButtonDefinition.blockquote
ToolbarButtonDefinition.bullets
ToolbarButtonDefinition.numbers

// Alignment
ToolbarButtonDefinition.alignLeft
ToolbarButtonDefinition.alignCenter
ToolbarButtonDefinition.alignRight

// Indentation
ToolbarButtonDefinition.indent
ToolbarButtonDefinition.outdent

// Editor Control
ToolbarButtonDefinition.undo
ToolbarButtonDefinition.redo
ToolbarButtonDefinition.clearFormat

// Special
ToolbarButtonDefinition.emojiPicker
ToolbarButtonDefinition.mention

Emoji Picker #

The built-in emoji picker with customizable configuration:

// Create emoji source
final emojiSource = JsonEmojiSource(jsonPath: 'assets/emoji.json');

// Use emoji picker
EmojiPicker(
  emojiSource: emojiSource,
  config: EmojiPickerConfig(
    columns: 8,
    rows: 6,
    showSearch: true,
    showCategories: true,
  ),
  style: EmojiPickerStyle(
    backgroundColor: Colors.white,
    emojiSize: 32,
  ),
  onEmojiSelected: (emoji) {
    controller.insertEmoji(emoji);
  },
)

Emoji Picker Presets

// Default
EmojiPickerConfig.defaultConfig

// Compact for mobile
EmojiPickerConfig.compact

// Full-featured
EmojiPickerConfig.full

// Minimal
EmojiPickerConfig.minimal

Creating Custom Emoji Source

class CustomEmojiSource extends EmojiSource {
  @override
  Future<List<EmojiCategory>> loadCategories() async {
    // Load your emoji data
    return categories;
  }

  @override
  EmojiSourceMetadata get metadata {
    return EmojiSourceMetadata(
      name: 'My Custom Emojis',
      version: '1.0.0',
      isUnicode: false,
      totalEmojis: 100,
      totalCategories: 5,
    );
  }
}

Mention Support #

Add @-mention functionality with custom providers:

// Create mention provider
final mentionProvider = StaticMentionProvider(
  users: [
    MentionUser(
      id: '1',
      username: 'john_doe',
      displayName: 'John Doe',
      avatarUrl: 'https://example.com/avatar1.jpg',
      role: 'Admin',
    ),
    MentionUser(
      id: '2',
      username: 'jane_smith',
      displayName: 'Jane Smith',
      avatarUrl: 'https://example.com/avatar2.jpg',
    ),
  ],
);

// Set up mention callbacks
controller.onMentionTrigger = (text) {
  // Show mention suggestions when @ is detected
  showModalBottomSheet(
    context: context,
    builder: (context) => MentionSuggestions(
      mentionProvider: mentionProvider,
      query: text,
      onUserSelected: (user) {
        controller.insertMention(Mention.text(user: user));
        Navigator.pop(context);
      },
    ),
  );
};

controller.onMentionHide = () {
  // Hide mention suggestions
  Navigator.pop(context);
};

Mention Configuration

MentionConfig(
  trigger: '@',
  minLength: 1,
  maxResults: 10,
  showAvatars: true,
  showRoles: true,
  highlightMatches: true,
  searchDebounce: Duration(milliseconds: 300),
)

// Presets
MentionConfig.defaultConfig
MentionConfig.minimal
MentionConfig.full

Mention Formats

// Plain text: @username
Mention.text(user: user)

// HTML link: <a href="/users/1">@john_doe</a>
Mention.link(user: user, baseUrl: '/users')

// Custom HTML
Mention.customHtml(
  user: user,
  htmlTemplate: '<span class="mention" data-id="{userId}">{displayName}</span>',
)

Creating Custom Mention Provider

class ApiMentionProvider extends MentionProvider {
  final String apiUrl;
  final String apiKey;

  ApiMentionProvider({required this.apiUrl, required this.apiKey});

  @override
  Future<List<MentionUser>> searchUsers(String query) async {
    final response = await http.get(
      Uri.parse('$apiUrl/users?q=$query'),
      headers: {'Authorization': 'Bearer $apiKey'},
    );

    final data = jsonDecode(response.body);
    return (data['users'] as List)
        .map((json) => MentionUser.fromJson(json))
        .toList();
  }

  @override
  MentionProviderMetadata get metadata {
    return MentionProviderMetadata(
      name: 'API Users',
      trigger: '@',
      maxResults: 10,
    );
  }
}

Getting HTML Content #

// Get current HTML
String html = await controller.getHtml();

// Set HTML content
await controller.setHtml('<p>Hello <b>World</b></p>');

Formatting Commands #

// Text formatting
await controller.setBold();
await controller.setItalic();
await controller.setUnderline();
await controller.setStrikeThrough();

// Headings
await controller.setHeading(1); // H1-H6

// Lists
await controller.setBullets();    // Unordered list
await controller.setNumbers();    // Ordered list

// Alignment
await controller.setAlignLeft();
await controller.setAlignCenter();
await controller.setAlignRight();

// Colors
await controller.setTextColor('#FF0000');
await controller.setTextBackgroundColor('#FFFF00');

Inserting Media #

// Insert image
await controller.insertImage(
  'https://example.com/image.jpg',
  alt: 'Description',
  width: 200,
  height: 150,
);

// Insert video
await controller.insertVideo(
  'https://example.com/video.mp4',
  width: 320,
);

// Insert YouTube video
await controller.insertYoutubeVideo(
  'https://www.youtube.com/embed/VIDEO_ID',
  width: 560,
  height: 315,
);

// Insert link
await controller.insertLink(
  'https://example.com',
  'Link Text',
);

// Insert checkbox
await controller.insertTodo();

Customizing Editor Appearance #

RichEditor(
  controller: controller,
  height: 400,
  width: double.infinity,
  placeholder: 'Write something amazing...',
  backgroundColor: Colors.white,
  textColor: Colors.black,
  fontSize: 16,
  padding: EdgeInsets.all(16),
  enabled: true,
  autoFocus: false,
)

Tracking Active Formatting #

The onDecorationChange callback returns a list of currently active formatting states:

RichEditor(
  controller: controller,
  onDecorationChange: (states) {
    setState(() {
      _activeStates = states;
      // states contains: ['bold', 'italic', 'justifyLeft', etc.]
    });
  },
)

Custom Summernote Options #

The editor supports injecting custom Summernote options at initialization:

RichEditor(
  controller: controller,
  customSummernoteOptions: {
    'height': 400,
    'minHeight': 200,
    'maxHeight': 600,
    'placeholder': 'Write your story...',
    'fontSizes': ['8', '9', '10', '11', '12', '14', '18', '24', '36'],
    'dialogsInBody': true,
  },
)

Available options include:

  • height, minHeight, maxHeight - Editor height constraints
  • toolbar - Custom toolbar configuration
  • placeholder - Placeholder text
  • fontSizes - Array of font size options
  • dialogsInBody - Render dialogs in body instead of editor
  • And any other Summernote option

Custom Summernote Callbacks #

Receive type-safe callbacks from JavaScript events directly in Dart:

RichEditor(
  controller: controller,
  summernoteCallbacks: SummernoteCallbacks(
    onInit: () {
      print('Editor is ready!');
    },
    onChange: (contents) {
      print('Content changed: ${contents.length} chars');
    },
    onFocus: () {
      print('Editor focused');
    },
    onBlur: () {
      print('Editor blurred');
    },
    onPaste: (event) {
      print('Pasted!');
    },
    onKeydown: (event) {
      if (event['key'] == 'Enter') {
        print('Enter pressed - custom handling');
      }
    },
    onStateChange: (state) {
      // Type-safe toolbar state object
      print('Bold: ${state.bold}');
      print('Italic: ${state.italic}');
      print('Heading: H${state.headingLevel}');
      print('Has list: ${state.hasList}');

      // Update custom toolbar UI
      setState(() {
        isBold = state.bold;
        isItalic = state.italic;
      });
    },
  ),
)

Available callbacks:

  • onInit - Editor initialized
  • onChange - Content changed
  • onFocus - Editor gained focus
  • onBlur - Editor lost focus
  • onKeydown - Key pressed
  • onKeyup - Key released
  • onPaste - Content pasted
  • onImageUpload - Images uploaded
  • onEnter - Enter key pressed
  • onStateChange - Toolbar formatting state changed

SummernoteToolbarState properties:

  • Boolean flags: bold, italic, underline, strikeThrough, subscript, superscript, orderedList, unorderedList, justifyLeft, justifyCenter, justifyRight, justifyFull
  • formatBlock - Current block format (e.g., 'h1', 'p', 'blockquote', 'pre')
  • Helper methods: hasAnyFormatting, hasList, hasAlignment, isHeading, headingLevel, isBlockquote, isCodeBlock, isParagraph

Platform Support #

  • Android
  • iOS
  • Web

Architecture #

This package uses a hybrid architecture:

  • Core editing: WebView with HTML contenteditable
  • JavaScript bridge: Bidirectional communication between Dart and JavaScript
  • Flutter UI: Native Flutter widgets for toolbar, emoji picker, and mentions

This approach provides:

  • Feature parity with web-based rich editors
  • Consistent behavior across platforms
  • Familiar document.execCommand API for formatting
  • Highly customizable Flutter UI layer

Customization #

The library is designed for extensive customization:

Component Customization Options
Toolbar Button selection, layout, styling, presets
Emoji Picker Config, Style, custom sources, custom builders
Mentions Config, Style, custom providers, formats
Editor Colors, fonts, padding, placeholder

Notes #

  • This package uses document.execCommand for formatting, which is deprecated but still widely supported
  • The editor is based on the successful richeditor-android library
  • WebView performance may vary across devices
  • Consider debouncing text change callbacks for better performance

License #

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Acknowledgments #

0
likes
160
points
462
downloads

Publisher

unverified uploader

Weekly Downloads

A highly customizable WebView-based rich text editor for Flutter with pre-built toolbar.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_inappwebview

More

Packages that depend on mb_rich_editor