FORMATUS

A plain Flutter Rich-Text-Editor without any dependencies and a HTML display

Features

  • Runs on all platforms
  • Small and easy to use
  • Directly integrates into TextField and TextFormField
  • No dependencies to other packages to keep it small and lightweight
  • Supports multiple section and inline formats
  • FormatusBar provides multiple callbacks to integrate your favorite packages
  • FormatusViewer to display formatted text provided as HTML

Getting started

Add the latest version of Formatus to the pubspec.yaml file:

flutter:
  formatus: ^2.3.1

Follow these steps:

  1. create a FocusNode
  2. create a FormatusController
  3. supply both FormatusController and FocusNode to FormatusBar and TextField

Usage

Use Formatus like this:

  FocusNode _formatusFocus = FocusNode(debugLabel: 'formatus');
  late FormatusController controller;
  String savedText = '';

void initState() {
  controller = FormatusController( formattedText: savedText,
      onChanged: (v) => setState(() => savedText = v ),);
}

Widget build(BuildContext context) => Column( children: [
  FormatusBar(
    controller: controller,
    hideInactive: true,
    textFieldFocus: _formatusFocus,
  ),
  TextFormField(
    controller: controller,
    focusNode: _formatusFocus,
    minLines: 3,
    maxLines: 10 ),
  ]);

//--- Don't forget the standard dispose of the controller
void dispose() {
  controller.dispose();
}

Colors

The integrated color chooser can be replaced by supplying a custom chooser to the callback method onSelectColor in FormatusBar. See example main_color about how to use it.

Emojis

Formatus has an action emoji. The button will become available as soon as an emoji selector callback is provided to FormatusBar. If only a small number of emojis is required then this approach is sufficient:

FormatusBar( ...,
  onSelectEmoji: (context) async => await showDialog<String>(
    context: context, builder: (context) => Dialog(
      child: Wrap( children: ['πŸ˜€', 'πŸš€', 'βœ…'].map( (e) => IconButton(
        icon: Text(e), onPressed: () => Navigator.pop(context, e),
        ), ).toList(), ), ), ),

A full emoji package like emoji_picker_flutter can also be integrated easily:

FormatusBar( ...,
          onSelectEmoji: (context) => showEmojiSelector(), );
          
Future<String?> showEmojiSelector() async =>
  showAdaptiveDialog(context: context, builder: (BuildContext context) =>
    Dialog( child: EmojiPicker(textEditingController: controller, ), ), );

Tooltips

In web or on desktop it could be helpful to users to show a tooltip on each formatting action. To allow integration of any localizer this can be applied by using the callback tooltipBuilder in FormatusBar. See example main_mini.dart about how to use it.

FAQ

Q: I can only enter one line of text. Enter does not work

A: Supply minLines: 2 or a larger value to TextFormField

Q: Text is not visible in dark mode

A: Set background color of Flutter TextField to white: TextField( ..., decoration: InputDecoration( fillColor: Colors.white), ...);

Q: FormatusBar is centered instead of left aligned

A: This happens if the bar has a smaller width then the available space. Solution: wrap FormatusBar with IntrinsicWidth to keep it aligned.

Supported Formats

  • H1 β†’ Header 1 with largest font size
  • H2 β†’ Header 2 with larger font size
  • H3 β†’ Header 3 with large font size
  • P β†’ Paragraph with standard font size
  • OL β†’ Ordered list
  • UL β†’ Unordered list
  • B β†’ bold text
  • I β†’ italicized text
  • S β†’ strike-through text
  • U β†’ underlined text
  • SUB β†’ subscript text. Renders as normal text in the editor
  • SUP β†’ superscript text. Renders as normal text in the editor
  • color β†’ text coloring. Optional callback FormatusBar.onSelectColor allows setting another color chooser
  • A β†’ Anchor. Requires setting callback FormatusBar.onEditAnchor
  • IMG β†’ Image. Requires setting callback FormatusBar.onSelectImage

Enhancements

  • implement undo / redo functionality
  • parse Markdown format as input
  • implement a horizontal ruler (tag: HR)

For an additional enhancement request please open an issue.

Known Deficiencies

  1. Line-breaks in pasted text are replaced by spaces
  2. Lists cannot be nested
  3. Subscript and superscript are displayed correctly only in FormatusViewer because Flutter TextField and TextFormField do not support their editing

User Manual

Definition of Terms

Caret : visible display of the cursor position

Format : All text has a format. Its format is specified by the section format and all inline formats applied to the text

Section : All text belongs to a section. Each section has a format. Multiple sections are separated by a newline

Use cases

This section describes the use cases for Formatus.

  • Position caret β†’ updates FormatusBar with formats at caret position
  • Select a text range β†’ updates FormatusBar with formats common to all text in range
  • Activate another section-format in FormatusBar β†’ the current section (defined by caret position or start of a selected text-range) will be changed to the activated section-format
  • Change an inline-format in FormatusBar β†’ if a text range is selected then the selected text will be updated with the new format
  • Enter characters (via keyboard or by pasting from a clipboard) β†’ characters will be inserted at caret position. Current format settings will be applied
  • Delete characters β†’ if this includes one or more (requires a text-range) line-breaks then the text right of the deleted text will be integrated into the node at deletion start

Additional information

Please find additional information like architecture considerations at https://www.djarjo.com/formatus

If you encounter any issues or have ideas for some enhancements please open a ticket at https://github.com/hlemcke/formatus

Libraries

formatus