Today I’m publishing the very first version of Zefyr on pub.dartlang.org. Zefyr is a rich text editor for Google’s Flutter.

Note this is an early preview release, things are still changing and moving around. I encourage you to try it out and let me know what you think. If you found a bug or have a suggestion, please file them at the issue tracker.

Since this is the first introduction of Zefyr to the world, I’d like to give a brief overview of how this project came to be and highlight some of the design decisions.

About Me

My name is Anatoly, I’ve been doing software development for more than 10 years. Learned basics of object oriented design with Delphi and switched to web stack for various reasons. I’ve been writing mostly PHP and some Java before I discovered Dart about 3 years ago.

I’m maintaining a few Dart and Flutter packages and gave a lightning talk at DartConf 2018.

I’m building Zefyr as part of my main project — Memspace, which is currently in private beta.

The “Why”

The project started as an experiment to figure out what kind of rich text editing experience is possible with Flutter. A rich text editor is one of the key components for Memspace so it was important to research this part before settling on Flutter. It was also a great opportunity for me to learn Flutter in general.

Three Key Ideas Behind Zefyr

Since this is an introductory post I’d like to focus here on core ideas behind Zefyr and leave detailed in-depth review for future posts.

Comfortable Simplicity

There are many different text editors out there, from full-fledged word processors like Google Docs to minimalistic and clean like the one used here, on Medium.

With Zefyr I wanted to strike balance between the two sides by giving it clean and modern look with nice built-in typography, while providing just enough functionality to cover most of the daily writing needs.

Today Zefyr already supports following styles: bold, italic, links, headings, number and bullet lists, quotes and code blocks, of course. There is also basic support for embedding non-textual content with two embed types: horizontal rules and images.

Support for more styles and functionality is coming, including: indenting, nested blocks, syntax highlighting for code blocks, more embeds (videos, tweets, etc), undo history.

Ready for collaboration

Another important design decision early on was to provide a mechanism that allows using Zefyr for collaborative editing. This may not seem relevant for mobile applications, but it’s actually quite important.

Many users have more than one mobile device these days, so a good application should be able to seamlessly synchronize all the changes without loosing or corrupting any data. This is where collaborative technologies are really useful as they provide exactly what’s needed.

Zefyr uses Operational Transformations (OT) as its underlying mechanism for collaborative and conflict-free editing. I spent quite some time researching all the options and just learning in general. Luckily there are plenty of OT implementations available so I ended up settling on already existing implementation used in Quill.js editor, which is called Delta. Delta serves as both the data format and the mechanism for resolving conflicts.

Dart version of the Delta library is available on Pub as well.

Smart and friendly

A good text editor allows users to edit text and apply different styles.

A better text editor can edit text and apply styles on its own based on the context of the user’s action. Like, when I paste a URL here on Medium it’s automatically formatted as link. Or when I type * at the beginning of a line it’s automatically converted into a bullet list item.

In Zefyr this kind of rules are called heuristic rules. They are not part of the document model and can be adjusted to provide exactly the user experience needed. For instance, Markdown fans (me included) would probably like when a sequence of # at the beginning of a line is automatically converted into an H1 heading.

Flutter impressions

In the beginning I was struggling quite a bit. This is mostly because I had to learn about RenderObjects and how they relate to widgets. But once I had a good mental model of all the moving parts everything became pretty straightforward.

There was no need to implement rendering of rich text from scratch. Zefyr uses the same render objects as Flutter’s built-in widgets like RichText and Image which paint all the contents.

Huge help was Flutter’s inline documentation, it helps to stay focused without having to jump around between online docs, Google search and the IDE.

There were some bugs and blockers along the road, but it was almost always a minor change. I had a few pull requests submitted to the Flutter repository. Again, thanks to the Flutter team for reviewing and merging my PRs.

There is still one major known issue where Flutter only supports single Clipboard format text/plain . So copy-pasting in Zefyr currently does not preserve any styles yet ¯\_(ツ)_/¯. I’m sure this will be addressed in near future.

Next steps

There you have it, a very brief introduction to Zefyr. I will be posting more specific articles in coming months targeting certain parts of the editor, so stay tuned!

You can checkout documentation at https://github.com/memspace/zefyr. It provides a bit more details about the data format, document model, style attributes and heuristic rules.