前回のセクションでは、電卓アプリのUIを作成しました。このセクションでは、電卓としての機能、つまり、ボタンをタップしたときの動作や、計算ロジックなどを実装していきます。前編では、状態管理の仕組みを導入し、数値と演算子を保持する方法について説明します。
1. 状態管理の導入
電卓アプリでは、ユーザーが入力した数値や選択した演算子を記憶し、それに基づいて計算を行い、結果を表示する必要があります。このようなアプリの状態を管理するために、ステートフルウィジェット (StatefulWidget) を使用します。
まず、Calculator
ウィジェットをステートフルウィジェットに変更しましょう。
dart
class Calculator extends StatefulWidget {
const Calculator({super.key});
@override
State<Calculator> createState() => _CalculatorState();
}
class _CalculatorState extends State<Calculator> {
// TODO: 状態変数と計算ロジックをここに追加
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
// 結果表示エリア
Expanded(
child: Container(
color: Colors.blueGrey,
alignment: Alignment.bottomRight,
padding: const EdgeInsets.all(16.0),
child: const Text(
'0',
style: TextStyle(
fontSize: 48.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
// ボタンエリア
Container(
height: 350.0,
color: Colors.black,
child: Column(
children: <Widget>[
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '7',
onPressed: () {},
),
CalculatorButton(
text: '8',
onPressed: () {},
),
CalculatorButton(
text: '9',
onPressed: () {},
),
CalculatorButton(
text: '/',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '4',
onPressed: () {},
),
CalculatorButton(
text: '5',
onPressed: () {},
),
CalculatorButton(
text: '6',
onPressed: () {},
),
CalculatorButton(
text: 'x',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '1',
onPressed: () {},
),
CalculatorButton(
text: '2',
onPressed: () {},
),
CalculatorButton(
text: '3',
onPressed: () {},
),
CalculatorButton(
text: '-',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '.',
onPressed: () {},
),
CalculatorButton(
text: '0',
onPressed: () {},
),
CalculatorButton(
text: 'C',
onPressed: () {},
),
CalculatorButton(
text: '+',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '=',
flex: 2,
onPressed: () {},
color: Colors.orange,
),
],
),
),
],
),
),
],
),
);
}
}
Calculator
クラスを StatefulWidget
に変更し、createState
メソッドを追加しました。createState
メソッドは、_CalculatorState
オブジェクトを返します。_CalculatorState
クラスでは、電卓の状態を保持し、UIを構築します。
2. 数値と演算子の保持
次に、電卓の状態を保持するための変数を _CalculatorState
クラスに追加しましょう。
dart
class _CalculatorState extends State<Calculator> {
double? _currentValue = 0.0; // 現在の入力値
double? _previousValue; // 前回の入力値
String? _operator; // 選択された演算子
String _display = '0'; // 画面に表示する文字列
// ... 省略 ...
}
以下の4つの変数を追加しました。
_currentValue
: 現在入力されている数値を保持する_previousValue
: 演算子が選択されたときに、それまでに入力されていた数値を保持する_operator
: 選択された演算子(+、-、×、÷)を保持する_display
: 画面に表示する文字列を保持する
これらの変数を使って、電卓の状態を管理します。
コラム:Flutterの状態管理
Flutterで状態を管理する方法はいくつかあります。ここでは、主な状態管理の方法について簡単に説明します。
setState: ステートフルウィジェットの
State
オブジェクトで状態を管理する最も基本的な方法です。setState
メソッドを呼び出すと、Flutterに状態の変更を通知し、build
メソッドが再実行されてUIが更新されます。小規模なアプリや、ウィジェット内で閉じた状態を管理する場合に適しています。InheritedWidget: ウィジェットツリーの上位で状態を保持し、下位のウィジェットに状態を伝播させるための仕組みです。
InheritedWidget
を継承したクラスを作成し、状態を保持します。下位のウィジェットでは、BuildContext.dependOnInheritedWidgetOfExactType
メソッドを使って、InheritedWidget
にアクセスできます。状態の変更を通知するには、StatefulWidget
と組み合わせて使用します。Provider:
InheritedWidget
を使いやすくしたパッケージです。ChangeNotifier
と組み合わせて、状態の変更を効率的に通知できます。Provider
を使うことで、ウィジェットツリーの任意の場所から状態にアクセスできるようになります。中規模から大規模なアプリの状態管理に適しています。BLoC: ビジネスロジックとUIを分離するためのデザインパターンです。BLoCは、
Stream
を使って状態の変化を伝えます。UIはStream
を購読し、状態の変化に応じてUIを更新します。BLoCパターンを使うことで、状態管理のロジックをUIから分離し、テストしやすいコードを書くことができます。Riverpod:
Provider
をさらに改良したパッケージです。Provider
の欠点を克服し、より柔軟でテストしやすい状態管理を提供します。Riverpod
は、Provider
と同様にInheritedWidget
をベースにしていますが、Provider
よりもシンプルで強力なAPIを提供します。
どの状態管理の方法を選ぶかは、アプリの規模や複雑さ、開発者の好みなどによって異なります。電卓アプリのような小規模なアプリでは、setState
でも十分ですが、より複雑なアプリでは、Provider
や Riverpod
などの状態管理パッケージの利用を検討するとよいでしょう。