UIを更新するのにUI自体の要素は一切変更しない
これが宣言的UIと言われる考え方で最も重要なコンセプトですが、ここで詰まる人は必ずいます。特に初めてフロントエンドのフレームワークを使う方は。バックエンドの経験があっても、です。
直感的なUIの更新コードって、こうですよね
以下、jQueryとかでよく見るコードです。
<button id="click-button">クリックしてね</button>
<p>クリック回数: <span id="click-count">0</span></p>
<script>
$(document).ready(function () {
let count = 0;
$('#click-button').on('click', function () {
count++;
$('#click-count').text(count);
});
});
</script>
ポイントは、click-coun
t という要素を取得して、
textというUIの属性を書き換えていることです。UI(こここではspanタグを初期化した後に、そのタグの属性を書き換えています。UIが初期化した後でも、UIが持っている属性(ここではtext)を変更できます。色や表示/非表示等も、同様に変更できます。
Flutterに限らず、宣言的UIをサポートしているフレームワークでは、上記のようなコードは書けません。UIを初期化した後で、その属性を何かのイベントで書き換えることが出来ないんです。
宣言的UI a.k.a UI = f(state)
FlutterでUIを変更するには、「自分が参照しているデータは更新されたので、UIを再構築(リビルド)しなさい」とWidgetに伝える必要があります。
UIを更新するロジックを書くのではなく、カウントアップされたので新しい数字になったので、今までのUIは破棄して、全く新しいUIを作って表示を更新する、という考え方になります。
UIの属性(テキストやスタイル)を都度書き換えるスタイルは、UIのロジックとデータを読み書きするロジックが、どうしても混在します。UIの属性を直接書き換えるスタイルはわかりやすくはありますが、どこからでもUIが更新できるため、秩序が崩れやすいです。
それに比べ宣言的UIで定義するのは、状態です。状態を単純に言えば、ユーザー操作によって更新されるデータのことです。カウントアップだと、押された回数がそれに当たります。更新があるので、その都度、最新化する必要があります。
更新されない読み取り専用のデータであれば、状態は存在しません。常に同じものを出すだけ。カウンターのように、最新化する必要があるのであれば、最新の状態でUIを構築する必要があります。
この絵を丸暗記してください。経典のようなものです。

FlutterでUIを構成するパーツのことを、Widgetと呼びます。HTMLでいうと、タグです。pとかspanとかtableとか。上記の方式は、Flutterにおきかえると Widget = build(state) になります。stateを引数に入れてbuildメソッドが実行されます。
Stateって何
次の問題です。state は何を表現しているのかについて、です。
これについては、別の記事で詳しく取り上げます。