Flutter와 Firebase를 사용하여 수학 게임을 만든 방법

내가 현재 Flutter를 배우고 있다는 것을 알고 계실 것입니다. 저는 YouTube 튜토리얼을 거의 보지 않았고 제가 배운 내용에 대한 게시물을 거의 쓰지 않았으며 #30DaysOfFlutter에 가입했습니다. 나는 실제로 무언가를 구현함으로써 학습 여정을 다음 단계로 끌어올리기로 결정했습니다.




BrainTrainer는 30초 동안 최대한 많은 문제를 풀어야 하는 수학 게임입니다. 가장 높은 점수는 앱 바에 표시되고 Firebase에 저장됩니다.



게임은 총 2개의 화면으로 구성되어 있습니다. main.dart에서 표시할 화면을 결정하기 위해 firebase 사용자를 관찰합니다.


class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final firebaseUser = context.watch<User>();
    return Scaffold(
        appBar: AppBar(
          title: Text('BrainTrainer'),
        ),
        body: firebaseUser != null ? loadGameScreen(context) : LoginPage());
  }

  Future<Object> loadGameScreen(BuildContext context) {
    Future.delayed(Duration.zero, () {
      return Navigator.of(context).pushNamedAndRemoveUntil(
          GameScreen.routeName, (Route<dynamic> route) => false);
    });
  }
}


우리는 Firebase를 사용하여 (Apple 로그인)을 활성화하기 위해 공급자를 사용하고 있습니다. 그것에 대한 내 이전 게시물을 확인하십시오.





우리는 게임에서 몇 가지 위젯을 사용하고 있습니다.

ActionButtons Widget: 게임을 시작하기 위한 재생 버튼을 표시하는 곳입니다. 또한 선택한 답변이 올바른지 또는 지금인지 플레이어에게 표시하는 데 사용합니다.


import 'package:brain_trainer_app/models/game.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class ActionButtons extends StatefulWidget {
  const ActionButtons({
    Key key,
  }) : super(key: key);

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

class _ActionButtonsState extends State<ActionButtons> {
  @override
  Widget build(BuildContext context) {
    final _game = Provider.of<Game>(context);
    return SizedBox(
      height: 100,
      child: GestureDetector(
        onTap: () {
          _game.playTheGame();
        },
        child: Card(
          margin: EdgeInsets.all(10),
          elevation: 8,
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(10),
              boxShadow: [
                BoxShadow(color: Colors.white, spreadRadius: 3),
              ],
            ),
            padding: EdgeInsets.all(10),
            child: Consumer<Game>(
              builder: (context, game, child) {
                return Image.asset(
                  _game.actionButtonImage,
                  fit: BoxFit.cover,
                );
              },
            ),
          ),
        ),
      ),
    );
  }
}



AnswerItem 위젯: 이것은 플레이어가 선택할 수 있는 숫자를 표시하는 그리드의 항목을 나타냅니다. 그 항목 중 하나가 정답이 될 것입니다.


import 'package:brain_trainer_app/models/game.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class AnswerItem extends StatelessWidget {
  final Answer answer;
  final int index;

  static const _answercolot = [
    Color.fromRGBO(224, 81, 98, 1),
    Color.fromRGBO(84, 160, 86, 1),
    Color.fromRGBO(68, 150, 224, 1),
    Color.fromRGBO(111, 64, 222, 1),
  ];

  const AnswerItem({Key key, this.answer, this.index}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final _game = Provider.of<Game>(context);
    return ClipRRect(
      borderRadius: BorderRadius.circular(10),
      child: GridTile(
        child: GestureDetector(
          onTap: () {
            _game.answerSelected(this.answer);
          },
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(10),
              color: _answercolot[this.index],
              boxShadow: [
                BoxShadow(color: Colors.white, spreadRadius: 3),
              ],
            ),
            padding: EdgeInsets.all(10),
            child: Center(
                child: Text(
              answer.value.toString(),
              style: TextStyle(
                  fontSize: 24,
                  fontStyle: FontStyle.italic,
                  fontWeight: FontWeight.bold),
            )),
          ),
        ),
      ),
    );
  }
}



AnswersGrid 위젯: 4개의 AnswerItem 위젯으로 구성된 그리드입니다.


import 'package:brain_trainer_app/models/game.dart';
import 'package:brain_trainer_app/widgets/answer_item.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class AnswersGrid extends StatelessWidget {
  @override
  @override
  Widget build(BuildContext context) {
    final gameData = Provider.of<Game>(context);
    final answers = gameData.answers;
    return GridView.builder(
        padding: const EdgeInsets.all(10.0),
        itemCount: answers.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            childAspectRatio: 3 / 2,
            crossAxisSpacing: 10,
            mainAxisSpacing: 10),
        itemBuilder: (BuildContext context, int index) {
          return AnswerItem(
            answer: answers[index],
            index: index,
          );
        });
  }
}



GameAds 위젯: Google AdMob 광고를 표시하는 곳입니다.


import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

class GameAds extends StatefulWidget {
  @override
  _GameAdsState createState() => _GameAdsState();
}

class _GameAdsState extends State<GameAds> {
  BannerAd _bannerAd;

  @override
  void initState() {
    super.initState();
    MobileAds.instance.initialize();
    _bannerAd = BannerAd(
      size: AdSize.banner,
      adUnitId: BannerAd.testAdUnitId,
      listener: AdListener(),
      request: AdRequest(),
    );
    _bannerAd..load();
  }

  @override
  void dispose() {
    super.dispose();
    _bannerAd.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(height: 50, child: AdWidget(ad: _bannerAd));
  }
}



GameConfetti 위젯: 이 재미있는 위젯을 사용하여 플레이가 게임을 마칠 때 축하 애니메이션을 표시합니다.



import 'package:brain_trainer_app/models/game.dart';
import 'package:confetti/confetti.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rflutter_alert/rflutter_alert.dart';

class GameConfetti extends StatefulWidget {
  @override
  _GameConfettiState createState() => _GameConfettiState();
}

class _GameConfettiState extends State<GameConfetti> {
  ConfettiController _controllerCenter;

  @override
  void initState() {
    _controllerCenter =
        ConfettiController(duration: const Duration(seconds: 10));
    _controllerCenter.play();
    super.initState();
  }

  @override
  void dispose() {
    _controllerCenter.dispose();
    super.dispose();
  }

  _showAlert(context) {
    Future.delayed(Duration.zero, () async {
      final String _gameMsg =
          Provider.of<Game>(context, listen: false).completionMsg;
      var alertStyle = AlertStyle(
        animationType: AnimationType.fromTop,
        isCloseButton: false,
        isOverlayTapDismiss: false,
        descStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
        descTextAlign: TextAlign.start,
        animationDuration: Duration(milliseconds: 400),
        overlayColor: Colors.transparent,
        alertBorder: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(0.0),
          side: BorderSide(
            color: Colors.grey,
          ),
        ),
        titleStyle: TextStyle(
          color: Colors.green,
        ),
        alertAlignment: Alignment.center,
      );

      Alert(
        style: alertStyle,
        context: context,
        type: AlertType.success,
        title: "Well done",
        desc: _gameMsg,
        buttons: [
          DialogButton(
            child: Text(
              "Brilliant!!!",
              style: TextStyle(color: Colors.white, fontSize: 16),
            ),
            onPressed: () => Navigator.pop(context),
            width: 120,
          )
        ],
      ).show();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.topCenter,
      child: ConfettiWidget(
          confettiController: _controllerCenter,
          blastDirectionality: BlastDirectionality
              .explosive, // don't specify a direction, blast randomly
          shouldLoop: false,
          gravity: 0.5,
          emissionFrequency: 0.05,
          numberOfParticles:
              20, // start again as soon as the animation is finished
          colors: const [
            Colors.green,
            Colors.blue,
            Colors.pink,
            Colors.orange,
            Colors.purple
          ], // manually specify the colors to be used
          child: _showAlert(context)
          ),
    );
  }
}



TimerQuestionScoreRow 위젯: 여기에서 30초 타이머, 수학 문제를 보여주고 플레이어 점수/


import 'package:brain_trainer_app/models/game.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class TimerQuestionScoreRow extends StatefulWidget {
  const TimerQuestionScoreRow({
    Key key,
  }) : super(key: key);

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

class _TimerQuestionScoreRowState extends State<TimerQuestionScoreRow> {
  @override
  Widget build(BuildContext context) {
    final _game = Provider.of<Game>(context);
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Expanded(
          flex: 3,
          child: SizedBox(
            height: 100,
            child: Card(
              margin: EdgeInsets.all(10),
              elevation: 8,
              child: Container(
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10),
                  color: Color.fromRGBO(255, 152, 0, 1),
                  boxShadow: [
                    BoxShadow(color: Colors.white, spreadRadius: 3),
                  ],
                ),
                padding: EdgeInsets.all(10),
                child: Consumer<Game>(
                  builder: (context, game, child) {
                    return Center(
                        child: Text(
                      '${game.timer}s',
                      style:
                          TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ));
                  },
                ),
              ),
            ),
          ),
        ),
        Expanded(
          flex: 4,
          child: SizedBox(
            height: 100,
            child: Card(
              margin: EdgeInsets.all(10),
              elevation: 8,
              child: Container(
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10),
                  boxShadow: [
                    BoxShadow(color: Colors.white, spreadRadius: 3),
                  ],
                ),
                padding: EdgeInsets.all(10),
                child: Consumer<Game>(
                  builder: (context, game, child) {
                    return Center(
                        child: Text(
                      '${game.question}',
                      style:
                          TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ));
                  },
                ),
              ),
            ),
          ),
        ),
        Expanded(
          flex: 3,
          child: SizedBox(
            height: 100,
            child: Card(
              margin: EdgeInsets.all(10),
              elevation: 8,
              child: Container(
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10),
                  color: Color.fromRGBO(3, 169, 244, 1),
                  boxShadow: [
                    BoxShadow(color: Colors.white, spreadRadius: 3),
                  ],
                ),
                padding: EdgeInsets.all(10),
                child: Consumer<Game>(
                  builder: (context, game, child) {
                    return Center(
                        child: Text(
                      '${game.score}',
                      style:
                          TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ));
                  },
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}




게임은 세 가지 모델을 사용합니다.

게임: 게임을 설정하는 기본 모델입니다. 여기에서 선택한 답변을 확인하고 타이머를 추적합니다.

답변: 답변 값을 나타내는 간단한 모델입니다.

플레이어: Firebase에서 가져온 플레이어 정보가 있는 곳이며 최고 점수를 추적하는 데도 사용합니다.

DataRepository 클래스를 사용하여 데이터를 검색하고 저장합니다.


import 'package:brain_trainer_app/models/player.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class DataRepository {
  final CollectionReference collection =
      FirebaseFirestore.instance.collection('players');

  Future<void> addPlayer(Player player) {
    return collection.doc(player.uid).set(player.toJson());
  }

  updatePlayer(Player player) async {
    await collection.doc(player.uid).update(player.toJson());
  }

  Future<Player> getPlayer(String uid) async {
    var doc = await collection.doc(uid).get();
    if (doc.data() == null) {
      return null;
    }
    return Player.fromJson(doc.data());
  }
}





이 게임은 Apple Storehere 및 Google Playhere에 있습니다.

여기에서 코드를 확인하세요


오프라인 프로그래머 / brain_trainer_flutter






brain_trainer_app


수학을 즐기십니까? 게임을 즐기십니까? 긴장을 풀고 두뇌를 훈련하고 싶습니까? 재미를 위해 이 놀라운 앱을 사용해 보세요 – Brain Trainer.
Brain Trainer는 기억력, 속도 및 주의력을 훈련하도록 설계된 간단한 수학 기반 게임으로 구성되어 있습니다. 바쁜 일정에서 휴식을 취하고 Brain Trainer를 사용하여 휴식을 취하고 수학을 연습하십시오.


View on GitHub


Follow me on for more tips about #coding, #learning, #technology...etc.

Check my Apps on Google Play

Cover image Amol Tyagi on Unsplash

좋은 웹페이지 즐겨찾기