Flutter Tips

トランプを表示してみよう

「ハートの2を探せゲーム」

※Scaffoldのbody引数に、Columnの代わりに「Wrap」を使用しています。これはRowと似ています(ウィジェットを横方向に並べます)が、画面の横幅の広さに応じてはみ出した場合にエラーとならず、次の段に移る特徴があります。
余力のある方は、RowとWrapを比較してみてください。

※Image.networkに新たな引数としてwidthを指定しています。widthは「横幅」を指定するもので、今回は各トランプカードの横幅を80に指定します。縦幅を指定するときは「height」を指定します。
・widthもheightも指定しない場合:元の画像の大きさがそのまま表示される
・横幅widthだけ指定した場合:縦幅は元の画像の縦横比を維持して、自動的に調整されます。
・縦幅heightだけ指定した場合:横幅は元の画像の縦横比を維持して、自動的に調整されます。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            GestureDetector(
              child: Image.network(
                "https://2nd-step.net/wp-content/uploads/2022/10/card_heart_01.png",
                width: 80,
              ),
              onTap: () {
                print("ハートの1を押したよ!");
              },
            ),
            GestureDetector(
              child: Image.network(
                "https://2nd-step.net/wp-content/uploads/2022/10/card_heart_02.png",
                width: 80,
              ),
              onTap: () {
                print("ハートの2を押したよ!");
                print("正解!!!!");
              },
            ),
            GestureDetector(
              child: Image.network(
                "https://2nd-step.net/wp-content/uploads/2022/10/card_heart_03.png",
                width: 80,
              ),
              onTap: () {
                print("ハートの3を押したよ!");
              },
            ),
            GestureDetector(
              child: Image.network(
                "https://2nd-step.net/wp-content/uploads/2022/10/card_spade_01.png",
                width: 80,
              ),
              onTap: () {
                print("スペードの1を押したよ!");
              },
            ),
            GestureDetector(
              child: Image.network(
                "https://2nd-step.net/wp-content/uploads/2022/10/card_spade_02.png",
                width: 80,
              ),
              onTap: () {
                print("スペードの2を押したよ!");
              },
            ),
          ],
        ),
      ),
    ),
  );
}

このプログラムにはいくつかの問題点があります。それは何でしょう?

〜シンキングタイム〜

GestureDetectorの部品化

トランプを13枚×4種類(スペード、クラブ、ハート、ダイヤ)=52枚を表示するために、GestureDetectorを52回コピー&ペーストするのは大変。。。

そこで、何度も似たような内容で登場するGestureDetectorのカタマリを部品化できないか?を検討します。

まずは、「それぞれのGestureDetectorで変わっている部分」を探します。

1つ目:URL内の「card_」の直後がハートであれば「heart」、スペードであれば「spade」を指定している

2つ目:URL内の2桁の連番がカードの数字を表している。1桁の場合は先頭に0がつく。

3つ目:onTapのprintの「〇〇の〜」の〇〇部分のマークがカードによって「ハート」、「スペード」など、異なる。

4つ目:onTapのprintの「○を押したよ!」の○にはカードの数字が入る。

5つ目:ハートの2の場合だけ、追加のprintが発生して「正解!!!!」と表示する。

これらを意識して、GestureDetectorを部品化します。

自分好みのカスタムWidgetを作る

GestureDetectorを自分好みのカスタムWidget化していきます。

void main(){ ~ }の外側に、以下のように記述します。

class 【新しく作るウィジェット名】 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return 【部品化するウィジェット】;
  }
}

トランプカードを表す「PlayingCard」ウィジェットを作るためには、以下のように記述します。

//PlayingCardウィジェット
class PlayingCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //繰り返し記述していたGestureDetectorウィジェットを部品化する
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_heart_01.png",
        width: 80,
      ),
      onTap: () {
        print("ハートの1を押したよ!");
      },
    );
  }
}

void main側からPlayingCardウィジェットを呼び出すためには、PlayingCard()と記述するだけです。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
          ],
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_heart_01.png",
        width: 80,
      ),
      onTap: () {
        print("ハートの1を押したよ!");
      },
    );
  }
}

しかしこれでは問題があります。PlayingCard()ではハートの1しか作ることができません。他のカードも作れるようにしたいですね。

PlayingCardウィジェットであらゆるカードを作れるようにする

先ほどの図を思い出してみます。

赤い丸が5か所ついていますので、まずはそこを考えます。

変数とは?

よくプログラミングの入門書では、「変数とは、データを保管するための箱です」という表現をされますが、この場面でも変数という考え方が役立ちます。

まず、先ほどの赤い丸の部分を、「【変数X】」に書き換えてみます。(一旦ハートの2は置いておきます)

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
          ],
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_【変数1】_【変数2】.png",
        width: 80,
      ),
      onTap: () {
        print("【変数3】の【変数4】を押したよ!");
      },
    );
  }
}

これで、変数が4つ必要なことが分かりましたが、変数1は「heart」の英語表記で、変数3は「ハート」の日本語表記なので、変数3は変数1で代用することとします。

同様に、変数4は変数2で代用することとします。すると、以下のようになります。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
          ],
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_【変数1】_【変数2】.png",
        width: 80,
      ),
      onTap: () {
        print("【変数1】の【変数2】を押したよ!");
      },
    );
  }
}

これで、必要な変数が2つに減りました。

次に、変数に名前をつけます。変数1がマークなので、英語で「mark」と名前を付けることにしましょう。同様に、変数2はカードの数字なので、英語で「number」と名前をつけることにしましょう。

これらは両方文字列中で使用されている「文字」なので、Dart言語では「String」という型(種類)の変数を使います。@overrideの前の行を開けて、「型名 変数名;」と変数の数だけ記述します。変数は好きなだけ作れます。

今回はすなわち、以下のようになります。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
          ],
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  String mark;   //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_【変数1】_【変数2】.png",
        width: 80,
      ),
      onTap: () {
        print("【変数1】の【変数2】を押したよ!");
      },
    );
  }
}

Dart言語では、文字列中に変数を埋め込むためには「${変数名}」と書きます。すなわち、以下のようになります。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
            PlayingCard(),
          ],
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  String mark;   //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_${mark}_${number}.png",
        width: 80,
      ),
      onTap: () {
        print("${mark}の${number}を押したよ!");
      },
    );
  }
}

コンストラクタとは

あと一歩で完成です!あとは、PlayingCardを生成する度にmarkとnumberを指定してもらう仕組みさえできれば解決です。そのためには、以下のように書きます。

ウィジェット名(this.引数名1, this.引数名2, ...);

また、呼び出し元(void main)に記載する時には、ウィジェット名(引数1, 引数2, ...)のように引数を指定します。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            //引数はコンストラクタに記載の順番、mark, numberの順に指定します
            PlayingCard("heart", "01"),
            PlayingCard("heart", "02"),
            PlayingCard("heart", "03"),
            PlayingCard("spade", "01"),
            PlayingCard("spade", "02"),
          ],
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  String mark;   //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  //コンストラクタ
  PlayingCard(this.mark, this.number);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_${mark}_${number}.png",
        width: 80,
      ),
      onTap: () {
        print("${mark}の${number}を押したよ!");
      },
    );
  }
}

ハートの2を判定しよう

残るは、ハートの2の判定です。要は、markが"heart"でかつ、numberが"02"の時に「正解!!!!」と表示させたいわけですね。

聞いたことがある方もいるかもしれませんが、「〇〇の場合は××する」ということを条件分岐と呼び、if文という文法で記載します。

if文の使い方

if(条件式){
【条件式が真(true)の場合の処理(何行書いてもよい)】
}

今回は複合条件として、「markが"heart"でかつ、numberが"02"」であるかを調べます。

あるデータとあるデータが一致するかどうかを調べるには、「==」という記号を使用します。

また、ある条件とある条件が両方とも成立するかどうかを調べるには、「&&」という記号を使用します。

すなわち、以下のように書きます。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: [
            //引数はコンストラクタに記載の順番、mark, numberの順に指定します
            PlayingCard("heart", "01"),
            PlayingCard("heart", "02"),
            PlayingCard("heart", "03"),
            PlayingCard("spade", "01"),
            PlayingCard("spade", "02"),
          ],
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  String mark;   //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  //コンストラクタ
  PlayingCard(this.mark, this.number);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_${mark}_${number}.png",
        width: 80,
      ),
      onTap: () {
        print("${mark}の${number}を押したよ!");
        if(mark == "heart" && number == "02"){
          print("正解!!!!");
        }
      },
    );
  }
}

PlayingCardがウィジェット化したことで、void main側からはマークと数字を指定するだけで、イラストもタップ時の操作も全てPlayingCard側に一任することができます。

void main側の記述がスッキリしたことがお分かりいただけるでしょう。これなら、何枚書いてもなんとかなりそうですね。

※今後も学習を継続する場合は、for文を用いた繰り返しを用いてPlayingCardウィジェットを効率よく作成することも可能ですので、是非チャレンジしてみてください。

応用編 カードの並び順をシャッフルしてみよう

毎回同じ順番にカードが並んでいても面白くありません。

mainではWrapウィジェットの子(children)として、PlayingCardのリストを作成しているのでしたね。

[
  //引数はコンストラクタに記載の順番、mark, numberの順に指定します
  PlayingCard("heart", "01"),
  PlayingCard("heart", "02"),
  PlayingCard("heart", "03"),
  PlayingCard("spade", "01"),
  PlayingCard("spade", "02"),
]

このリストも、丸ごと変数に格納することができます。

以下2通りの方法で書くことができます。

List<リスト化するウィジェット名> 変数名 = [データ1, データ2, ...];

var 変数名 = <リスト化するウィジェット名>[データ1, データ2, ...];

(varというキーワードは、リストだけでなくあらゆる型の変数を作ることができる汎用キーワードです。理解できなくてもOKです)

今回は、varを使用した書き方で書いてみます。void main(){の直後に、cardListという名前のリストを作成して、WrapのchildrenにはcardListを指定してみます。

import 'package:flutter/material.dart';

void main() {
  //カードリスト
  var cardList = <PlayingCard>[
    //引数はコンストラクタに記載の順番、mark, numberの順に指定します
    PlayingCard("heart", "01"),
    PlayingCard("heart", "02"),
    PlayingCard("heart", "03"),
    PlayingCard("spade", "01"),
    PlayingCard("spade", "02"),
  ];

  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: cardList,
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  String mark;   //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  //コンストラクタ
  PlayingCard(this.mark, this.number);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_${mark}_${number}.png",
        width: 80,
      ),
      onTap: () {
        print("${mark}の${number}を押したよ!");
        if(mark == "heart" && number == "02"){
          print("正解!!!!");
        }
      },
    );
  }
}

これで実行しても特にこれまでと変化がないのですが、変数にすることで便利な機能が使えるようになります。

【リストの変数】.shuffle();

たったこの1行を書くだけで、リストの並び順をシャッフルすることができます。

import 'package:flutter/material.dart';

void main() {
  //カードリスト
  var cardList = <PlayingCard>[
    //引数はコンストラクタに記載の順番、mark, numberの順に指定します
    PlayingCard("heart", "01"),
    PlayingCard("heart", "02"),
    PlayingCard("heart", "03"),
    PlayingCard("spade", "01"),
    PlayingCard("spade", "02"),
  ];

  //シャッフル
  cardList.shuffle();

  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: cardList,
        ),
      ),
    ),
  );
}

class PlayingCard extends StatelessWidget {
  String mark;   //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  //コンストラクタ
  PlayingCard(this.mark, this.number);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_${mark}_${number}.png",
        width: 80,
      ),
      onTap: () {
        print("${mark}の${number}を押したよ!");
        if(mark == "heart" && number == "02"){
          print("正解!!!!");
        }
      },
    );
  }
}

実行するたびに、並び順が変わるのが分かります。

例え並び順が変わったとしても、ひとつひとつはPlayingCardウィジェットなので、そこには絵柄に連動したmarkとnumberが内包されているので、クリックした時に想定通りに動作するわけです。(ハートの2がどの位置にあったとしても関係なく、ハートの2をタップしたら正解となる)

状態(State)を管理するStatefulWidget

次に、カードが「現在表である」「現在裏である」という状態を管理してみます。すなわち、「初期状態では裏となっていて、クリックすると表になる(めくれる)」ということを実現してみましょう。

これまでPlayingCardはStatelessWidget(State状態を、less管理しない、Widgetウィジェット)という分類で作っていたのですが、それをStatefulWidget(State状態を、ful管理する、Widgetウィジェット)に作り替える必要があります。

StatefulWidgetの使い方

StatefulWidgetは、次の2つをセットで使用します。

①StatefulWidget(ステートフルウィジェット)
②State(ステート)

書き方にはクセがありますが、ワンパターンなので「そういうもの」と認識すればOKです。

①StatefulWidgetを書く(StatelessWidgetと同様、void mainの外側に書きます)

class 【ステートフルウィジェット名】 extends StatefulWidget {
  【必要であれば、コンストラクタで受け取る用の変数(StatelessWidgetの時と同様)】
  【必要であれば、コンストラクタ(StatelessWidgetの時と同様)】

  @override
  【ステート名】 createState() => 【ステート名】();
}

②Stateを書く(同様に、void mainの外側に書きます)

class 【ステート名】 extends State<【ステートフルウィジェット名】> {
  【状態管理用変数(複数書いてもOK)】
  【状態管理用変数を変更する処理(複数書いてもOK)】

  @override
  Widget build(BuildContext context) {
    return 【部品化するウィジェット】;
  }
}

見るからに難しそうですが、順を追って解説します。

まず、StatelessWidgetの時はその内部にWidget build〜を書いていましたが、ステートフルのパターンではStatefulWidgetではなくState側にWidget buildを書きます

PlayingCardをステートフル化する

今回は、PlayingCardをステートフルに書き換えてみましょう。
※今までのPlayingCardをPlayingCardStateに書き換えるとやりやすいです。

以下のように名前を名前を付けることにします。
①StatefulWidget:PlayingCard
②State:PlayingCardState

まずは、各所の名前を埋めます。

①StatefulWidgetの方は、こうなります。
外部から受け取る変数(下記例ではmark、number)とコンストラクタについては、こちら側に記載します。

class PlayingCard extends StatefulWidget {
  String mark; //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  //コンストラクタ
  PlayingCard(this.mark, this.number);

  @override
  PlayingCardState createState() => PlayingCardState();
}

②Stateの方は、こうなります。
Widget buildはこちら側に記載します。
StatefulWidget側に記載している変数については、「widget.変数名」に書き換えることでアクセスできます。
「mark」→「widget.mark」
「number」→「widget.number」

class PlayingCardState extends State<PlayingCard> {

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_${widget.mark}_${widget.number}.png",
        width: 80,
      ),
      onTap: () {
        print("${widget.mark}の${widget.number}を押したよ!");
        if (widget.mark == "heart" && widget.number == "02") {
          print("正解!!!!");
        }
      },
    );
  }
}

お疲れ様でした!これでひとまずStatelessWidgetからStatefulWidgetに書き換えることができました!(動作は特に変わっていません)

StatefulWidgetでカードの表・裏を状態管理してみよう

これで土台が整いました。いよいよ、カードの裏・表を管理してみましょう。

ここでは、bool(ブール)という型の変数を使ってみます。bool型というのは2択を表すことができる概念で、「Yes・No」や「表・裏」など2択のデータを管理するのに使われます。

bool型では真を「true」、偽を「false」と呼びます。

「カードが既にめくれたか?」という意味を込めて、変数名「isFlipped」を作ってみます。
状態管理を行う変数は、State側に記載します。(この場合はPlayingCardState)
初期状態はfalse(めくれていない)、クリックしたらtrue(めくれた)にすることにしましょう。

そしてカードをタップした時(onTap)、isFlippedをfalseからtrueにします。この時、onTapで状態変更を行う部分を「setState」という処理で囲みます。

setState(() { 【状態変更を行う処理】 });

class PlayingCard extends StatefulWidget {
  String mark; //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  //コンストラクタ
  PlayingCard(this.mark, this.number);

  //PlayingCardStateコンストラクタの引数としてmark, numberを指定
  @override
  PlayingCardState createState() => PlayingCardState(mark, number);
}

class PlayingCardState extends State<PlayingCard> {
  //状態管理を行う変数isFlipped
  bool isFlipped = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        "https://2nd-step.net/wp-content/uploads/2022/10/card_${widget.mark}_${widget.number}.png",
        width: 80,
      ),
      onTap: () {
        
        //タップ時に状態変更(isFlippedを変更)
        setState(() {
          isFlipped = true;
        });

        print("${widget.mark}の${widget.number}を押したよ!");
        if (widget.mark == "heart" && widget.number == "02") {
          print("正解!!!!");
        }
      },
    );
  }
}

この処理によって、タップ時にisFlippedが変更されるようになりました。

あとは、isFlippedの状況に応じて画像を変更するだけです。

色々な書き方がありますが、今回は三項演算子(〜?〜:〜)という方法を使って記述してみます。

三項演算子の書き方

【bool型の変数や式】?【trueの場合の値】:【falseの場合の値】

今回は、Image.networkで読み込むURLを、isFlippedがtrueの場合とfalseの場合で分けたいです。

そこで、このように記述を変更します。

import 'package:flutter/material.dart';

void main() {
  //カードリスト
  var cardList = <PlayingCard>[
    //引数はコンストラクタに記載の順番、mark, numberの順に指定します
    PlayingCard("heart", "01"),
    PlayingCard("heart", "02"),
    PlayingCard("heart", "03"),
    PlayingCard("spade", "01"),
    PlayingCard("spade", "02"),
  ];

  //シャッフル
  cardList.shuffle();

  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      //MaterialAppのhome引数にScaffoldウィジェットを指定する
      home: Scaffold(
        //Scaffoldの引数1、appBar(タイトル領域)
        appBar: AppBar(title: Text("ハートの2を探せゲーム")),
        //Scaffoldの引数2、body(本文領域)
        body: Wrap(
          children: cardList,
        ),
      ),
    ),
  );
}

class PlayingCard extends StatefulWidget {
  String mark; //変数mark(文字列String型)
  String number; //変数number(文字列String型)

  //コンストラクタ
  PlayingCard(this.mark, this.number);

  @override
  PlayingCardState createState() => PlayingCardState();
}

class PlayingCardState extends State<PlayingCard> {
  //状態管理を行う変数isFlipped
  bool isFlipped = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Image.network(
        isFlipped
            ? "https://2nd-step.net/wp-content/uploads/2022/10/card_${widget.mark}_${widget.number}.png"
            : "https://2nd-step.net/wp-content/uploads/2022/10/card_back.png",
        width: 80,
      ),
      onTap: () {
        
        //タップ時に状態変更(isFlippedを変更)
        setState(() {
          isFlipped = true;
        });

        print("${widget.mark}の${widget.number}を押したよ!");
        if (widget.mark == "heart" && widget.number == "02") {
          print("正解!!!!");
        }
      },
    );
  }
}

おめでとうございます!これで裏面の状態、表面の状態を管理できるようになりました。だいぶゲームらしくなりましたね。

応用

・setState内、isFlipped = true; を isFlipped = !isFlipped;とすると、タップするたびに表と裏が入れ替わるようになります。

・慣れている方は、setStateをメソッドに切り出しても構いません。

・勝利条件を他のものに変更してみましょう。

おすすめの記事