DEV Community

Cover image for Sandbox#1: Flutter Application Design First Steps
Mathieu Kerjouan
Mathieu Kerjouan

Posted on

Sandbox#1: Flutter Application Design First Steps

Epic things start with small humble steps. Pay respect to your beginnings. And if you're just starting out, know that it's OK to be sucky. To be small. To be messy and chaotic. Just make sure to never ever stop dreaming.

-- Vishen Lakhiani

Let start another chapter of this journey with Dart by creating a mobile application with Flutter. For this post, a really simple application will be created called sandbox. Instead of adding some interactive part, like sending/receiving data from a backend, let simply design an application based on a wireframe.

Bootstrapping

As usual, I'm using asdf to install Flutter on my laptop but it can also be installed manually by following the Flutter Quick Install documentation. This new project will be called sandbox, a good name for a draft.

$ flutter create sandbox
Creating project sandbox...
Resolving dependencies in `sandbox`... 
Downloading packages... 
Got dependencies in `sandbox`.
Wrote 131 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

In order to run your application, type:

  $ cd sandbox
  $ flutter run

Your application code is in sandbox/lib/main.dart.

$ cd sandbox
Enter fullscreen mode Exit fullscreen mode

Wireframe and Prototype

My career is from network and system administration, backend and distributed development, when it comes to design something, I'm thinking like a technician/engineer: I don't care about the look, it should work. In a previous publication, rock-paper-scissors data structure and primitive have been designed, why not starting with a kind of interface for this game?

Prototype made with Figma

Creating a prototype or a wireframe with Figma is painful. Really. I still don't believe such kind of "application" can be so successful. Anyway, after a long fight, and without any idea how to expert a layer correctly, I finally decided to do a screenshot of the page. Yeah. Ridiculous. If you are a developer at Figma, are you really using your software? I mean, exporting a selected area can be done quite easily with draw.io.

Anyway, the goal here is to reproduce this with Flutter. No need for interactive feature, just a raw prototype.

First Version

Someone said "start humble", this is the first version, with lot of duplicated code an crappy colors, but it should give the big picture of the application.

The top bar will be used for the menu, to switch to another mode or game for example. It contains the title of the current page. At this time, it will do nothing.

Then comes the opponent container. This part will be empty at first, but in the future, it could be an animated area, where the final shape of the opponent will be loaded.

The third container will contain the final result, when both players selected the shape, and the result should be displayed. If this application is used also by someone else (remotely), one player can see "loss", while the other one will see "win". If the shapes are the same, both players will see "draw".

The fourth container is where the player will select the action. Three shapes are available, rock, paper and scissors. When tapping on one of them, it will send it to a remote server (if playing against someone else), or simply update the state of the application (if playing against the machine).

Finally, the last container is the bottom bar. It will be removed in the second version because I think an user can click by mistake on one of the button instead of selecting one shape. Let begins the implementation. Nothing really fancy at first.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}
Enter fullscreen mode Exit fullscreen mode

The interesting part is present in MyApp() class where we are defining the different layer of the application.

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(onPressed: () {}, icon: Icon(Icons.menu)),
          title: Text("Jan Ken Pon"),
        ),
        body: Column(
          children: <Widget>[
            Expanded(
              child: Center(
                child: InkWell(
                  onTap: () {},
                  child: Ink(
                    height: double.infinity,
                    width: double.infinity,
                    color: Colors.grey,
                    child: Center(child: Text("opponent choice")),
                  ),
                ),
              ),
            ),
            Expanded(
              child: Center(
                child: InkWell(
                  onTap: () {},
                  child: Ink(
                    height: double.infinity,
                    width: double.infinity,
                    color: Colors.amber,
                    child: Center(child: Text("result")),
                  ),
                ),
              ),
            ),
            Expanded(
              child: Center(
                child: Row(
                  children: <Widget>[
                    Expanded(
                      child: InkWell(
                        onTap: () {},
                        child: Ink(
                          height: double.infinity,
                          width: double.infinity,
                          color: Colors.blue,
                          child: Center(child: Text("rock")),
                        ),
                      ),
                    ),
                    Expanded(
                      child: InkWell(
                        onTap: () {},
                        child: Ink(
                          height: double.infinity,
                          width: double.infinity,
                          color: Colors.green,
                          child: Center(child: Text("paper")),
                        ),
                      ),
                    ),
                    Expanded(
                      child: InkWell(
                        onTap: () {},
                        child: Ink(
                          height: double.infinity,
                          width: double.infinity,
                          color: Colors.red,
                          child: Center(child: Text("scissors")),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
        bottomNavigationBar: BottomNavigationBar(
          items: [
            BottomNavigationBarItem(icon: Icon(Icons.star), label: "Home"),
            BottomNavigationBarItem(
              icon: Icon(Icons.star),
              label: "Jan Ken Pon",
            ),
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

I know, this is a disgusting piece of code. The level of indentation is insane and the duplicated code is crazy, but at least, it works. What kind of classes I used here?

  • Scaffold class is used to configure the visual layout.

  • AppBar() class is instantiated in the Scaffold object (appBar attribute) and configure the top bar of the application. The menu button on the left currently don't work. The title is a simple Text() object;

AppBar(
  leading: IconButton(
    // ...
  ),
  title: Text(
    // ...
  ),
 )
Enter fullscreen mode Exit fullscreen mode
BottomNavigationBar(
  items: [
   BottomNavigationBarItem(
     // ...
   ),
   BottomNavigationBarItem(
     // ...
   ),
  ]
)
Enter fullscreen mode Exit fullscreen mode
  • Column class is used in the body attribute of the Scaffold object. This is where the user will be able to see the actions and play with the application. A list of Widget can be provided using the children attributes, each one will divide the available space. So, in our case, we need 3 "lines", one for the opponent, one for the result and the last one for the user selection;
Column(
  children: <Widget>[
    // ...
  ]
)
Enter fullscreen mode Exit fullscreen mode
  • Expanded class is used on every "line" to use all the space available from each line;
Expanded(
  child: Center(
    // ...
  )
)
Enter fullscreen mode Exit fullscreen mode
  • Center is then used to vertically and horizontally center all elements;
Center(
  child: InkWell(
    // ...
  )
)
Enter fullscreen mode Exit fullscreen mode
  • InkWell classes are then used to define a place that can react to user touch, a bit like a button, but with more flexibility;
InkWell(
  onTap: () {},
  child: Ink(
    // ...
  ),
)
Enter fullscreen mode Exit fullscreen mode
Ink(
  height: double.infinity,
  width: double.infinity,
  color: Colors.amber,
  child: Center(
    // ...
  ),
)
Enter fullscreen mode Exit fullscreen mode
  • Row is also used for the shapes user layer, where the user can select rock, paper or scissors. The goal of this class is to split the available space in rows.
Row(
  children: <Widget>[
    // ...
  ]
)
Enter fullscreen mode Exit fullscreen mode

Conclusion

This application can be compiled and installed on the Android emulator. Here a screenshot of this first application draft from Android (API 36.0).

Design First Version

The colors have been set simply to see where the important information will be displayed. The same colors will stay there until we have something functional.

With this first version, we also have an idea of the essential bricks we can reuse in the future. It means the next iteration will remove some part of this design, create reusable functions and likely add a bit of interactivity.

Have fun!


Cover Image by Arseny Togulev on Unsplash

Top comments (0)