Flutter Wordle Replica

Flutter Wordle Replica

Apk file

https://drive.google.com/file/d/1YkVqUanmtEAz2e5_OQYauloGWin-RyKV/view?usp=sharing

Let's get started! wordle_pic1.png

Screenshot_1648076087.png

Screenshot_1648076842.png

main.dart

import 'package:flutter/material.dart';

import 'pages/home/home.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Wordle Replica',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(title: 'Wordle Replica'),
    );
  }
}

constants.dart

import 'package:flutter/material.dart';

const words = [
  'SNACK',
  'PIZZA',
  'QUICK',
  'CHECK',
  'EQUIP',
  'PLAZA',
  'ENJOY',
  'HAPPY',
  'ZEBRA',
  'JUDGE',
  'ADULT',
  'APPLE',
  'AWARD',
  'BIRTH',
  'BLOOD',
  'DREAM',
  'DRINK',
  'ENEMY',
  'ERROR',
  'BLAME',
  'ALIVE',
  'BRAVE',
  'OTHER',
  'CROSS',
  'EARTH',
  'FOCUS',
  'GLASS',
  'HOTEL',
  'IMAGE',
  'MONEY',
  'NIGHT',
  'NORTH',
  'PARTY',
  'QUEEN',
  'ROUTE',
  'SCENE',
  'SHIRT',
  'SLEEP',
  'SOUTH',
  'STAGE',
  'SUGAR',
  'TABLE',
  'TOWER',
  'TOTAL',
  'TRUTH',
  'THING',
  'WOMAN',
  'WATER',
  'WATCH',
];

const alphabet = [
  'A',
  'B',
  'C',
  'D',
  'E',
  'F',
  'G',
  'H',
  'I',
  'J',
  'K',
  'L',
  'M',
  'N',
  'O',
  'P',
  'Q',
  'R',
  'S',
  'T',
  'U',
  'V',
  'W',
  'Y',
  'X',
  'Z',
];

const Color letterButtonDefaultColor = Colors.blueGrey;
const Color letterButtonMatchColor = Colors.green;
const Color letterButtonHalfMatchColor = Colors.orange;

final letterBoxWaitInputColorTween = TweenSequence([
  TweenSequenceItem(
      tween: ColorTween(begin: letterButtonDefaultColor, end: Colors.blue),
      weight: 1),
  TweenSequenceItem(
      tween: ColorTween(begin: Colors.blue, end: letterButtonDefaultColor),
      weight: 1),
]);

models/letter_box_model.dart

import 'package:flutter/material.dart';

class LetterBoxModel {
  String letter;
  Color color;
  int orderInFive;

  LetterBoxModel(
      {required this.letter, required this.color, required this.orderInFive});
}

pages/home/components/letter_box.dart

import 'package:flutter/material.dart';

class LetterBox extends StatelessWidget {
  final String letter;
  final Color color;
  final VoidCallback onTap;

  const LetterBox(
      {Key? key,
      required this.letter,
      required this.color,
      required this.onTap})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        color: color,
        alignment: Alignment.center,
        child: Text(
          letter,
          style: const TextStyle(color: Colors.white),
        ),
      ),
    );
  }
}

pages/home/home.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:wordle/constants.dart';
import 'package:wordle/models/letter_box_model.dart';
import 'package:wordle/pages/home/components/letter_box.dart';
import 'package:wordle/pages/input/input_page.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  final List<List<LetterBoxModel>> _letterBoxModelMatrix = [];
  final List<String> _selectedLetters = [];

  late final AnimationController _animController;
  late final Animation _letterBoxWaitInputColorAnim;

  int _currentRowIndex = 0;
  int _currentColumnIndex = 0;
  int _score = 300;
  bool _showScore = false;
  bool _gameOver = false;
  String _word = '';
  String _enteredWord = '';

  @override
  void dispose() {
    _animController.dispose();

    super.dispose();
  }

  @override
  void initState() {
    _animController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 500));

    _letterBoxWaitInputColorAnim = letterBoxWaitInputColorTween.animate(
        CurvedAnimation(parent: _animController, curve: Curves.easeInOut));

    _initLetterBoxModelMatrix();
    _initWord();

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              const Spacer(),
              Text('Wordle Replica',
                  style: Theme.of(context).textTheme.headline5),
              const SizedBox(height: 25),
              SizedBox(width: 300, height: 300, child: _buildLetterBoxes()),
              const SizedBox(height: 25),
              IconButton(
                icon: const Icon(Icons.refresh),
                onPressed: () {
                  _resetGame();
                },
              ),
              if (_showScore)
                const SizedBox(
                  height: 25,
                ),
              if (_showScore)
                Text(
                  'SCORE: $_score',
                  style: const TextStyle(fontWeight: FontWeight.bold),
                ),
              const Spacer(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildLetterBoxes() {
    return Column(
      children: _letterBoxModelMatrix
          .asMap()
          .entries
          .map<Widget>((entry) => Expanded(
                child: Row(
                  children: entry.value.map<Widget>((model) {
                    Widget letterBox = LetterBox(
                        letter: model.letter,
                        color: model.color,
                        onTap: () => _onTapLetterButton(model, entry.key));

                    if (_currentRowIndex == entry.key &&
                        _currentColumnIndex == model.orderInFive) {
                      letterBox = AnimatedBuilder(
                        animation: _letterBoxWaitInputColorAnim,
                        builder: (context, child) {
                          return LetterBox(
                            color: _letterBoxWaitInputColorAnim.value,
                            letter: model.letter,
                            onTap: () => _onTapLetterButton(model, entry.key),
                          );
                        },
                      );

                      _animController.repeat();
                    }

                    return Expanded(
                        child: Padding(
                      padding: const EdgeInsets.all(4.0),
                      child: letterBox,
                    ));
                  }).toList(),
                ),
              ))
          .toList(),
    );
  }

  Future<void> _onTapLetterButton(
      LetterBoxModel model, int letterButtonRowIndex) async {
    if (letterButtonRowIndex == _currentRowIndex &&
        _currentColumnIndex == model.orderInFive &&
        model.letter.isEmpty &&
        !_gameOver) {
      final letter = await Navigator.push(
          context,
          MaterialPageRoute(
              fullscreenDialog: true,
              builder: (context) =>
                  InputPage(selectedLetters: _selectedLetters)));

      setState(() {
        model.letter = letter;
        _enteredWord += letter;
        _selectedLetters.add(letter);
        _currentColumnIndex++;

        if (_word.contains(letter)) {
          model.color = letterButtonHalfMatchColor;

          if (_word[model.orderInFive] == letter) {
            model.color = letterButtonMatchColor;
          }
        }

        if (_word == _enteredWord) {
          _showScore = true;
          _gameOver = true;

          if (_currentRowIndex != 0) {
            _score -= _currentRowIndex * 5 * 10;
          }
        }

        if (model.orderInFive == 4) {
          _currentRowIndex++;
          _currentColumnIndex = 0;
          _enteredWord = '';
        }
      });
    }
  }

  void _initLetterBoxModelMatrix() {
    if (_letterBoxModelMatrix.isNotEmpty) {
      _letterBoxModelMatrix.clear();
    }

    for (int r = 0; r < 6; r++) {
      _letterBoxModelMatrix.add(<LetterBoxModel>[]);

      for (int c = 0; c < 5; c++) {
        _letterBoxModelMatrix[r].add(LetterBoxModel(
            letter: '', orderInFive: c, color: letterButtonDefaultColor));
      }
    }
  }

  void _initWord() {
    var rnd = Random();
    _word = words[rnd.nextInt(words.length)];
  }

  void _resetGame() {
    setState(() {
      _currentColumnIndex = 0;
      _currentRowIndex = 0;
      _enteredWord = '';
      _selectedLetters.clear();
      _score = 300;
      _showScore = false;
      _gameOver = false;
      _initLetterBoxModelMatrix();
      _initWord();
    });
  }
}

pages/input/input_page.dart

import 'package:flutter/material.dart';
import 'package:wordle/constants.dart';

class InputPage extends StatelessWidget {
  final List<String> selectedLetters;

  const InputPage({Key? key, required this.selectedLetters}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Select a letter'),
      ),
      body: _buildLetterButtons(context),
    );
  }

  GridView _buildLetterButtons(BuildContext context) {
    return GridView.count(
      crossAxisCount: MediaQuery.of(context).size.width < 426 ? 5 : 10,
      mainAxisSpacing: 8,
      crossAxisSpacing: 8,
      padding: const EdgeInsets.all(16),
      children: alphabet.map<Widget>((a) => _letterButton(context, a)).toList(),
    );
  }

  GestureDetector _letterButton(BuildContext context, String a) {
    return GestureDetector(
      onTap: () => _onTapLetter(context, a),
      child: Container(
        alignment: Alignment.center,
        color: selectedLetters.contains(a) ? Colors.black : Colors.orange,
        child: Text(
          a,
          style:
              const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
        ),
      ),
    );
  }

  void _onTapLetter(BuildContext context, String letter) {
    Navigator.pop(context, letter);
  }
}