Style a Document

Style a Document

Super Editor includes support for rudimentary stylesheets, which make it easy to apply sweeping styles across all document content.

A Stylesheet is a priority list of StyleRules. Each StyleRule has a BlockSelector, which determines for which nodes the rule applies. Think of BlockSelectors as rudimentary css selectors. BlockSelector can match all nodes, nodes of a specific type, nodes that appear after a specific node type, and so on. A StyleRule also includes a Styler, which is a function that returns the style metadata.

SuperEditor includes sane defaults for common node types, but you can define your own styles by providing a custom StyleSheet.

Creating a custom stylesheet

The easiest way is to create a custom stylesheet is to copy the defaultStylesheet and add your rules at the end. For example, to make all level one headers green, create the following stylesheet:

const myStyleSheet = defaultStylesheet.copyWith(
  addRulesAfter: [
    StyleRule(
      // Matches all level one headers.
      const BlockSelector("header1"),
      (document, node) { 
        return {
          Styles.textStyle: const TextStyle(color: Colors.green),
        };
      },  
    ),
  ],
);

Then pass it to SuperEditor.

class MyApp extends StatelessWidget { 
  @override
  Widget build(BuildContext context) {
    return SuperEditor(
      // ...
      stylesheet: myStyleSheet,
    );
  }
}

See the Styles class for the list of keys to the style metadata used by SuperEditor.

Multiple matching rules

Multiple StyleRules can match a single node. When that happens, SuperEditor attempts to merge them, by looking at each key. For example, consider the following stylesheet:

const myStyleSheet = defaultStylesheet.copyWith(
  addRulesAfter: [
    StyleRule(
      // Matches all level one headers.
      const BlockSelector("header1"),
      (document, node) { 
        return {
          Styles.textStyle: const TextStyle(color: Colors.green),
        };
      },  
    ),
    StyleRule(
      // Matches all nodes. 
      BlockSelector.all,
      (document, node) { 
        return {
          Styles.textStyle: const TextStyle(fontSize: 14),
        };
      },  
    )
  ],
);

Both styles will be applied. Each level one header will have green text with a font size of 14px.

If the styles can't be merged, the first one wins. For example, consider the following stylesheet:

const myStyleSheet = defaultStylesheet.copyWith(
  addRulesAfter: [
    StyleRule(
      // Matches all nodes. 
      BlockSelector.all,      
      (document, node) { 
        return {
          Styles.textAlign: TextAlign.center,
        };
      },  
    ),
    StyleRule(
      // Matches all nodes. 
      BlockSelector.all,
      (document, node) { 
        return {
          Styles.textAlign: TextAlign.right,
        };
      },  
    )
  ],
);

Since we cannot match two different text alignments, the first one is used. All nodes will be center-aligned.

However, non-conflicting keys are preserved. For example, consider the following stylesheet:

const myStyleSheet = defaultStylesheet.copyWith(
  addRulesAfter: [
    StyleRule(
      // Matches all nodes. 
      BlockSelector.all,      
      (document, node) { 
        return {
          Styles.textAlign: TextAlign.center,
        };
      },  
    ),
    StyleRule(
      // Matches all nodes. 
      BlockSelector.all,
      (document, node) { 
        return {
          Styles.textAlign: TextAlign.right,
          Styles.textStyle: const TextStyle(color: Colors.green),
        };
      },  
    )
  ],
);

SuperEditor keeps the text alignment from the first rule, ignores the text alignment from the second rule, and keeps the text style from the second rule. As a result, all nodes will be center-aligned and have green text.