Greg's blog

Creating a Wordle clone in Flutter

I recently started seeing tweets like the following show up on my timeline:

🟩⬜⬜⬜⬜
🟩🟩🟨⬜⬜
🟩🟩⬜🟨⬜
🟩🟩🟩🟩🟩

Wordle 186 4/6

I was intrigued by this cryptic code, so googled around for Wordle. It's a word game where your goal is to guess the hidden word in 6 tries. Every time you guess you're told whether the letter is in the word in the right spot (green square), in the word in the wrong spot (yellow square) or not in the word at all (grey square). The fun of it is that everyone gets the same puzzle every day, and there's only one puzzle per day. It's reminiscent of doing the daily crossword in the newspaper in that way.

I'm learning Flutter right now, so I thought I'd try making a Wordle clone.

Getting the word list

Wordle uses only 5 letter English words. To get a list, I found this site that included a large list of words. Not all were 5 letters, so I had to do a bit of cleaning. There are numerous ways to do this, but for simplicity I just opened node and read the contents of the file, then filtered out only the 5 letter words, then wrote that new list to another file. This still has words that would be unguessable in a game like this, as well as some naughty words, but it's fine for a prototype.

Laying out the widgets

There's two main parts of the Wordle game: the board which shows all of your guesses, and the keyboard. It's important to build our own keyboard rather than using the system keyboard because it will light up with the results of your guesses, which can help you get the right word.

The game board is a StatelessWidget that builds container with Columns and Rows, each containing a GameLetter. The GameLetter is another StatelessWidget that builds a Container to display a letter with the right colour background, depending on the guess.

The keyboard is similar. This is a StatefulWidget full of Columns and Rows, each containing a KeyboardButton. KeyboardButton is a StatefulWidget too, that builds a TextButton widget. Again, the KeyboardButton is similar to the GameLetter in that it displays a letter with a background dependent on your guesses.

Why did I pick StatefulWidgets for the keyboard but StatelessWidgets for the Game and GameLetters? The keyboard has interactivity, so the user will be able to change the game state by pressing buttons. To represent this in the app, I need to be able to call setState(() {...}) to tell Flutter to redraw the widget tree after my changes have been made. The Game and GameLetters really only needs to listen to the state and redraw when it changes, so those widgets can be stateless.

Keeping state

I followed the Provider pattern demonstrated in this article. It's similar to the Observable pattern I'm used to from working with Angular. Essentially I have one class that maintains my game state that I'm calling Wordle. In this pattern, this Wordle class extends ChangeNotifier. Other classes/widgets can subscribe to changes in the state by calling context.watch<Wordle>() in their build method. When I want my game state to update for all listening classes, I can call notifyListeners() in my Wordle class. The nice thing about this pattern is then all of my widgets get to be very dumb, and only need to draw themselves properly given some state, or call a method on my Wordle state.

There are only three methods in my Wordle class to update state. addLetter is called when a keyboard button is pressed, and adds the letter to the current guess. backspace just removes a letter from the current guess. submitGuess checks the current guess against the secret word, then updates each letter's state. I created an enum for tracking LetterState with the values unknown, notInWord, wrongSpot and correctSpot. Then I keep a list of each guess's letter states, and one for each letter to use for the keyboard. This way we can update the keyboard separately from the previous guesses when you move a right letter in the wrong spot to the right spot, for example.

Next steps

So far the app is working well. To get it to be a full clone of Wordle, I'll need to add some additional features to track player's statistics for win percentage, and which guess they get the word on. I'll also need to update the app so a new word is selected every day. Both of these features can be added using the SharedPreferences library. There are also some additional rules to Wordle that I haven't included yet, such as your guess must be a word in the wordlist (you can't guess ABCDE just to find letters, for instance). Of course, to make this production-ready, it should be covered by tests too.

I'm enjoying working with Flutter too. It makes it really easy to lay out a view, and it's nice not having to switch contexts between HTML, CSS and JavaScript, because Flutter's "everything is a widget" philosophy means I'm only working in Dart. Take a look at the repo if you're interested.

#flutter #wordle