setStateの役割とは?
🤔どんな目的で使う?
setStateはFlutterでアプリケーションの状態を管理し、UIを更新するための重要なメソッドです。
画面の更新:
setStateを使う最も一般的なユースケースは、ユーザーのインタラクション(ボタンクリック、テキストの入力など)に応じて画面の一部を更新する場合です。
以下に、ボタンをクリックするとカウンタが増加するシンプルな例を示します:
カウンターアプリ
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return const MaterialApp(title: 'Flutter Demo', home: CounterWidget());
}
}
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Count: $_counter'),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
💁メリットは?
シンプルさ:
setStateはFlutterフレームワークの中心的な部分であり、Widgetの状態を管理する最も基本的な方法です。特に小規模なアプリケーションや初心者にとっては、setStateの使用は直感的で簡単です。ステートフルウィジェットの内部で状態を更新するには、ただsetStateを呼び出すだけです。
すぐにUIを更新:
setStateを呼び出すと、Flutterはそのウィジェットを再描画します。これにより、状態の変更を即座にUIに反映させることができます。
明示的な状態管理:
setStateを使用することで、状態の変更が明示的になります。すなわち、どの状態が変更され、その結果としてどのウィジェットが再描画されるかが明らかになります。
したがって、小規模なアプリケーションや状態管理の初心者にとっては、setStateは便利な状態管理の手段です。
🤦デメリットは?
その一方で、setStateはいくつかのデメリットも持っています。
規模の大きいアプリケーションでは状態管理が難しくなる:
多くの状態を持つアプリケーションでは、各状態をどのWidgetが管理すべきかが複雑になります。
全体の再レンダリング:
setStateを呼び出すと、Widget全体が再レンダリングされます。そのため、パフォーマンスの問題が生じる可能性があります。
これらのデメリットを解決するために、Flutterでは状態管理ライブラリ(例えば、ProviderやRiverpod)を利用することが推奨されています。
まとめ
DevToolsのアプリのパフォーマンスを調べることができるツールを使用して、比較をしてみると、StatefulWidgetは、JANKが多く、Riverpodを使用した場合は、少ないようです。
StatefulWidgetの場合
Riverpodの場合
「JANK」とは、UIが滑らかに動かない、つまりフレームレートが一時的に低下する現象を指します。60fps(フレームパーセコンド)が理想的なレンダリング速度であり、これに達するためには、フレームを描画するのに16.67ミリ秒以内(1秒÷60)が必要です。16.67ミリ秒以上かかると、それが「jank」となり、ユーザーにとってはアプリの動作が不自然に見えるか、遅く感じることになります。
DevToolsのパフォーマンスタブにあるフレームタイムグラフでは、赤いバーがJANKを示しています。これは、該当フレームのレンダリングに16.67ミリ秒以上かかったことを意味します。
JANKが多いということは、アプリのパフォーマンスに問題がある可能性を示しています。これは通常、再描画の頻度が高すぎる、またはCPUやメモリの使用量が高すぎることが原因であることが多いです。
Riverpodを使用した場合にJANKが少ないと感じるのは、Riverpodがより効率的な状態管理を提供するためです。Riverpodを使用すると、状態の変化があった場合でも、その状態を直接使用しているウィジェットだけが再描画されます。これにより、再描画の頻度と範囲が制限され、結果的にパフォーマンスが向上する可能性があります。
StatefulWidgetを多用すると、状態の変化ごとに大量の再描画が発生し、これがJANKを引き起こす可能性があります。これは特に、アプリの状態が頻繁に更新され、大規模なウィジェットツリーが存在する場合に顕著です。
したがって、パフォーマンスの問題を解決するためには、Riverpodのような状態管理ライブラリの使用や、再描画の範囲を制限する工夫が有効です。
Discussion