-
Notifications
You must be signed in to change notification settings - Fork 0
WidgetSystem
← Home
実装状況:
- 実装済み:
StatelessWidget/StatefulWidget/InheritedWidgetとそれぞれの Element が動作します。State.SetState()による再ビルド、InheritedWidgetの依存追跡・通知、MultiChildRenderObjectElementのKey対応の子リスト差分も実装済みです。SingleChildRenderObjectWidget<T>/MultiChildRenderObjectWidget<T>ベースのウィジェット(ColoredBox,Align,Flex,Clip*,SizedBox,ConstrainedBox,RichText,Textなど)も使用可能で、BuildOwnerによる差分ビルドが動作します(BuildPipeline 参照)。- 未実装:
Padding,Container,ListView,GridView,SingleChildScrollView,Opacityはinternalで、公開 API から除外されています。入力系のGestureDetector/Listenerは公開スタブです。Button/Iconはデザインシステム層(FloatSoda.UI.Cream/FloatSoda.UI.FizzyPop)へ移動しました(→ UILayering)。- WIP:
FloatSoda.Hooks(R3 ベースのUseStateなど)はフレームワークのビルドループと未統合です。ジェスチャ・ヒットテストは未実装です。
Widget (immutable record)
│ CreateElement()
▼
Element (mutable) ← 状態・ライフサイクル管理、BuildOwner が差分ビルド
│ CreateRenderObject() / UpdateRenderObject()
▼
RenderObject ← レイアウト・描画(dirty フラグで差分更新)
-
Widget — UI の設計図。
abstract recordで不変。フレームごとに再生成されても==で差分検知できる。 - Element — Widget と RenderObject を橋渡しする永続ノード。ウィジェットが更新されても Element は再利用される。再ビルドの仕組みは BuildPipeline を参照。
-
RenderObject —
PerformLayoutとPaintを実装する描画エンジン。詳細は RenderObjects を参照。
| 基底クラス | 役割 | 対応する Element |
|---|---|---|
Widget |
すべてのウィジェットの基底。CreateElement() を宣言 |
— |
StatelessWidget |
Build(IBuildContext) で子ツリーを返す純粋関数コンポーネント |
StatelessElement ✓ |
StatefulWidget<T> |
CreateState() で State<T> を分離 |
StatefulElement ✓ |
InheritedWidget |
ツリー下方へのコンテキスト伝播 |
InheritedElement ✓ |
RenderObjectWidget<T> |
CreateRenderObject() / UpdateRenderObject(T) で RenderObject を所有 |
RenderObjectElement<T> ✓ |
SingleChildRenderObjectWidget<T> |
単一の Child を持つ RenderObjectWidget |
SingleChildRenderObjectElement<T> ✓ |
MultiChildRenderObjectWidget<T> |
Children(List<Widget>)を持つ RenderObjectWidget |
MultiChildRenderObjectElement<T> ✓(Key 対応の子リスト差分) |
RenderObjectToWidgetAdapter |
Widget ツリーのルートを RenderView に接続 |
RenderObjectToWidgetElement<RenderView> ✓ |
状態を持たない純粋関数コンポーネント。Build(IBuildContext) でウィジェットツリーを返します。
public record MyWidget : StatelessWidget
{
public required string Title { get; init; }
public override Widget Build(IBuildContext context)
{
return new Center
{
Child = new Text(Title)
};
}
}Build() はマウント時と、MarkNeedsBuild() でスケジュールされた再ビルド時に BuildOwner から呼ばれます。
StatefulWidget<T> は Widget から State<T> を分離するパターンです。State.SetState(Action) は状態を書き換えたうえで Element.MarkNeedsBuild() を呼び、次フレームの BuildScope() で再ビルドされます。
public record WatchWidget : StatefulWidget<WatchWidget>
{
public override State<WatchWidget> CreateState() => new WatchState();
}
public record WatchState : State<WatchWidget>
{
private Timer? _timer;
private string _time = "00:00:00";
public override void InitState()
{
_timer = new Timer(_ => SetState(() => _time = DateTime.Now.ToString("HH:mm:ss")),
null, dueTime: 0, period: 1000);
}
public override Widget Build(IBuildContext context) => new Text(_time);
}(このサンプルの全体は samples/FloatSoda.Samples.OverlayApp/WatchWidget.cs にあります)
State<T> のライフサイクルメソッド: InitState() / SetState(Action) / DidUpdateWidget(T oldWidget) / DidChangeDependencies()。
ツリーの下方にコンテキスト(テーマなど)を伝播させるためのウィジェットです。InheritedElement が依存する子孫を追跡し、UpdateShouldNotify(InheritedWidget oldWidget) が true を返したときに依存側を再ビルド対象にします。
WIP:
FloatSoda.Hooksプロジェクトに R3 ベースのHookWidget/HookElementが部分実装されていますが、フレームワークのビルドループとは未統合です。HookExtensionのUseState/UseEffect/Depends/UseMemo/UseActionはNotImplementedExceptionを投げます。
HookWidget.Build() 内で UseState(initialValue) を呼ぶと ReactiveProperty<T> が返り、値の変更が再ビルドをトリガーする、という React フック風の API を目指しています。
// 構想中の API(未動作。Button は FloatSoda.UI.Cream などのデザインシステム層のもの)
public override Widget Build(IBuildContext context)
{
var count = UseState(0);
return new Button
{
Child = new Text($"Count: {count.Value}"),
OnPressed = () => count.Value++,
};
}| ウィジェット | 実装状況 | 説明 | 主なプロパティ |
|---|---|---|---|
Center |
✓ | 子を中央に配置(Align に委譲) |
Child |
Align |
✓ | 子を指定の Alignment で配置 |
Alignment, WidthFactor, HeightFactor, Child
|
Column |
✓ | 垂直方向に並べる(Flex に委譲) |
Children, MainAxisAlignment, CrossAxisAlignment, MainAxisSize
|
Row |
✓ | 水平方向に並べる(Flex に委譲) |
Children, MainAxisAlignment, CrossAxisAlignment, MainAxisSize
|
Flex |
✓ | 方向指定のフレックスレイアウト。UpdateRenderObject と Key 対応の子リスト差分に対応 |
Direction, Children, MainAxisAlignment, CrossAxisAlignment, VerticalDirection
|
SizedBox |
✓ | 固定サイズのボックス |
Width, Height, Child
|
ConstrainedBox |
✓ | 追加制約を適用 |
Constraints (BoxConstraints), Child
|
Padding |
✗ internal スタブ |
子に余白を追加(RenderSiftedBox.PerformLayout 未実装) |
Spacing (EdgeInsets), Child
|
Container |
✗ internal スタブ |
パディング・色・サイズなどを一括指定 | — |
ListView |
✗ internal スタブ |
スクロール可能なリスト | Children |
GridView |
✗ internal スタブ |
グリッドレイアウト | — |
SingleChildScrollView |
✗ internal スタブ |
単一子をスクロール | Child |
| ウィジェット | 実装状況 | 説明 | 主なプロパティ |
|---|---|---|---|
ColoredBox |
✓ | 単色背景 |
Color (SKColor), Child
|
Image (Paint) |
✓ |
ImageProvider 経由で画像を表示 |
ImageProvider, Child
|
ClipRect |
✓ | 矩形クリップ |
Clipper, ClipBehavior, Child
|
ClipRoundRect |
✓ | 角丸矩形クリップ |
BorderRadius, Clipper, ClipBehavior, Child
|
ClipOval |
✓ | 楕円クリップ |
CustomClipper, ClipBehavior, Child
|
ClipCustomPath |
✓ | カスタムパスクリップ |
Clipper, ClipBehavior, Child
|
Opacity |
✗ internal スタブ |
透明度を適用 | Child |
| ウィジェット | 実装状況 | 説明 | 主なプロパティ |
|---|---|---|---|
FadeTransition |
✓ |
IAnimation<double> で子の不透明度を駆動(リビルド不要、ペイントのみ)→ Animation
|
Opacity (IAnimation<double>), Child
|
| ウィジェット | 実装状況 | 説明 | 主なプロパティ |
|---|---|---|---|
RichText |
✓ | スタイル付きテキスト(Topten.RichTextKit) |
Text (TextSpan) |
Text |
✓ | プレーンテキスト表示(RichText に委譲) |
Data (string) |
Button / Icon はデザインシステム層(FloatSoda.UI.Cream / FloatSoda.UI.FizzyPop)へ移動しました。振る舞いを担うヘッドレスウィジェット(ButtonBase など)は FloatSoda.UI にあります。詳細は UILayering を参照してください。
| ウィジェット | 実装状況 | 説明 |
|---|---|---|
GestureDetector |
✗ スタブ | タップ・ドラッグ検知 |
Listener |
✗ スタブ | 低レベル入力ハンドラ |
IKey / ValueKey<T> / UniqueKey が定義され、Widget.Key プロパティと差分判定に組み込まれています。Widget.CanUpdate(old, new) は「同じ実行時型かつ Key が等しい」なら既存 Element を再利用します(Flutter と同じ型 + Key 判定)。Element.UpdateChild は先に record 等値の高速パスで同一 Widget をスキップし、その後 CanUpdate で更新可否を判断します。MultiChildRenderObjectElement の子リスト差分でも Key を使って要素の同一性を追跡します(詳細は BuildPipeline)。
- BuildPipeline — BuildOwner / dirty list / UpdateChild の詳細
- RenderObjects — Widget が生成する RenderObject のリファレンス
- GettingStarted — Widget を使った最初のアプリ
- UILayering — ヘッドレスUI層とデザインシステム層の構成
- APIDesign — ウィジェット API の設計規約