Skip to content

電卓の機能を実装する(前編)

公開日:January 31, 2025更新日:January 31, 2025
FlutterDartCoding📄

前回のセクションでは、電卓アプリの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で状態を管理する方法はいくつかあります。ここでは、主な状態管理の方法について簡単に説明します。

  1. setState: ステートフルウィジェットの State オブジェクトで状態を管理する最も基本的な方法です。setState メソッドを呼び出すと、Flutterに状態の変更を通知し、build メソッドが再実行されてUIが更新されます。小規模なアプリや、ウィジェット内で閉じた状態を管理する場合に適しています。

  2. InheritedWidget: ウィジェットツリーの上位で状態を保持し、下位のウィジェットに状態を伝播させるための仕組みです。InheritedWidget を継承したクラスを作成し、状態を保持します。下位のウィジェットでは、BuildContext.dependOnInheritedWidgetOfExactType メソッドを使って、InheritedWidget にアクセスできます。状態の変更を通知するには、StatefulWidget と組み合わせて使用します。

  3. Provider: InheritedWidget を使いやすくしたパッケージです。ChangeNotifier と組み合わせて、状態の変更を効率的に通知できます。Provider を使うことで、ウィジェットツリーの任意の場所から状態にアクセスできるようになります。中規模から大規模なアプリの状態管理に適しています。

  4. BLoC: ビジネスロジックとUIを分離するためのデザインパターンです。BLoCは、Stream を使って状態の変化を伝えます。UIは Stream を購読し、状態の変化に応じてUIを更新します。BLoCパターンを使うことで、状態管理のロジックをUIから分離し、テストしやすいコードを書くことができます。

  5. Riverpod: Provider をさらに改良したパッケージです。Provider の欠点を克服し、より柔軟でテストしやすい状態管理を提供します。Riverpod は、Provider と同様に InheritedWidget をベースにしていますが、Provider よりもシンプルで強力なAPIを提供します。

どの状態管理の方法を選ぶかは、アプリの規模や複雑さ、開発者の好みなどによって異なります。電卓アプリのような小規模なアプリでは、setState でも十分ですが、より複雑なアプリでは、ProviderRiverpod などの状態管理パッケージの利用を検討するとよいでしょう。