At this point you have followed the points outlined above. You have completed several tutorials. You now have an understanding of state management in Flutter and how to build a variety of user interfaces. For me this was a break in the mold as Blackberry, Android, iOS and Flex architect state and UI differently. Having these concepts down makes the following steps easier. You have a game that you want to build that falls into a niche category and some mechanics that you must ship in order to have a fun game (your minimal viable product!). Do not worry about UI, you can generate placeholders in a later step. The very first thing I advise you do when starting down the path of actually writing game code is to invest in unit tests. For the following weeks and maybe even months, do not even deploy code to a simulator.

Build your game engine.

There is something beautiful about a well made and even better tested game engine. In the case of a 2D game, you have sprite positioning, actions that represent user actions, outputs from that game engine that eventually tie into UI. This separation of concerns is paramount! Your game engine is known as “logic”. UI should not contain any logic. I teach my team a similar practice using Android Architecture Components. They have now coined the phrase “bob-logic” as many of these game principles I am teaching you now are similar to the approach we take for our client. Separation of concerns is important to remember because at the end of the day unit tests always run faster than instrumentation tests. In the case of Flutter, this is where you will find resounding success. I have been so very pleased with writing unit tests in Dart. They are concise. They run fast. They validate your engine is running smooth and operating as expected. Now mind you, UI can still exist in a unit test. Though not as pretty and often forgotten about. I am talking about console output!

Unit tests in Dart are concise and easy to create

Enhanced with console output.

Now we can visualize the state of our game data and engine

One thing I have noticed that causes a minor issue in unit tests is printing newlines

\r . Still implement toString for other debugging purposes, just know that newline characters may not appear in your test. Newlines will terminate output. Instead, when printing to console, write a separate toConsole method. For each line you want to print in console invoke println("some string"); this will guarantee that all the output is displayed in a test.

Because we are so focused on writing unit tests and not UI, we can quickly iterate over all of those fun game mechanics. Personally, this is the most fun part of writing a game. As mentioned previously, this is where we focus on writing efficient algorithms. Validating that the engine is working properly and that when we hook up UI click listeners to these actual actions the only margin of error exists in UI code. The engine has already been validated!

In the title I am working on currently, there are 146 unit tests. I am now just starting on UI code. Code coverage is not a metric I value near as much as valuable unit tests that assert business cases are operating as expected. For instance, a test that validates that a sprite moves to the correct location given some input is highly valuable. A test that covers all code paths only prevents further changes to that algorithm. Though one paradigm can represent the other, they are not mutually exclusive.

Rewinding a bit, you might be wondering how emitted events from a game engine tie into Flutter UI. You do not need Redux for this! Your game engine runs all of its logic synchronous, it does not need to know about threading at all. This makes writing unit tests significantly more simple! You can wrap jobs in async later when working with UI integration. Knowing this, architect your game engine to allow inputs to be translated to events. In this case, annotate your Dart methods to output a List of dynamic .

Flutter prefers comments are composed with three forward slashes `///`. Android Studio running the Flutter integration will generate more comments each time you start writing a new line when following this convention.

As you can see in this code sample, our game engine this has blocks stowed in an array. We initiate the method with an AvailableMove . This simple data class contains the coordinates of the location the user would like to move a particular block. The output is a List<dynamic> because there are many types of outputs that can be resolved as a result of a change to the game board. For instance, user moves a block east, that block will then have to actually move east in the UI. Upon moving east that block might fall off a ledge. Falling is yet another output. After that block falls some other change might occur. dynamic allows for us to output a variety of data classes that we can later type check. Without going into too much detail, you can begin to see how this lends to unit testing. We can now setup our engine in a particular state, commit-changes to the board, and expect very specific outputs in some linear order. This again, is a valuable unit test. When it comes time to tie this into our UI, we can animate those changes and resolve the queue of outputs from the game engine. The beauty here is that since we have a queue of actions to resolve, the UI can be built to execute them over any span of time. Taking the time to carefully orchestrate fluid animations that would otherwise be difficult to encapsulate in a game engine. Remember, our game engine is concerned mostly with core game logic. How we interpret that logic can be expanded upon later and wrapped in non-UI threads. This lends perfectly to writing tests.

There remains two big algorithms that I just can not wait to build. I actually lose sleep thinking about them because they are going to be so much work and fun to figure out. At this time I am thinking that I will build them after visiting some UI implementation as they do not impact game logic.