世界を動かす技術を、日本語で。

FlutterとRustからRustとEguiに切り替えました

概要

  • Flutter + Rust から Rust + egui への移行体験談
  • 複雑性の最小化個人の強みに合わせた選択 が主な理由
  • eguiの即時モードUI による開発効率の向上
  • 生成コードやFFI管理の煩雑さ からの解放
  • パフォーマンス向上シンプルなUI構築 の実感

Flutter + RustからRust + eguiへの移行理由

  • これは 個人的な経験談 であり、絶対的な真実ではない
  • Flutter + Rust 構成では、 flutter_rust_bridge によるバインディング生成を利用
    • 多くのケースでflutter_rust_bridge_codegen generateだけで動作
    • ただし、FFIやAPI設計時に 生成が動作しない場合のトラブル が発生
  • egui を選択したきっかけは rerunプロジェクト の印象的な成果
  • 週末でUIコードを書き直し、 Flutterを捨てる決断

複雑性の最小化

  • 2言語併用 はプロジェクトの複雑性を増加
  • BoquilaHUB は小規模プロジェクトであり、不要な複雑性は排除したい
  • 自動生成コード は数千行に及び、理解不能で最適化困難
  • 自分で書いたコードより生成コードが多い 状態も発生
  • プロジェクト全体の理解度管理しやすさ を重視

個人の強みに合わせる

  • 開発者は自分一人 であり、FlutterのGUI力を活かしきれない
  • egui は最小テンプレートの修正だけで十分な見た目を実現
  • FlutterのWidget構造 による状態管理の煩雑さ(例:setState問題)に悩まされる
  • egui なら即時モードで毎フレームUIがリフレッシュ
    • コールバックや状態管理フレームワーク 不要
    • 例えばボタン追加もシンプルなRustコードで完結

即時モードUIのメリット

  • egui では状態管理・再描画の悩みがゼロ
  • 複雑なコールバックやフレームワーク (getx, bloc, provider等)不要
  • UI変更が即座に反映 され、直感的な開発体験

パフォーマンスの体感

  • Rustが速い、Dartが遅い という単純比較ではない
  • FFI経由の処理やデータコピーの影響 で、Flutter + Rust構成は体感的に遅く感じた
  • ONNX Runtime の呼び出しも、Rustのみの構成で より高速・スナッピー な動作
  • アプリ全体のレスポンス向上 を実感

まとめ

  • Flutter + Rust から Rust + egui へ移行したことで
    • 複雑性の削減
    • 自分に合った開発体験
    • パフォーマンス向上 を実現
  • BoquilaHUB への支援を希望

この体験談が、同様の選択に悩む方の参考になれば幸いです。

Hackerたちの意見

インスタントモードのGUIはクールだけど、アクセシビリティのサポートがちょっと不足してる気がする。ネイティブフレームワークではそれが無料で手に入ることが多いし、WebではARIAに従えば無料でできるけど、インスタントモードのGUIだといつも後回しにされてる感じがする。例えば、eguiはAccessKitをサポートしてるみたいだけど、Webで使うときはそうじゃないみたい。Dear ImGuiはもっとひどくて、そっち方面の努力はあるけど、アクセシビリティに関するチケットが開いてる状態(これはざっと見た感じだから、間違ってるかも)。インスタントモードはスピードとゲームみたいなアプリに焦点を当ててるから、そうなるのも分かるけど、両方の良いとこ取りができたらいいのにね。

インスタントモードのGUIでアクセシビリティサポートが不足しているのは、技術的な制限があるからなの?それとも単に手間がかかるだけ?Flutterはコントロールのレンダリングを自分でやるから、たくさんのアクセシビリティ機能を自分で実装しなきゃいけないんだよね。

ウェブのアクセシビリティの欠如は、即時モードの問題というより、ウェブのネイティブUIスタックを避けて、自分で全部レンダリングすることに起因してると思う。カスタムレンダリングの内容をブラウザに伝える方法はあるけど、それにはかなりの手間がかかるし、AccessKitがネイティブでやるよりも統合が必要なんだよね。

やっぱり昔ながらのWYSIWYGデザイナー付きのGUIフレームワークが好きだな。

最近、Slintがかなり近づいてきてると思う:https://slint.dev/ まだ簡単なドラッグ&ドロップはないけど、IDEのライブプレビュー機能があって、かなり近い感じ。Visual StudioのGUIデザインの日々が懐かしいけど、オープンソースのRust界隈も昔ほどシンプルじゃなくなってきたね。

WYSIWYGデザイナーは便利そうだけど、あんまり人気がないのには理由があるよね。コードでUIを書く方が柔軟だし、メンテもしやすいし、プロジェクトが大きくなるにつれてうまく機能するんだ。

FlutterFlowを試してみたけど、結局はすべてコードで書く方が好きだったな。それに、LLMはFlutterのレイアウトを書くのにすごく便利だよ。

HTMLにあまりにも焦点を当てすぎて、ウェブデザインが後退したってことをずっと言い続けるつもり。Dreamweaverのサイトは確かに素晴らしいものではなかったけど、今の大多数の作品よりもクリエイティビティとデザインがあったと思う。

eguiは、アプリケーションの実行時間が短いプロジェクトや、長寿命のプロジェクトのオーバーレイには最適だね。スクリプトのビジュアル版みたいなもので、ユーザーにとって役立つためには少しの即時の視覚フィードバックとパラメータの調整が必要だってことが分かる。Flutterはもっと堅牢なUIについての質問に答えてくれる。正しいツールを選んだのは良いことだし、もっと多くの人が選択肢があることを知るべきだと思う。でも根本的には、eguiのように低摩擦で、アクセシビリティ、パフォーマンス、スタイル可能な保持モードのGUIの視覚的柔軟性を持つ堅牢なUIフレームワークの可能性に最もモチベーションを感じてる。Raph Levienとxilemプロジェクトがそれに近づけてくれるかも。

UIが十分に速ければ、複雑なUIでも問題ないんじゃない?UI処理のコードをできるだけ速く保つための良いモチベーションになると思うよ。

両方のアプローチには欠点があって、私の見解では、保持モードとインスタントモードはUIの複雑さが増すにつれて収束する傾向があると思う。今のところ、少し複雑なアプリケーション(デスクトップのワードプロセッサ)でeguiを使っても、実装したいUIに問題はなかったよ。インスタントモードはReactからの新鮮な風だね。[編集:ただし、私のアプリケーションには標準的なアクセシビリティの批判が当てはまるけど、これはインスタントモード全般の問題というより、私の実装の問題だと思う。]

Flutterとeguiについての意見、ありがたい!完璧ではないけど、堅牢なUIと単一のコードベースを提供しているのはFlutterくらいしか知らない。Flutterよりも複雑でない方法で、同じように単一のコードベースからマルチプラットフォームソリューションを実現できるものにはとてもオープンだよ。

eguiはアプリケーションのランタイムが短命なプロジェクトに最適だよ 長寿命のアプリケーション、例えばメールクライアントには何がいいかな?Pythonの世界でQtがフィットするようなものを探してるんだ。

「flutter setstate is not refreshing」でググると、Flutterを使ってるとよく直面する問題が見つかるよ。簡単な修正に思えるけど、Flutterがたくさんのネストされたウィジェットを使う性質上、自然とラザニアコードができちゃって、これを理解するのが難しくなるんだ。これについて詳しく教えてくれる?自分はFlutterでsetStateや「ラザニアコード」に問題を感じたことはないよ。ざっと検索した感じだと、まだFlutterを学んでいる人たちが基本的なことを間違えてる質問が多いみたい。

Flutterの状態管理は色んな方法があって、複雑に感じるのもわかる。俺たちはHooksを多用してて、かなり簡素化されたよ。BLOCのやり方もあるし、setStateに問題があるってことは、アンチパターンなことをやってるってことだね。

アイスド(iced)は、もっと複雑なアプリケーション向けの素晴らしいRust GUIライブラリだと思うから、ここで言っておくね。: https://iced.rs

他の人がRustのGUIライブラリをシェアしてるから、Slintについても言っとくね。[https://slint.rs] これはRustで書かれたネイティブGUIツールキットだよ。宣言型のドメイン特化言語やエディターツールがあって、2023年以降はAPIの破壊的変更もなく安定してる。私も開発者の一人なんだ。

GUIの経験はあまりないんだけど、新しいGUIアプリケーションのために最近icedとgpuiを見てる。icedを使ったアプリは見た目がいいんだけど、どういうわけか俺のハイエンドLinuxボックスで「ラグ」があるように感じる。これって即時モードのGUIの限界なのか、俺のシステムに関係してるのか、アプリの問題なのか気になる。例えば、ドラッグ&ドロップやマウスでのテキスト選択がマウスカーソルに対して遅れてる感じがする。もしかして、持ち方が悪いのかな?

即時モードGUI(IMGUI)のパラダイムがすごく好きなんだ。先日、ウェブベースのIMGUIライブラリがあるか調べてみたんだけど、HTMLとDOMはIMGUIとは全然違う設計になってるから、残念ながらそういうアプローチはあんまり意味がないみたい。手動でキャンバス、WebGL、WebGPUでレンダリングしない限りはね。それにはまた別の課題があるし。

それって基本的にVDOMのことじゃない?

Mithril.js(https://mithril.js.org/)がすごく好きなんだ。個人的には、ウェブIMGUIに近いと思う。見た目はReactに似てるけど、レンダリングは手動で行われて、イベントごとにか、手動でm.redraw()を呼び出す形なんだ。

アプリ全体を一つの言語で書けるのは大きな利点だよね。モバイルでのeguiの体験はよくわからないけど、eguiは開発者として使うには最高だよ。 メリット

  • しっかりしたウィジェットセット
  • 始めやすい
  • 状態管理が少ない
  • カスタムウィジェットが作りやすい
  • 活発なコミュニティとクレート(ドッキングビューやテーブルなど)
  • 新しいUIを素早く構築できる

デメリット

  • レイアウトが難しい(マルチパスやいくつかのフレックスボックスクレートがあるけど、やっぱり難しいし、コンパイルループがあって反復が遅くなる)
  • 自分のアーキテクチャを持ち込む必要がある(アプリの作り方に制限がないから、気をつけないとスパゲッティコードになりやすい)

今のところ、Eguiが私のお気に入りのRust UIクレートだけど、Slintやicedも面白いよ。

icedがホットリロードに対応したよ! :tada: https://github.com/iced-rs/iced/pull/3000

似たようなことをやってるんだけど、こっちはGoを使ってるんだ。俺の場合、Flutterのフロントエンドと、Go Mobileで作ったGoのバックエンドがあるんだよ。いろんなネイティブフレームワークに対応したデータ型を使う方法を考える代わりに、フロントエンドとバックエンドで共有するタイプにはprotobufオブジェクトを使うことにしたんだ。これで、FlutterのFFIを通じてバイナリ配列を受け取ってprotobufオブジェクトに変換する単一のGo関数を公開できる。ビジネスロジックとフロントエンドの間にいい感じの分離ができて、フロントとバックエンドの両方に使いやすいオブジェクトも提供できる。全員にこのアプローチを勧めるかは分からないけど、protobufのコード生成は設定がちょっと面倒だからね。でも、アプリでGoの豊富なライブラリにアクセスするためにやってるんだ。

それは全然いいアプローチだよ。Protobufの強みはまさにこういう使い方だし。

はは、そうだね。埋め込みアプリケーションのためにprotobufとRPCを深く掘り下げてみたんだけど、たくさん学んで頭が痛くなったよ。heaplessを使ってたから、正しいVecサイズを使うようにジェネレーターを設定するまでエラーが出てたんだ。

この場合、gRPCを使うことを考えたことある? 100%の言語分離(FFIなし)とリモートクライアント/サーバーを得られるけど、少しコールオーバーヘッドが増えるよ。

これがBeatScratchでやったことだよ! https://beatscratch.io 音楽モデルはすべてProtobufメッセージで、Dart/FlutterからKotlin/C/Swift/JSのオーディオバックエンドに渡るんだ。保存や共有にもProtobufを使ってる。すごく耐久性があってパフォーマンスも良いよ。

ffiの話をするときに、フロントエンドとバックエンドがどういう意味かよくわからないんだ。これってリモートサーバーのバックエンドなのか、それとも同じアプリ内の話なの?俺はRustでproto bufを使ったことがあって、RustのクライアントがFlutterのフロントエンドとdbusで通信してた。Rustのクライアントはウェブソケットを通じてリモートサーバーに接続して、全てのメッセージはprotobufでラップされてバイナリとして送信されてた。これで全てがかなり具体的になったんだけど、結局自分でgRPCのしょぼいバージョンを作る羽目になった。ネットワークのWANが切れたらクライアントに通知が遅れて、ネットワークバッファがいっぱいになるとメッセージが消えちゃうことがあったからね。メッセージIDと確認プロセスを追加して、sqliteで各メッセージをバックアップした。あの時のことは今でも悪夢に出てくるよ。

それは起こらなかったから、Flutterの機能はほとんど活用されなかった。 > 見た目を良くしようとする努力すらしなかった。 原始的なニーズを満たすために原始的な道具を使うのは理にかなってるよね。余計な複雑さを避けるために。ただ、もし資金が集まってより良いGUIを作ることになったら、また切り替えなきゃいけないリスクがあるんじゃない?

ゲーム系のアプリケーションでEguiを使ってるんだけど、一般的なGUIアプリケーションでEguiが人気になっていくのがちょっと心配。機能が増えすぎて、オーバーヘッドも増えるだろうし。元々の目標は、Eguiがメインスレッドのフレーム時間の1%未満を使うことだったんだ。最初はEguiは完全に一回通しだったけど、APIはもっと一般的に見える。下や右に対して整列させたり、上や左に調整したりできるようになってる。でも最初はそれがうまくいかなかった。ウィジェットを下や右に配置する必要があったから、これが速くてシンプルだったんだ。スクロールするテキストボックスや行の折り返しがうまくいかないことも多かった。でも普通のGUI作業をしているユーザーは、どんどんレイアウト機能を求めていて、ブラウザレベルのレイアウトが実現されるまで止まらない。オーバーヘッドが増えてるのが問題だね。Rustのゲーム界隈では小さい問題なんだけど、いつか誰かがEguiをフォークしてEgui-liteを作る必要があるかもね。