Reimplementing the Deal-O-Round game in Flutter - part 2
Sep 2, 2020
Earlier this year I mentioned multiple times that I’ll reimplement a game in Flutter. In my previous blog post I recounted the decisions and the technological stack of the original project. I enjoyed Flutter when I competed at the Flutter Clock contest and I saw that it could be a viable technology to carry on the legacy of Deal-O-Round. The final straw for the reimplementation was when the Flutter web platform support appeared so Flutter could cover Android, iOS and Web at the same time.
Reimplementation was in the back of my mind for many months and I was researching which libraries could cover which features, for example:
- Preferences / settings support (ended up using shared_preferences)
- Playing music in the background (ended up using audioplayers)
- Playing sound effects during certain game actions (ended up using soundpool)
- Google Game Services integration (ended up using games_services)
- Drawing sprites on the game action screen
- Drawing textual information and widgets on the game action screen
- Animations on the action screen
- What should happen with the game loop and game logic?
During this SARS-CoV-2 pandemic I attended several Flutter oriented sessions (also organized and co-organized some 1234) and at one webinar I asked a Google engineer if he would advise the usage of game specific libraries like the Flame engine or I should rather try to stick with "plain" Flutter components if I can. I specified that the nature of my game may provide a possibility to use "regular" Flutter. So I would not have to rely on sprites and game loops of traditional gaming engines, but I would stay with normal Flutter widgets and Flutter state handling and concepts. I took the advice of the Google engineer and now I can confirm that it is possible to implement (certain) 2D games in Flutter without resorting to any game engine. Check out the web demo of Deal-O-Round, and if you compare it to the old PlayN version hopefully you agree that it is more appealing.
Advantages of the new Flutter reimplementation:
- For many features I mentioned above there are at least three but sometimes even dozens of alternative packages available. The pub.dev portal fortunately is extremely good at presenting which platforms the packages support, how well they are maintained, how much they are liked and even there’s a scoring system called pub score / pub points. That helps a lot with decision making. The overabundance of packages underlines the healthiness and progressiveness of the platform.
- I find Dart as a language very appealing and soothing.
- All of the game assets are vectorial. What you see is either geometrically painted (the faint background circular gradient spot on the felt, the chip edge pattern of the cards), drawn by the Flutter engine, and fonts are natively vectorial.
- The code-first approach of Flutter makes it possible to create nice animations with limited amount of code and ease. I’m talking about Tween animations, or Widgets which have built-in support like AnimatedList. You can easily combine multiple animations like SlideTransition, RotationTransition, SizeTransition at the same time while the animation curve is also customized with CurvedAnimation.
- There’s even more to the Tween animations like staggered animations, Tween chaining and more. If that’s still nto enough you can reach out to 3rd party libraries like Flare Flutter (Mother of Dashes example app and source code here) or Rive (another Rive article). I must note that almost all the Flutter Clock contest winners and mentions used some form of animation. It can really make a solution stand out.
- The rendering is optimized really well. I was rather battling with some cases when the UI didn’t update although I’d liked it to. These issues usually were about not calling setState properly or I needed to generate a custom Widget Key so the framework could deduct changes.
Notable details of my implementation:
- It was relatively easy to add the full-house swipe animation to the Home screen, I’m using simple Tween animation.
- For the card insertion onto the board and removal from the board I’m using AnimatedList. Since the game physics metaphor is like with Bejeweled, each column of cards is represented by a separate AnimatedList. Animated List is a special widget though. The regular update of a Flutter Widget is the following:
- you have a StatefulWidget or an inherited state somewhere
- you declaratively (code-frist manner, not XML) compose the UI by depending on the state here and there
- when the state is changed by any means Flutter knows the dependency of the Widget from the state and rebuilds it on demand (but only when it’s needed!).
- In case of an AnimatedList however you’d have to explicitly call insertItem or removeItem of the listKey.currentState (which is a GlobalKey <AnimatedListState>). On top of that if you want to change an item already placed in the AnimatedList without explicit removal and insertion then you have to make sure you generate such a Key for the items in the list which reflects any change you want to update.
- Right now I use InheritedWidget technique for state handling. This allows child widgets in the hierarchy to access state maintained in the root level of the widget tree. This is adequate right now, but later I may transition to BLoC or other more advanced patterns.
- Flutter Get is an excellent framework which helps with not just service architecture implementation and dependency injection but also has a ton of additional convenience feature like context-less media queries or context-less SnackBars, Dialogs, and navigation as well.
- You need to tweak the Android port of the project. You may need to add some meta-tags to the AndroidManifest.xml. You probably want to replace the launcher icon set too.
I could truly verify the Game Services integration now that the Flutter version went through Google’s review and rolled out to the Play Store. Now there are also native UI feedback for achievement unlocking, the package developer reacted to a ticket I opened quickly. The reimplementation journey required hard overall effort, but in the end Flutter presented me with such a developer satisfaction and joy I haven’t experienced for a long time.