parent
7ab6444520
commit
7282e721dd
1 changed files with 142 additions and 0 deletions
@ -0,0 +1,142 @@ |
|||||||
|
## Rule |
||||||
|
|
||||||
|
`Rule` in `flutter_quill` is a handler for specific operations within the editor. They define how to apply, modify, or delete content based on user actions. Each Rule corresponds to a type of operation that the editor can perform. |
||||||
|
|
||||||
|
### RuleType |
||||||
|
|
||||||
|
There are three main `RuleTypes` supported in `flutter_quill`, each serving a distinct purpose: |
||||||
|
|
||||||
|
- **insert**: Handles operations related to inserting new content into the editor. This includes inserting text, images, or any other supported media. |
||||||
|
|
||||||
|
- **delete**: Manages operations that involve deleting content from the editor. This can include deleting characters, lines, or entire blocks of content. |
||||||
|
|
||||||
|
- **format**: Deals with operations that apply formatting changes to the content in the editor. This could involve applying styles such as bold, italic, underline, or changing text alignment, among others. |
||||||
|
|
||||||
|
### How Rules Work |
||||||
|
|
||||||
|
When a user interacts with the editor in `flutter_quill`, their actions are translated into one of the predefined `RuleType`. For instance: |
||||||
|
|
||||||
|
- When the user types a new character, an **insert** Rule is triggered to handle the insertion of that character into the editor's content. |
||||||
|
- When the user selects and deletes a block of text, a **delete** Rule is used to remove that selection from the editor. |
||||||
|
- Applying formatting, such as making text bold or italic, triggers a **format** Rule to update the style of the selected text. |
||||||
|
|
||||||
|
`Rule` is designed to be modular and configurable, allowing developers to extend or customize editor behavior as needed. By defining how each RuleType operates, `flutter_quill` ensures consistent and predictable behavior across different editing operations. |
||||||
|
|
||||||
|
|
||||||
|
### Example of a custom `Rule` |
||||||
|
|
||||||
|
In this case, we will use a simple example. We will create a `Rule` that is responsible for detecting any word that is surrounded by "*" just as any `Markdown` editor would do for italics. |
||||||
|
|
||||||
|
In order for it to be detected while the user writes character by character, what we will do is extend the `InsertRule` class that is responsible for being called while the user writes a word character by character. |
||||||
|
|
||||||
|
```dart |
||||||
|
/// Applies italic format to text segment (which is surrounded by *) |
||||||
|
/// when user inserts space character after it. |
||||||
|
class AutoFormatItalicRule extends InsertRule { |
||||||
|
const AutoFormatItalicRule(); |
||||||
|
|
||||||
|
static const _italicPattern = r'\*(.+)\*'; |
||||||
|
|
||||||
|
RegExp get italicRegExp => RegExp( |
||||||
|
_italicPattern, |
||||||
|
caseSensitive: false, |
||||||
|
); |
||||||
|
|
||||||
|
@override |
||||||
|
Delta? applyRule( |
||||||
|
Document document, |
||||||
|
int index, { |
||||||
|
int? len, |
||||||
|
Object? data, |
||||||
|
Attribute? attribute, |
||||||
|
Object? extraData, |
||||||
|
}) { |
||||||
|
// Only format when inserting text. |
||||||
|
if (data is! String) return null; |
||||||
|
|
||||||
|
// Get current text. |
||||||
|
final entireText = document.toPlainText(); |
||||||
|
|
||||||
|
// Get word before insertion. |
||||||
|
final leftWordPart = entireText |
||||||
|
// Keep all text before insertion. |
||||||
|
.substring(0, index) |
||||||
|
// Keep last paragraph. |
||||||
|
.split('\n') |
||||||
|
.last |
||||||
|
// Keep last word. |
||||||
|
.split(' ') |
||||||
|
.last |
||||||
|
.trimLeft(); |
||||||
|
|
||||||
|
// Get word after insertion. |
||||||
|
final rightWordPart = entireText |
||||||
|
// Keep all text after insertion. |
||||||
|
.substring(index) |
||||||
|
// Keep first paragraph. |
||||||
|
.split('\n') |
||||||
|
.first |
||||||
|
// Keep first word. |
||||||
|
.split(' ') |
||||||
|
.first |
||||||
|
.trimRight(); |
||||||
|
|
||||||
|
// Build the segment of affected words. |
||||||
|
final affectedWords = '$leftWordPart$data$rightWordPart'; |
||||||
|
|
||||||
|
// Check for italic patterns. |
||||||
|
final italicMatches = italicRegExp.allMatches(affectedWords); |
||||||
|
|
||||||
|
// If there are no matches, do not apply any format. |
||||||
|
if (italicMatches.isEmpty) return null; |
||||||
|
|
||||||
|
// Build base delta. |
||||||
|
// The base delta is a simple insertion delta. |
||||||
|
final baseDelta = Delta() |
||||||
|
..retain(index) |
||||||
|
..insert(data); |
||||||
|
|
||||||
|
// Get unchanged text length. |
||||||
|
final unmodifiedLength = index - leftWordPart.length; |
||||||
|
|
||||||
|
// Create formatter delta. |
||||||
|
// The formatter delta will include italic formatting when needed. |
||||||
|
final formatterDelta = Delta()..retain(unmodifiedLength); |
||||||
|
|
||||||
|
var previousEndRelativeIndex = 0; |
||||||
|
|
||||||
|
void retainWithAttributes(int start, int end, Map<String, dynamic> attributes) { |
||||||
|
final separationLength = start - previousEndRelativeIndex; |
||||||
|
final segment = affectedWords.substring(start, end); |
||||||
|
formatterDelta |
||||||
|
..retain(separationLength) |
||||||
|
..retain(segment.length, attributes); |
||||||
|
previousEndRelativeIndex = end; |
||||||
|
} |
||||||
|
|
||||||
|
for (final match in italicMatches) { |
||||||
|
final matchStart = match.start; |
||||||
|
final matchEnd = match.end; |
||||||
|
|
||||||
|
retainWithAttributes(matchStart + 1, matchEnd - 1, const ItalicAttribute().toJson()); |
||||||
|
} |
||||||
|
|
||||||
|
// Get remaining text length. |
||||||
|
final remainingLength = affectedWords.length - previousEndRelativeIndex; |
||||||
|
|
||||||
|
// Remove italic from remaining non-italic text. |
||||||
|
formatterDelta.retain(remainingLength); |
||||||
|
|
||||||
|
// Build resulting change delta. |
||||||
|
return baseDelta.compose(formatterDelta); |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
To apply any custom `Rule` you can use `setCustomRules` that is exposed on `Document` |
||||||
|
|
||||||
|
```dart |
||||||
|
quillController.document.setCustomRules([const AutoFormatItalicRule()]); |
||||||
|
``` |
||||||
|
|
||||||
|
You can see a example video [here](https://e.pcloud.link/publink/show?code=XZ2NzgZrb888sWjuxFjzWoBpe7HlLymKp3V) |
Loading…
Reference in new issue