FlutterのCasual Game ToolKitを使ってみる! その1

ブログ

以下のGithubのソースコードを流用してゲームを作ってみましょう。

samples/game_template at main · flutter/samples
AcollectionofFlutterexamplesanddemos.Contributetoflutter/samplesdevelopmentbycreatinganaccountonGitHub.

と思って読んでみたのはいいのですが、これがなかなかソースコードの量が多く解析に苦労しています。。

こちとらFlutterもDartも始めて1ヶ月も経ってないんじゃ!!

というわけで、まずは簡単なところから。

Flutterのflutter createしたときに作成されるソースコードをFlutter Casual Game ToolKitの内容に改変してみましょう。その方が早い気がするので。

元となるソースコード

flutter createしたときに作られるソースコードの全容を載せておきます。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), 
    );
  }
}

このソースコードをFlutter Casual Game ToolKitに作ってみる感じですね。

で、状態管理はProviderパッケージを使っているようなので、このソースコードをProviderライブラリで書き換えてみましょう。

ソースコードをいじる

Providerは割と有名なので、他の記事を参考にしていただくとして早速内容を書き換えてみましょう(pubspec.yamlの記述は省略)。

まずは一つのファイルにクラスが2つ・3つあるのは個人的に気分が悪いので、MyHomePage・_MyHomePageStateクラスを別ファイルに移動してみましょう。

以下のようになりました。

main.dart

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

MyHomePage.dart

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), 
    );
  }
}

これだけでもだいぶ分かりやすくなったのではないでしょうか?

次に、MyHomePage.dartの内容をいじってみましょう。

こちらはStatefulWidgetのままなので、こちらをProviderパッケージを利用した内容に書き換えてみます。

その事前段階として、setStateされている変数をProviderが状態管理するstateとして切り出しましょう。以下の部分になります。

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

この切り出した箇所をオブジェクト指向プログラミングっぽく(?)1ファイルに切り出せたら理想ですね。この方針で、内容を書き換えてみましょう。

切り出した部分は削除して、以下のファイルを作成してみました。

counter_state.dart

class CounterState extends ChangeNotifier {
  int _counter = 0;

  void incrementCounter() {
    _counter++;
    notifyListeners();
  }

  int get() {
    return _counter;
  }
}

setStateしている箇所はincrement()関数で置き換えつつ、外部からアクセスできるようにget()関数を追加してみました。

管理したい状態が明確になるので、このようにファイルを切り出すのは積極的にやっていきたいものです。

続いて、MyHomePage.dartファイルをCounterStateを使うように書き換えてみましょう。

MyHomePage.dart

import 'package:tic_tac_toe_v2/counter/counter_state.dart';

class MyHomePage extends StatelessWidget { <-変更
  const MyHomePage({super.key, required this.title});
  final String title;
  // _MyHomePageStateクラスは削除
  @override
  Widget build(BuildContext context) {
    var counter = context.watch<CounterState>(); <-追加
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              counter.get().toString(), <-変更
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.incrementCounter(); <-変更
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

context.watchで変数をwatchできるようにした上で、counter変数を更新してみました。

復習ですが、ProviderパッケージはChangeNotifierを継承したクラスでnotifyListener()すれば、ウィジェットの再ビルドをしてくれます。

これでProviderパッケージを使用したソースコードにリファクタリングできました。

まとめ

事前準備として、既存のソースコードをリファクタリングしてみました。

次回、早速Flutter Casual Game ToolKitの内容を使ってこのソースコードをさらに書き換えしてみましょう。

具体的にはshared_preferencesパッケージを導入してみます。

Flutter Casual Game ToolKitの状態管理として、Providerパッケージと合わせshared_preferencesパッケージの2段構えで行っているようなのです。

内容的にはローカルストレージにもデータを残しておくことで、アプリを終了しても引き続き状態を保っておけるようにしているものと思われます。

確かにめっちゃ大事なところですね。

状態管理の手法を確立したら、ルーティングの実装を行って、他の主要パッケージ(Crashlytics・AudioController)の実装を行なっていき、本格的なアプリ作成の準備を行なっていきます。

乞うご期待。

コメント