Text can be selected on screen. Additional widgets can be rendered to control the text actions. One might expect that the copy paste menus are implemented by the system. Actually these are fully controller by Visual Editor. We currently have 2 ways of customizing the selection menu:
- Attaching To Markers (new) - When the selection callbacks have emitted we can use the rectangles data to place any attachment anywhere. Recommended when you want to place atypical looking markers related to the lines of selected text.
- TextSelectionControls (old) - Standard flutter procedure using a custom TextSelectionControls. Recommended when you want to display standard selection menu with custom buttons.
This is a breakdown of the various parts that are used to execute updates of the selection on screen.
- TextGestures - Starting and updating the selection is triggered from the
TextGestures
widget. TheTextGestures
widget is used as a wrapper over the document text in the mainbuild()
method. It adds a gesture detector which interprets the various touchscreen signals into potential gestures. - TextGesturesService - Controls the selection of text after tapDown and tapUp events.
- SelectionService - Once the raw events are parsed then we send higher level calls to the
SelectionService
.- selectWordsInRange() - One highly interesting method in the codebase is
selectWordsInRange()
. This methods teaches you a lot about how the document model and editor work together. This method makes use ofselectWordsInRange()
andgetWordAtPosition()
. These methods are highly useful for computing coordinates on screens. They are now exposed to the public in theEditorController
.
- selectWordsInRange() - One highly interesting method in the codebase is
- State Store - Once the selection range is detected it's then passed to the state store.
- controller.updateSelection() - Once a gesture is detected in the callback executed, then the
controller.updateSelection()
is called which triggers therunBuild()
which triggers abuild()
in main. - build() -Remember, the main
build()
contains the widgets generated by thedocumentBlocsAndLines()
. These are a list of editable text lines and blocks. - EditableTextLine - Once
build()
is triggered eachEditableTextLine
widget, then calls on the renderer object methods to update information. If the renderer callbacks notice that the selection is changed and within it's bounds then it triggers apaint()
cycle. - paint(), Rectangles - A new
paint()
cycle will "render" the selection rectangles vector data on top of the text. Thanks to the rectangles coordinates the client devs have total freedom to place their attachments wherever needed. - onSelectionCompleted() - This callback is invoked after the full build cycle is completed. This ensures that we have acces to the rectangles data. The rectangles data is computed during the paint() phase.
This is a general overview of setting up a selection menu or custom widgets when the text selection has changed. To view a complete sample go to the SelectionMenuPage
and inspect the code. Looking at the demo code one can believe that the current API for managing selection menu is rather complex. However, our goal is not to provide a minified toolbars as the selection menu. Our goal is to provide an API that is versatile enough to be used for any menu, in any position triggered by any conditions you can think of. This gives maximum flexibility to implement any kind of UX interactions you might need for your particular app. Even the positioning logic was left open for the client developers to best decide what fits them best. If all you need is to setup a new button in the custom menu, then try using the custom controls option.
Widget build(BuildContext context) => Stack(
children: [
DemoPageScaffold(
child: _controller != null
? _col(
children: [
_editor(),
_toolbar(),
],
)
: Loading(),
),
// Has to be a Positioned Widget (anything you need)
if (_isQuickMenuVisible) _quickMenu(),
],
);
Init the editor with callbacks hooked to the selection events.
Widget _editor() => VisualEditor(
controller: _controller,
scrollController: _scrollController,
focusNode: _focusNode,
config: EditorConfigM(
onScroll: _updateQuickMenuPosition,
// Hide menu while the selection is changing
onSelectionChanged: (selection, rectangles) {
_hideQuickMenu();
},
// Trigger the rendering of the attached menu or custom widgets
onSelectionCompleted: (markers) {
final isCollapsed = _controller?.selection.isCollapsed ?? true;
// Don't render menu for selections that are collapsed (zero chars selected)
if (!isCollapsed) {
// Use your own logic for rendering and positioning the attached widget(s)
_displayQuickMenuOnTextSelection(markers);
}
},
),
);
- Selection Handles - On mobile the copy paste actions are executed via the context menu. These are controlled by the
SelectionHandlesController
. This controller is also in charge of rendering the options menu overlaid on top of the text. - Flutter Custom Selection - Flutter custom selection toolbar