How to write “Fifteen” on Flutter

You may have already read about the contest Flutter Puzzle Hack and think about how to show maximum creativity. And we will help you with this by talking about how the code base of our puzzle example is structured. We share the details before the start of the author’s course on web development in Python.

Architecture

In the provided source code, a layered architecture for state management is implemented using flutter_bloc. Blocks help you manage everything from game logic to theme customization.

All states are managed consistently: to change the logic of the puzzle, you only need to find and update the corresponding block. The reset and shuffle buttons, the timer and the countdown buttons are also separate blocks that can be arranged according to your idea: maybe make an hourglass for the timer? Or chic stylize mixing?

Please note: all game logic is in one PuzzleBloc block, which changes on events such as clicking on one of the 15 squares and moving it using the TileTapped event, as well as completely resetting the puzzle using the PuzzleReset event. The state of the puzzle changes with every change in the game:

class PuzzleBloc extends Bloc<PuzzleEvent, PuzzleState> {
  PuzzleBloc(this._size, {this.random}) : super(const PuzzleState() {
    on<PuzzleInitialized>(_onPuzzleInitialized);
    on<TileTapped>(_onTileTapped);
    on<PuzzleReset>(_onPuzzleReset);
  }
  void _onPuzzleInitialized(
    PuzzleInitialized event,
    Emitter<PuzzleState> emit,
  ) {...}
  void _onTileTapped(
    TileTapped event,
    Emitter<PuzzleState> emit,
  ) {...}
  void _onPuzzleReset(
    PuzzleReset event,
    Emitter<PuzzleState> emit,
  ) {...}
}

Theme customization

There are two topics in the puzzle code sample: Simple and Dashatar. You can use them as the basis for your own settings or start from scratch – the implementation is up to you! You can be creative in customizing puzzle themes.

In the demo, everything happens at the top of the PuzzlePage. It is enough to change the elements of the theme in one place, and the changes will be reflected everywhere. In both themes, a number of parameters are defined: screen background, menu, logo, buttons, text color, timer display (available in Dashatar, but not in Simple) and others. They are located in the root of the repository, in the dashatar and simple directories:

/// {@template simple_theme}
/// The simple puzzle theme.
/// {@endtemplate}
class SimpleTheme extends PuzzleTheme {
  /// {@macro simple_theme}
  const SimpleTheme() : super();
  @override
  Color get backgroundColor => PuzzleColors.white;
  @override
  Color get buttonColor => PuzzleColors.primary6;
  @override
  Color get hoverColor => PuzzleColors.primary3;
  @override
  Color get pressedColor => PuzzleColors.primary7;
  ...
}

Each theme has a LayoutDelegate, which is used to calculate the layout. New themes can be created by reusing the same layout objects. It remains to configure the settings.

For a more complex design, you can customize the entire LayoutDelegate theme. For example, to create a custom background that only appears on the big screen, you can override backgroundBuilder :

@override
Widget backgroundBuilder(PuzzleState state) {
  return Positioned(
    bottom: 74,
    right: 50,
    child: ResponsiveLayoutBuilder(
      small: (_, child) => const SizedBox(),
      medium: (_, child) => const SizedBox(),
      large: (_, child) => const DashatarThemePicker(),
    ),
  );
}

Animation

Animation is a great puzzle piece to explore. There is no animation in the Simple theme, but a few phased animations are implemented in the Dashatar code. These animations are controlled by a single controller, which has Interval to set animation delay and Tween to define the range of animation values.

This can be seen in the assembled puzzle, in the Dashatar theme, where multiple widgets are rendered in sequence, animating block offset and opacity. Similarly, using the same technique, each tick slowly increases and then disappears the countdown timer before the next timer appears:

Most of the animations in the Dashatar theme are implicit: there is no need to write all the animation yourself – property changes are animated by the widgets themselves. For example, the DashatarPuzzleTile widget animates the movement of squares by clicking on them. Thanks to the implicitly animated AnimatedAlign, the current position of the square changes according to the given movementDuration:

class DashatarPuzzleTile extends StatelessWidget {
  ... 
  
  final Tile tile;
  
  @override
  Widget build(BuildContext context) {
    return AnimatedAlign(
      alignment: FractionalOffset(
        (tile.currentPosition.x - 1) / (size - 1),
        (tile.currentPosition.y - 1) / (size - 1),
      ),
      duration: movementDuration,
      curve: Curves.easeInOut,
      child: ResponsiveLayoutBuilder(...),
    );
  }
}

Puzzle for the web

With puzzles created for the web. Responsive design implemented for small, medium and large screens. In addition, there is a ResponsiveLayoutBuilder with a wrapper around the LayoutBuilder widget in Flutter, which allows you to specify different widgets on different breakpoints.

It is necessary to take into account additional circumstances in the web. To optimize performance during gameplay, images and audio are pre-cached.

When the user enters the default puzzle game, Simple, files for the Dashatar version are downloaded in the background. We used a similar approach when loading all properties for I/O Photo Booth (I/O photo booths). This way we ensure that when you switch to the Dashatar theme, most of the files are already downloaded.

You can get creative and try to make a puzzle for several platforms. How will it look on mobile devices and desktop computers? How to adapt ideas for different platforms?

Availability

When creating “Fifteen”, accessibility was taken into account. Interaction with the puzzle is possible using the keyboard, for this there is a RawKeyboardListener widget that uses a callback when the user presses or releases a key on the keyboard.

The app can be interacted with through screen readers. This is being done using semantic labels. Some actions have tooltips: the Tooltip widget is used.

If you are writing “Fifteen” from scratch, we strongly recommend that you make the puzzle available to all users (use strategies similar to those described above).

Other ideas

Take the codebase from the demo as a basis, or implement a new idea from scratch. It is important that the application has a working puzzle, but the result is up to you!

From the examples of Simple and Dashatar, you can borrow ideas for settings or something interesting that you can do yourself. One interesting idea is to project onto puzzle squares created Felix Blaschke Flutter Plasma.

The plasma effect is achieved by using a CustomPaint widget wrapped in a Transform widget and animated with an AnimationController. Here example plasma renders from demo:

Another idea is to get images or other data from the API. For example, to make “Fifteen”, you can use Google Photos APIby taking photos from one of the Google Photos albums. There is no limit to perfection in this competition!

See sample puzzle code here. Share your creations with us on Twitter using the hashtag #FlutterPuzzleHack. Look forward to!

The code is licensed under MIT, so it’s easy to use in your Fifteens. And you can continue immersion in IT and upgrade your skills in our courses:

Choose another in-demand profession.

Brief catalog of courses and professions

Data Science and Machine Learning

Python, web development

Mobile development

Java and C#

From basics to depth

As well as

Similar Posts

Leave a Reply