Warning
At the time of writing,
visual-editor
is still in development and does not offer all the features flutter-quill
does.
There are a few bugs that are noticeable (more specifically, text selection)
that do not work properly on mobile devices.
We will use a fork of visual-editor
- https://github.com/dwyl/visual-editor in this guide.
This is because the PR that was opened to visual-editor
has need yet been merged
(visual-space/visual-editor#237).
Once it is merged, we'll update this dependency accordingly.
If you see that this PR has been merged and this document has yet to be updated,
please open an issue.
visual-editor
is a fork of flutter-quill
that,
unlike the latter,
is actively maintained.
They've done a complete refactor of flutter-quill
,
documented the code and made it easier to contribute to.
Because it offers some new features that can be easily integrated into your app,
we've created this small migration guide so you can leverage this library
from the app you've just implemented with flutter-quill
.
Note
visual-editor
provides a migration guide.
Check it in https://github.com/visual-space/visual-editor.
This guide builds upon the app that was created in
README.md
.
Make sure you have completed the tutorial first
until the 6. Give the app a whirl
chapter
and head back here so the code is in the same state.
If you've completed the rest of the chapters, it's okay! There principles are still the same 🙂.
Head over to pubspec.yaml
and change the dependencies
section to the following.
dependencies:
flutter:
sdk: flutter
file_picker: ^5.3.3
universal_io: ^2.2.2
responsive_framework: ^1.1.0
universal_html: ^2.2.3
path: ^1.8.3
path_provider: ^2.1.0
http: ^1.1.0
mime: ^1.0.4
http_parser: ^4.0.2
visual_editor:
git:
url: https://github.com/dwyl/visual-editor
ref: update_dependencies#236
- we've removed
flutter_quill
andflutter_quill_extensions
. - we've upgraded
http
to version1.1.0
. - we've installed
visual_editor
through the aforementioned fork. Normally you'll follow the guidelines invisual_editor
.
With visual-editor
, we do not need to use separate web embeds
to make it work on the web.
So you can safely delete 🗑️
mobile_platform_registry.dart
,
web_embeds.dart
and web_platform_registry.dart
,
as they no longer will be needed.
The classes from visual_editor
,
although similar to flutter-quill
,
have different constructors and some will differ,
so renaming classes won't work sometimes.
But let's do that first! We'll deal with each issue along the way. Let's start with replacing the imports. Change the imports like so:
import 'package:flutter_quill/flutter_quill.dart';
->import 'package:visual_editor/visual-editor.dart';
Add import 'package:visual_editor/document/models/attributes/attributes.model.dart';
, as well.
We are going to need it.
You can now delete any flutter_quill
and flutter_quill_extensions
import.
You can also delete import 'web_embeds/web_embeds.dart';
,
since it no longer exists.
Now, it's time to rename some classes! Follow the next steps.
QuillEditor
->VisualEditor
QuillController
->EditorController
QuillToolbar
->EditorToolbar
DefaultTextBlockStyle
->TextBlockStyleM
DefaultStyles
->EditorStylesM
Document
->DeltaDocM
Awesome!
The last thing we need to do is
deleting our _SelectionType
methods and classes.
We have used these to handle the triple click selection behaviour
on tap up.
We don't need this any more.
Therefore:
- delete the
_SelectionType
enum. - delete
_SelectionType _selectionType = _SelectionType.none;
field fromHomePageState
. - delete the
_onTripleClickSelection()
function insideHomePageState
.
Great job! 🥳
We are now ready to
change how our visual-editor
classes are constructed!
From now on, we'll only be working inside the HomePageState
class.
Let's start by changing our _initializeText
function.
EditorController
now only receives one argument,
which is the document
.
Change it to look like so:
Future<void> _initializeText() async {
final doc = DeltaDocM();
setState(() {
_controller = EditorController(
document: doc,
);
});
}
Let's move to the VisualEditor
.
We previously used QuillEditor
,
where we had a myriad of parameters we set.
These parameters are now more organised,
and will be changed like so.
Widget quillEditor = VisualEditor(
controller: _controller!,
scrollController: ScrollController(),
focusNode: _focusNode,
config: EditorConfigM(
scrollable: true,
autoFocus: false,
readOnly: false,
placeholder: 'Write what\'s on your mind.',
enableInteractiveSelection: true,
expands: false,
padding: EdgeInsets.zero,
customStyles: const EditorStylesM(
h1: TextBlockStyleM(
TextStyle(
fontSize: 32,
color: Colors.black,
height: 1.15,
fontWeight: FontWeight.w300,
),
VerticalSpacing(top: 16, bottom: 0),
VerticalSpacing(top: 0, bottom: 0),
VerticalSpacing(top: 16, bottom: 0),
null,
),
sizeSmall: TextStyle(fontSize: 9),
),
),
);
As you can see, most of the configuration
is now done under the config
parameter,
which receives an EditorConfigM
.
enableSelectionToolbar
becomesenableInteractiveSelection
.- we define the
customStyles
with theEditorStyleM
class. In this case, we are defining theh1
field withTextBlockStyleM
class, which has also changed. TextBlockStyleM
has an additional parameter, where you will need to define 4 arguments, instead of 3. The added argument pertains tolastLineSpacing
, the spacing at the end of the text block. We've just added aVerticalSpacing(top: 16, bottom: 0)
- we can't set the
subscript
andsuperscript
fields inEditorStylesM
(previouslyDefaultStyles
), as they're not yet available. - we've also removed the
onTapUp
callback field, as we no longer need it.
In the same file,
we've re-defined quillEditor
if it was in a web platform.
Let's update that as well.
It now becomes the following:
// Alternatively, the web editor version is shown (with the web embeds)
if (widget.platformService.isWebPlatform()) {
quillEditor = VisualEditor(
controller: _controller!,
scrollController: ScrollController(),
focusNode: _focusNode,
config: EditorConfigM(
scrollable: true,
enableInteractiveSelection: false,
autoFocus: false,
readOnly: false,
placeholder: 'Add content',
expands: false,
padding: EdgeInsets.zero,
customStyles: const EditorStylesM(
h1: TextBlockStyleM(
TextStyle(
fontSize: 32,
color: Colors.black,
height: 1.15,
fontWeight: FontWeight.w300,
),
VerticalSpacing(top: 16, bottom: 0),
VerticalSpacing(top: 0, bottom: 0),
VerticalSpacing(top: 16, bottom: 0),
null,
),
sizeSmall: TextStyle(fontSize: 9),
),
),
);
}
QuillToolbar
now becomes EditorToolbar
.
With flutter-quill
,
we used FlutterQuillEmbeds.buttons
and appended these custom embed buttons
this to the toolbar's children.
This is not the case in visual-editor
.
You will simply list the buttons in the customButton
field
and add the necessary callbacks (like webImagePickImpl
or onImagePickCallback
, for example) to the relevant buttons.
You can also use the children
field to enforce a custom order,
feeding the list straight to EditorToolbar
's constructor.
However, in this case, it's almost pointless to use this field as it does not provide much functionality on top of the customs buttons set.
Let's add our buttons to the toolbar, then!
Locate the toolbar
variable,
and change it.
// Toolbar definitions
const toolbarIconSize = 18.0;
const toolbarButtonSpacing = 2.5;
// Instantiating the toolbar
final toolbar = EditorToolbar(
children: [
HistoryButton(
buttonsSpacing: toolbarButtonSpacing,
icon: Icons.undo_outlined,
iconSize: toolbarIconSize,
controller: _controller!,
isUndo: true,
),
HistoryButton(
buttonsSpacing: toolbarButtonSpacing,
icon: Icons.redo_outlined,
iconSize: toolbarIconSize,
controller: _controller!,
isUndo: false,
),
ToggleStyleButton(
buttonsSpacing: toolbarButtonSpacing,
attribute: AttributesM.bold,
icon: Icons.format_bold,
iconSize: toolbarIconSize,
controller: _controller!,
),
ToggleStyleButton(
buttonsSpacing: toolbarButtonSpacing,
attribute: AttributesM.italic,
icon: Icons.format_italic,
iconSize: toolbarIconSize,
controller: _controller!,
),
ToggleStyleButton(
buttonsSpacing: toolbarButtonSpacing,
attribute: AttributesM.underline,
icon: Icons.format_underline,
iconSize: toolbarIconSize,
controller: _controller!,
),
ToggleStyleButton(
buttonsSpacing: toolbarButtonSpacing,
attribute: AttributesM.strikeThrough,
icon: Icons.format_strikethrough,
iconSize: toolbarIconSize,
controller: _controller!,
),
// Our embed buttons
ImageButton(
icon: Icons.image,
iconSize: toolbarIconSize,
buttonsSpacing: toolbarButtonSpacing,
controller: _controller!,
onImagePickCallback: _onImagePickCallback,
webImagePickImpl: _webImagePickImpl,
mediaPickSettingSelector: (context) {
return Future.value(MediaPickSettingE.Gallery);
},
),
],
);
Here's a list of relevant changes we've made:
- removed
FlutterQuillEmbeds.buttons
- we don't need to useFlutterQuillEmbeds
to create embed buttons any more. We simply create them normally in the children field. - removed the
afterButtonPressed
field fromEditorToolbar
constructor. - added
buttonsSpacing
field to all buttons, as it's required. Attribute
is now renamed toAttributesM
(which is why we imported it in the beginning of this guide).MediaPickSetting
is now renamed toMediaPickSettingE
.
Each button we add needs to have the buttonsSpacing
field defined.
But now can simply define our array of buttons,
display them in the order we like,
and not have to worry about web embeds or anything like that!
All the necessary callbacks (like onImagePickCallback
)
are related to relevant buttons (CameraButton
, for example).
So we don't add this behaviour to the definition of the toolbar
,
but to the adequate button.
Much simpler, right?
Let's fix the compiling errors on our tests.
Luckily, it's simple.
Just change the imports like we've done at the beginning of this guide,
and replace QuillEditor
with VisualEditor
(and other necessary classes).
The method tester.quillEnterText()
no longer exists,
so you can safely delete it.
Congratulations, you've successfully migrated
the app from flutter-quill
to visual-editor
!
Give yourself a pat on the back! 👏