Slide Puzzle: Escape of Cao Cao
This is a puzzle game made on live-stream, demonstrating various animation techniques in Flutter with just Dart code - no dependencies or asset files. It can target mobile, desktop, and the web from a single code base.
The goal of the game is to move "the biggest puzzle piece (Cao Cao)" to "the exit" at the bottom, in as few steps as possible.
A playable web version is available. For the best experience, you can also try out native desktop or mobile apps.
- Added an animation when the game launches.
- Added a hint button.
- Minor changes to the reset button.
The new hint feature spawns an
isolate to find an optimal solution for the current state using
Breadth-First Search. Once the result comes back, an
OverlayEntry is created precisely on top of
the hinted piece, with the help of a
CompositedTransformFollower widget and
in the pieces.
- Added more levels.
- Added a new 3D animation during level transitions.
- Added an option to hide decorative texts on puzzle pieces.
- Various minor UI enhancements and performance improvements.
This is my first time trying to simulate 3D objects without 3rd party tools or plugins in Flutter.
It's made using
Transform widgets to translate and rotate 6 rectangles to correct places to form a
Further optimizations were made so only up to 3 faces (visible to the viewers) are rendered at a
time, and a glare effect was added by changing
stops on a
LinearGradient. This formed the basis
for a new transition animation when a level is completed.
- Added a simple tutorial screen.
- Fixed a bug where the web version was not rendering correctly on mobile browsers.
The tutorial screen is a responsive dialog made with
CupertinoPopupSurface. It also includes a
WillPopScope to allow Android users to
dismiss it with the back button.
It provides an option to hide decorative texts (v1.1.0) in case they don't render properly, e.g. when there are no international fonts installed.
When building for web, make sure to use
canvaskit renderer for better experience across devices.
You can do so with
flutter build web --web-renderer canvaskit command. Otherwise, the puzzle might
not render correctly on mobile browsers. I've
created an issue for this and looks like the
Flutter team is already on it.
- Initial release for all platforms.
The initial version of the app was created as "live coding" stream sessions. With discussions and voting polls on design choices such as the color palette, together we built the game in 12 live-stream sessions.
The support for more platforms were added after the live stream ended.
The video overview (see below) was released at the same time as the initial version, so new features mentioned above are not included in the video.
A short video (less than 3 min) is available, providing a brief overview of version 1.0.0.
At a high level, the app can be divided into 3 parts: the background layer, the game board, and the puzzle pieces. Each puzzle piece also comes with a pair of interlocking attachments and a shadow for some added depth. These components are stacked as 3 different layers, to make sure they always appear in the correct order when being moved.
The color palette is configured as an
InheritedWidget so its values can be modified in one place,
and easily accessed elsewhere in the widget tree.
The puzzle pieces are made from
AnimatedPositioned. For the duration, I pass in zero if the window
size is different from before. This is to skip the unwanted animation when the app is being resized.
And for the curve, I use
EaseOut to slow them down naturally.
To handle user input, I added
GestureDetector. I used the
onPanUpdate event instead of
onPanEnd event, to reduce input lag.
From the picture above you can see that the user has not finished the action (finger is still
touching the screen), but with
onPanUpdate event, the game has already responded.
For the reset button, I added a
MouseRegion widget to make it glow when being hovered.
The step counter on the side is another example of utilizing implicit animations in Flutter. It’s
also wrapped in a
ValueListenableBuilder, so it can always keep up with the steps.
The game board is mostly a
Container with some decorations and clipping. I also used
BackdropFilter here to blur everything behind it, and a
ShaderMask to create a beam effect.
The beam created by
ShaderMask is an explicit animation with its controller set to repeat, so it
can keep dancing around in the game.
Similarly, the decorative texts puzzle pieces are also animated to have a subtle glare effect.
Lastly, I used a
CustomPaint for the app background. It efficiently draws colorful rectangles
every frame and runs well even on low-end devices.
- [x] Resolve web rendering issue for mobile browsers
- [x] Add a tutorial screen to explain the game
- [x] Add more levels to the game
- [x] Add an automatic solver or a hint system
- [ ] Allow users to select levels
- [ ] Add keyboard support
- [ ] Further improve swiping gestures
I've been making lots of video tutorials (mostly in Chinese) on Flutter, covering a wide range of topics and popular questions from viewers. If you are interested, please follow me on "bilibili" so you won't miss the next live stream or video from me.
I've made a total of 19 video tutorials on Flutter Animations, divided into 3 sections: implicit, explicit and others.
- Get moving with just 2 lines of code
- Smooth transitioning between different widgets
- Curves, and more animation widgets
- DIY with a TweenAnimationBuilder
- Case study: make a flip counter
- Case study: flip counter continued
- A repeating animation
- What is an AnimationController
- Curves and Tween
- Staggered animations with intervals
- DIY with an AnimatedBuilder
- Case study: coordinating multiple animations
- Case study: multiple animation controllers
Other types of Animations
- Under the hood: animations and tickers
- Hero animations
- CustomPaint: do you wanna build a snowman
- Animate with Rive/Flare
- Bonus: create an asset with Rive tool
I've made 7 video tutorials on keys, covering basic concepts such as widgets and elements, 3 types of local keys, 2 different uses of global keys, and more.
- Spooky stuff when you forget to use keys
- Widgets, Elements and their States
- Three types of LocalKeys
- Two purposes of GlobalKeys
- Case study: make a color sorting game
- Case study: use LocalKey in the game
- Case study: use GlobalKey in the game
- ListView and lazy loading
- Deep dive into the ListView widget
- RefreshIndicator and NotificationListener
- Swipe away with the Dismissible widget
- Case study: a GitHub repo browser
- GridView widget
- Even more scrollable widgets
- Event loop, queues, and microtasks
- Deep dive into the Future type
- FutureBuilder widget
- Stream and StreamBuilder widget
- Case study: generate stream from user actions
- Case study: listen to our event stream
- Case study: a good use for StreamTransformer
- Constraints, size, and positions
- LayoutBuilder and ConstraintBox
- Flex: Flexible and non-flexible
- Stack: Positioned and non-positioned
- What is a Container, really
- CustomMultiChildLayout widget
- Let's make a RenderObject
- Welcome to the world of Slivers
- All sorts of sliver lists
- SliverAppBar widget
- More sliver widgets and SliverLayoutBuilder
- Case study: convert a ListView into a sliver
- Case study: SliverPersistentHeader
- Case study: Design a page with a SliverAppBar
Coding challenge and follow-ups
(Sometimes I post questions for my viewers and then do follow-up videos to discuss all the creative solutions I receive, so we can learn from each other.)
- Pinch-to-zoom gallery with smooth transitions
- A button that counts down with its border
- Adaptive watermark overlay with FittedBox
- Different ways to implement Hollowed Text
- Ink, InkWell, and Material
- Creative ways to achieve diagonal layout
- Adaptive banner made with Pythagorean Theorem
- The new and the old material buttons
- Different ways to implement the same animation
- Different ways to detect screen rotation
Other popular topics
- What is BuildContext?!
- Flutter Web: common problems and solutions
- What is SOUND null safety?
- Some super useful widgets in Flutter
- Some less known widgets in Flutter
- Flutter 2.0 is here!
- WillPopScope and iOS swiping gesture
- Weird tricks about Flutter Hot Reload
- Hotkeys in Android Studio
- Publish a package: animated flip counter
- Publish a package: interactive chart