0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flutter Webで日本語入力が崩れない最強のPlain Text Editorを開発した話

Last updated at Posted at 2026-02-08

TL;DR;

FlutterWebでも使えるPlaintext Editorって全然存在しないこと知ってましたか?
Flutter Quill, zeflyなどなど、便利な機能がたくさんあるエディターは存在しますが、これら全部まともに使えません。
なぜなら、全部日本語の表示が崩れるからです。

今回は、日本語を入力させても絶対に表示が崩れない、かつ必ず正確に表示し、高速に動作する。
そんなエディターを開発しました。

demo

plain_text_editor_demo.gif

コード

このプレーンテキストエディタの強みは?

表示部分にFlutterのNative要素を用いていない

これにより、表示部分のテキストの柔軟性が大きく向上します!

これまで、Flutterで改行を含む文字の入力を受け付ける手段は、TextFieldしかありませんでした。
これは、そもそもFlutterのネイティブの要素として、TextField以外、文字入力を受け付ける手段が存在しなかったためです(逆にいうと、それぐらいTextFieldが最低限の機能を確実に実装してたとも言える)。

ですが、TextFieledには大きな問題点がありました...

TextFieldが持つ"Ideographic"な文字の表示不具合

これは、日本語に含まれる"ひらがな"や"カタカナ"、"漢字"(中国語圏も含む)がFlutter上では正しく表示できないという問題です。

具体的には、Flutterの持つ文字のレンダリングエンジンが、”文字の高さ情報を文字の形で計算”してしまうのがすっごく問題なんです。
普通にサイズが崩れるのも問題なんですけど、FlutterWebに限って、TextFieldの多段入力でえげつない崩れ方すんですよ。

実際は下のやつみたいになります。

image.png

若干、日本語のbaselineと文字列変換(TextCompose状態)のアンダーラインが、ずれています。

ちなみにこのずれ方はまだいい方で、文字サイズをもう少し大きくしたり、行間を広げたりするともっと大変なことになります。

ちなみにこの問題の前にStructStyleは無力です。

以下、この問題におけるFlutter本体のIssueとQiitaの記事です。

結論、WebViewを実装しないと実現できそうにない。

既存のTextFieldに対して、次のことを試しました。

  • StructStyleの適用
  • TextStyleの調整
  • サイズの調整
  • etc...

全く改善されそうにもありません。
ずっとずれたままです。

よくよく調べると、上のIssueにもあるように、Flutterの根底に問題がありそうなんですよ。

つまり、私たちじゃどうしようもないんです。

なら、どうするか。

文字のレンダリングエンジンをFlutterのエンジンから別のやつに載せ替えるしかない。

ここで出てくるのが、HTMLのレンダリングエンジン。
というわけです。

Flutterの基盤言語Dartには、dart:webというブラウザのAPIへ直接接続するためのラッパークラスとdart:js_interopというWebのためだけのライブラリが存在します。

これを使えば、次のことができるようになります。

  • HTMLオブジェクトをFlutterのWidgetとして扱える
  • オブジェクト単位でJavaScriptの相互設定ができる
    • Dart -> JavaScript (JavaScriptで設定された関数の実行)
    • JavaScript -> Dart(JavaScriptのイベントを検知しDartのCallBackを実行可能)

これを使えば、テキストエディタ実装できそうですよね?
しかも、HTMLのレンダーは各ブラウザが搭載しているものを使います。
ってことは、表示が崩れる責任をブラウザのせいにできます。
しかも、大半のブラウザは日本語正式対応です。

つまり勝ち確です。

HTMLへの接続

これは、dart標準ライブラリを使いました。
flutter_in_appweb_viewwebview_flutterなど、公式、サードパーティ関係なく、HTMLを扱うライブラリはたくさん存在します。

ですが、これらは"普通のネイティブアプリにブラウザの機能を追加する"みたいなことをすルためのライブラリなので、Flutter Webとはそもそも趣向が異なります。

なぜならFlutter Webはそもそも、表示される場所が、ブラウザーなので。

そのため、制限的なインポートを使いつつ、ブラウザ専用APIのラッパーであるdart:webを使うことにしました。

他にもdart:htmlが存在します。ですが、これは最新のDartでは非推奨パッケージです。公式もdart:webへの移行を推奨しています。

成果物の説明

EditControllerクラス

これは、PlaintextEditorのためのControllerです。

使い方は、TextEditingControllerなどと同じです。

もし、PlaintextEditorを実装する場合、必ずPlaintextEditor内でこのコントローラーを受け取り、テキストコールバックを設定してください。
つまり、このコントローラーはインターフェース的な役割しか担ってません。

PlaintextEditorをライブラリとしてExportするコード

これは、コンパイルエラーを回避するためのものです。

PlaintextEditor Webにはdart:webライブラリが必要ですが、これはFlutter Webのplatformにしか存在しません。そのため、一般にコンパイルするとエラーが出ます。

そのため、存在するライブラリによって、外部に出力するコード、ライブラリを替えるために、export文を使っています。

具体的には、stubを基本読み込みの対象として、存在するライブラリによって、出力するコードを切り替えています。

PlaintextEditor Stub

これは、コンパイラーを騙すための、クラスの形を決めるためのクラスです。
いわば抽象クラスみたいなものです。

PlaintextEditor Web

実際にdart:webを使って実装しています。

PlaintextEditor Native

まだ実装していません。

最後までお読みいただきありがとうございます。

また時間ができたら、もう少しFlutter Webの問題を解決するために躓いた点と解決策をメモって書いていきます!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?