Dart

66. Flutterでアプリにbutton配列をランダムにシャッフルして表示させる

今回は久しぶりにFlutterでのアプリ開発になります。WWDC2020以来になります。

振り返りとして、これまでFlutterの章では1番から25番を順番にタップするアプリ「ナンバーズ」を開発してきました。前回の続きはだいぶ前になりますがこちらの記事でFlutterで配列データをシャッフルする方法について学びました。

また、今までで出来上がっているgif動画はこちらになります。

今回はListのシャッフル関数を使って1から25番のボタンのテキストをランダム配置させて、そのListをGridViewに渡す処理を実装しようと思います。

game_play_page.dart

/// See: https://stackoverflow.com/questions/13554129/list-shuffle-in-dart
  List _shuffle(List items) {
    var random = new Random();
    for (var i = items.length - 1; i > 0; i--) {
      var n = random.nextInt(i + 1);
      var temp = items[i];
      items[i] = items[n];
      items[n] = temp;
    }
    return items;
  }

上記の方法ではlistの配列を引数にとって配列データを渡すとランダム生成されたListヲ』取得する関数になります。

あとは同じクラスの中のbuild関数内でintの1~25のリストを生成して上記のランダム関数を呼び出します。

game_play_page.dart

@override
  Widget build(BuildContext context) {
    final list = List<int>.generate(25, (i) => i + 1);
    final randomList = _shuffle(list);

    return Container();
  }

このrandomListをゲームの盤面であるGridView.count()のchildrenに渡してやればいいだけです。

つまり、全体のソースコードは次のようになります。

game_play_page.dart

class GamePlayPage extends StatelessWidget {

  /// See: https://stackoverflow.com/questions/13554129/list-shuffle-in-dart
  List _shuffle(List items) {
    var random = new Random();
    for (var i = items.length - 1; i > 0; i--) {
      var n = random.nextInt(i + 1);
      var temp = items[i];
      items[i] = items[n];
      items[n] = temp;
    }
    return items;
  }

  void _updateCurrentNumber(BuildContext context, CounterModel provider) {
    if (provider.currentNumber >= 25) {
      Navigator.push(
        context,
        new MaterialPageRoute<Null>(
          settings: RouteSettings(name: Constants.clearRoute),
          builder: (BuildContext context) => ClearPage(),
        ),
      );
    }
    provider.updateCurrentNumber();
  }

  @override
  Widget build(BuildContext context) {
    final list = List<int>.generate(25, (i) => i + 1);
    final randomList = _shuffle(list);

    return ChangeNotifierProvider<CounterModel>(
      create: (context) => CounterModel(),
      child: Consumer<CounterModel>(
        builder: (context, provider, child) => Scaffold(
          body: Center(
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 20.0),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: <Widget>[
                      Padding(
                        padding: EdgeInsets.only(left: 20.0),
                        child: CurrentNumberText(),
                      ),
                      Container(
                        padding: EdgeInsets.only(right: 20),
                        child: Center(
                            child: TimerText()),
                      )
                    ],
                  ),
                  SizedBox(
                    height: 48.0,
                  ),
                  SizedBox(
                    height: 400.0,
                    child: GridView.count(
                        mainAxisSpacing: 8,
                        crossAxisSpacing: 8,
                        physics: const NeverScrollableScrollPhysics(),
                        crossAxisCount: 5,
                        children: List.generate(randomList.length, (index) {
                          final isCorrect = randomList[index] == provider.currentNumber;
                          return NumberButton(
                            number: randomList[index],
                            onPressed: () => _updateCurrentNumber(context, provider),
                            isOnTouch: isCorrect,
                          );
                        })),
                  )
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

import部分は省略しました。

この変更を加えてソースコードをビルドすると次のgif動画のようにボタンがランダムに表示されます。

はてなブログからwordpressに移行したことで綺麗なgif動画を表示できるようになりました。個人的にこれが一番移行して良かったと思います。

ここまで来るとゲームアプリっぽく見えてきますね。まだ、gifでは実装が終わってますが、Timer処理の部分の説明が残っていますがこちらは次回に繰り越します。

ABOUT ME
tamappe
都内で働くiOSアプリエンジニアのTamappeです。 当ブログではモバイルアプリの開発手法について紹介しています。メインはiOS、サブでFlutter, Android も対応できます。 執筆・講演のご相談は tamapppe@gmail.com までお問い合わせください。