Story
So, I am currently trying to live a less materialistic lifestyle, which means, when special occasions come around, I do not want any physical things as gifts. So, what does that mean? Well, it means I am incredibly hard to get a gift for! It doesn’t mean I expect a gift, but if you wanted to give one to me, and knew me well enough to want to try to respect my values, you might end up drawing a blank as to what I would want. Anyway, to help my husband out as my birthday rolled around this year, I told him that I wanted a Game Jam for my birthday. I felt like we had fun making Bug Planet Escape and Signal Crash together, so I wanted to work together again. Granted those two games took longer than a day, but I thought I had a one-day idea in mind already. For the past few weeks, or maybe months, there was simple game idea mulling about in my head, which I knew would be easy to create in GameMaker Studio 2. We wouldn’t have to burn any time having to come up with the game idea on the day of… or so I thought.
A few days before my birthday, since I was so excited about it, I began to more clearly define the design. However, I began to realize that I the concept was not as fully fleshed out in my mind as much as I thought. It wasn’t that I didn’t have ideas, it was more like I was still in the brainstorming phase; I had TOO many ideas. This meant that the implementation was FUZZY, and I could not decide on the ultimate direction of the game. Since I couldn’t form concrete instructions to give my husband, I decided to change to a more clearly defined game. Enter the other thing that was wandering about in the back of my mind.
Before my birthday, I had noticed the “Flutter Puzzle Hack” hackathon. The idea was to create a Slider Puzzle for the web, and there were prizes for the winners. After our first experience with Bug Planet Escape and Signal Crash, I knew that my chances of winning were pretty slim. According to former Google employee, Filip Hráček, the app that will win isn’t necessarily the most “beautiful” and also, you could be competing against TEAMS of like 100’s of PROFESSIONAL developers. The other thing that the entry needed was an unexpected amount of marketing materials, which I immediately knew would be a waste of my time on Earth personally. If Google is going to do a showcase, it should make the resources itself versus push it onto the developer that is already on a time crunch to finish the technical aspect. However, again, that means the target audience for this hackathon was probably a TEAM. One person could handle the media, one could handle bugs, etc. Another drawback to these sorts of things is that you CANNOT monetize your project and it is forced to be open source; the community can take it and do what they want with your idea and monetize it, but you can’t.
So, I thought the Flutter Puzzle Hack was a cool thing, but I had shelved the idea of actually participating. One of the main reasons to shelve the idea was also that I just SUCKED at completing slider puzzles as a kid and didn’t know how to possibly make the algorithm to ensure it was solvable…. or so I thought. As time passed, the Flutter Puzzle Hack just kept taking over my consciousness at random points in the day, and my mind tried to figure out how to code the algorithm. Then, I figured it out…. Suddenly, the temptation to participate in the Flutter Puzzle Hack was breathing down my neck. I had the solution in mind, and it was pestering me to be implemented. Originally, I had shoved this nuisance to the back of my mind, but when I decided to scrap my original Birthday Game Jam idea, this Slider Puzzle game came up.
Since I didn’t really know how to make sliding pieces on the screen with Flutter, I had resolved myself to using GameMaker Studio 2 (GMS2). The Flutter Puzzle Hack was just a prompt, but actually, I just wanted to make the Slider Puzzle game, because I had come up with the algorithm for something that my previous self had thought was impossible for me to solve. The first requirement for the game was that I should be able to place an ad so I could generate income to work towards my dream of making games for a living. Of course, I was in it more for the exercise, but I might as well put the ad in, or else the purpose of the game wouldn’t have any meaning. If I want income, I have to stay practiced in the skills that will generate it.
This meant mobile development in GMS2, since I had a mobile export license for GMS2. However, that’s all that I had. GMS2 has terrible mobile support in the sense of backwards compatibility. I had made a previous game with ads, so I thought I could just reuse that code, but I was GRANDLY wrong. They had completely REMOVED their original Google Ad Mob plugin without warning and left a message in its place to migrate to the next plugin. Basically, I cannot compile and fix my old project anymore, because they pulled the old plugin without warning, and the library is NOT baked into the project; it is linked to your account, so you are basically screwed if you update. Just trying to compile their SAMPLE project didn’t work with the minimum SDK I was targeting for Android. I kept discussing this with one of their support members, but they just could not understand the problem, nor did they bother to even test out the sample project themselves until like the 4 or 5th email. In fact, their email was so vaguely worded, I don’t even know if they did it in the end. This reminded me that they also notoriously do NOT let you change the Android versionCode yourself.
Anyway, since I couldn’t get my old GMS2 code to compile nor the sample project, my “easy” jump start approach basically failed. The lackluster experience with GMS2 support didn’t help either. So, if I really had to start from scratch, I decided I would make the game using Flutter, since I also had a previously working app with ads there. I began to laugh, because this jam would line up with the Flutter Puzzle Hack, and I entertained the idea of entering it. However, I confirmed that it was for web only, needed media materials, and needed to be open source and free; which means, I re-resolved myself into not actually entering.
The sad part was that changing to Flutter meant that my husband couldn’t participate as much as I planned and that the project would probably take more than a day. Way before this, I had taught myself how to do remote Git hosting so that I could work with my husband in GMS2. He’s not a programmer, but generally understands GMS2. Version control was a nightmare during Bug Planet Escape and Signal Crash, so I wanted to use this Birthday Game Jam to set up a better coordination method. I researched and tried several hosts, and finally ended up on one. I painstakingly then went through the whole process of learning to push, branch, etc., so that I could explain it to my husband. Originally, I was going to make a tutorial series for him to watch on YouTube, but my video production skills are really slow, and I didn’t have a lot of time. I even researched a beginner friendly UI for handling Git. I had basically got all this research done for collaborating with him, only to not use most of it.
Okay, back to the Flutter. I originally didn’t want to use Flutter, because I didn’t know how I could create the sliding motion. However, my husband reminded me that it would be okay to just teleport the numbers. It’s not a “slider” puzzle anymore, but at this point, I just wanted to prove the algorithm worked and, at the end of the day, I wanted to work on something with my husband. So he handled the design and graphics, and I did the coding; it was just like our GMS2 projects, except that he didn’t have the set up to compile and test it.
So, sometime when I was being proud of getting basic functionality, Very Good Ventures, in partnership with Google, PUBLISHED basically “the answer” to the Flutter Puzzle Hack online. They published a sample code that you could just grab and edit. It felt kind of like a slap in the face really. Why give us the answer to the challenge. I’m pretty sure it was guide us into some basic quality and help beginners feel like they actually made something cool. That’s nice…. However, I think they should have provided the example in the FIRST post of the rules, not AFTER people have been working on an implementation already. Anyway, since I wasn’t even entering the contest to begin with, I told myself to avoid using the shiny thing, and keep trying to get better at writing from scratch. My game would not be flashy and have nice code structure and documentation, but it would be MY code. It would be my battle scars.
So, like a lot of coders, I continued to do it the hard way. No one will know how awesome it is under the hood, because it’s not as flashy and fancy as code made by a team of developers, but I can say that I coded it without copying the “answer”. I actually skimmed over the “answer” to see how close I was, and it looks like it uses the Bloc Architecture. I definitely wasn’t going to waste time switching over from Riverpod. Also, my guaranteed solution algorithm was completely different from theirs. I also didn’t use super fancy packages, and just used those that can be traced specifically to Google or are Google Favorites (except Riverpod, which is the successor the Flutter Favorite Provider) to try to ensure security and stability.
I’m so proud of us for actually pretty much making the deadline. If we had started at the time the Flutter Puzzle Hack was actually announced, I’m fairly certain we would have made the March 14, 2022 deadline. The app itself was done; I just didn’t have the time to bundle, sign, gather media, etc. for review in the Google Play Store. However, all of that was resolved in a week or so, so I’m happy. 🙂
Anyway, have an awesome day! Thanks for reading!
Things I Learned & Did
- Became acquainted with Flutter statement management using
Provider
and Riverpod
packages.
- Learned
null
safety and Dart null syntax shorthand.
- Learned the definition of mutability and immutability, and its importance in state management.
- Gained a clearer picture of the difference between the Dart language and Flutter framework.
- Learned a significant amount of Dart classes and syntax.
- Dart style tends to use single quotes (
'
) verus double quotes ("
) to denote strings, although both are still valid.
- Three quotes (
'''
) or three double quotes ("""
) observe whitespace characters, like newline characters, as TYPED in the CODE. So you don’t need to use '\n'
.
- These can be combined with regular strings with the
+
operator.
- Learned that
List
s can be O(1) with hashmaps.
- Learned what a hashmap is.
- Learned that underscore (_) defines scope, or, if it is a parameter, it is just a placeholder variable name convention (having other passed-in parameters that have more than one underscore just the parameter names different from each other to meet the syntax regulation of having uniquely-named parameters).
- Learned keywords
part
versus library
versus part of
.
- Making a dart-file a
part
is like making c-file which will have other C-FILES included into it using a preprocessor directive (#include "file.c"
).
- Making a dart-file a
part of
is like including it into the part
file that you specify.
- There can only be only one
part
, but multiple part of
s and they are all treated as one giant file.
- Putting
part of 'cat.dart';
at the top of a file means that it is considered as part of the cat.dart
file.
- Keywords
final
versus const
.
- Learned you can rename the library in the local context using
as
in case there are name collisions between libraries.
import 'global.dart' as globs;
- Then use it like this
globs.ClassInGlobalDotDar
.
- Named constructors.
Cat()
verus Cat.sleeping()
.
- Optional parameters, required parameters, positioned parameters, named parameters.
- Gained a clearer picture of what the three Flutter trees do and how they interact with each other:
- Widget
- Element
- RenderObject
- Learned the difference between imperative and declarative programming.
- Learned what Flutter keys are and why they are needed since the Widget Tree looks at Widget Type and not an object ID to determine redraw. Keys enforce an ID if an object must move around the screen via an automatic or used-controlled animation.
- Used
SharedPrefrences
to store and read from non-volatile data.
- Learned what a
Tuple
is.
- Gained a better idea of how the
google_mobile_ads
Flutter package works.
- Relearned how to implement a banner ad.
- Implemented an implicit animation.
- Got REALLY GOOD at solving 4×4 puzzles during testing. I remember being frustrated at not being able to do it as a kid, so I feel so smart now.
- Created a custom
Snackbar
widget.
- Used a
Stack
to create a complex layout.
- Relearned the difference between a
Stateless
and Stateful
widget.
- Relearned how to create a timer.
- Had a eureka moment in understanding asynchronous and synchronous code.
- Learned about isolates.
- Heavily used the
"await"
keyword and didn’t block main thread.
- Learned that a
Future.delayed(Duration.zero,(){}
is useful if you want to postpone the execution of a function in init()
to occur AFTER the first called to build()
. An example scenarios is if you want to display a SnackBar
for an error during init()
; SnackBar
requires a BuildContext
, so build()
needs to have been called once or else you won’t have any draw context
for the SnackBar
to attach to and pop off from.
- Learned about
Semantics
and about Flutter’s accessibility features.
- Gained experience in trying to use TalkBack.
- Became acquainted with Git, GitHub, and Bitbucket.
- Learned how to
push
.
- Learned how to
tag
.
- Learned how to
pull
.
- Learned how to
branch
.
- Learned how to navigate GitHub.
- Learned the Markdown language and how to use
MarkdownBody
from a Flutter package.
- Learned how to
rebase
.
- Learned that commit is local and pushing is to the remote (i.e. Internet Hosted).
- Learned how an
amended commit
works.
- Connected my local repository to the remote repository.
- Learned how to use GitHub Desktop.
- Became more acquainted with Advanced IDE capabilities, in particular, for VSCode.
- Learned that
"///"
will help you make comments for Intellisense for your code. Using "///"
versus "//"
will cause the "///"
comment directly above a function, etc. to pop up on a mouseover of the function name in other parts of the code.
- Learned how to use code snippets.
- Learned that VSCode can automatically reformat your code to a particular style on saving a file.
- Learned how to create my own
VoidCallback
to passthrough a custom onPressed
function to a custom button I made.
- Relearned how to implement a:
Scrollbar
.
ListView
.
Scaffold
.
SafeArea
.
DeviceOrientation
.
showDialog()
with an AlertDialog
.
Image.asset()
.
showLicensePage()
.
- Relearned how to import your own
.ttf
fonts.
- Learned about and used
extension
methods.
- Learned how to create a
ModalBarrier
.
- Learned how to properly use a
LayoutBuilder
to get the parent height and width verses using MediaQuery
. MediaQuery
is a global call for dimensions.
- Learned how to check the type
T
of a generic class to only implement it for specific types and throw an error if it that type is not implemented.
- Learned the existence of
assert()
and the difference between using assert()
and throwing an error/exception.
- Learned the difference of the Dart Team’s differentiaton between an
Exception
versus Error
.
- An
Exception
is something that the program should be able to recover from, kind of like a switch-case for expected possible problems. If properly handled, the user will probably not know that an Exception
has occurred.
- An
Error
is an irrecoverable state in which the program probably will display an message to the user and shutdown. The developer might want to store some state information to crash logs to look for the BUG that caused this Error
to trigger.
- And much more that I’m probably forgetting to write!