What You'll Build in this Workshop:

Full Application

DartPad will be our Flutter development editor, so let's set it up for that.

First, go to a browser and launch dartpad.dev. Your browser should look as follows:

DartPad on the Web

Setting up DartPad for Flutter Development

By default, DartPad is set up for Dart development. For Flutter development, do the following steps:

Select New Pad

Create New Pad

Select Flutter

You should be all set! Now you're ready to start coding in Flutter right from your browser. If you hit the Run button, you will see the generated output on the right panel.

Select Flutter

Are you ready? Let's go to the next step!

For the initial page in this application, we'll be building the Splash Screen.

This is what we'll be accomplishing in this codelab:

Splash Screen and its widget composition

The following illustration shows a schematic view of the widget composition we'll accomplish while building the layout for our splash screen widget:

Splash Screen

Start Coding the App

Let's start coding this application from scratch. Remove all code from the editor and leave just the following code:

import 'package:flutter/material.dart';

void main() {
  // TODO: more code here later
}

We are importing the Material Library Flutter package and like every Flutter app, the main entrypoint is the main method. Inside this method is where things kick off. We'll get back to this point in a minute.

Inside the main method, add the following code:

Main Method

The runApp method is part of the Flutter framework and is in charge of mounting the root widget on the widget tree. We create the first widget (MaterialApp) which represents the application's root widget.

We disable the debugging sash by setting the debugShowCheckedModeBanner to false, and set the home property of the MaterialApp widget to be our SplashPage widget, which we'll create in a moment.

Here's the code you need to add inside the main method, to help you out a bit:


// add this code inside the **main** method:
runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SplashPage()
    )
  );
 

Let's create the SplashPage Widget.

Right under the main method, create a StatelessWidget called SplashPage. We create it as Stateless since we won't be maintaing state. More info on StatelessWidgets here.

Since every widget has a build method, add one, and return an empty Container widget.

Main Method

Under the import statement, right above the main method, add the following line:


// add this code under the first line of code in the file
// import 'package:flutter/material.dart';

final Color mainColor = Color(0xFFFF5656);

We want to use this mainColor property throughout our application and don't want to repeat ourselves, hence holding on to this value on this global property.

Back on the SplashPage widget, add the color property and set it to our newly added color, mainColor:

Container Color

Now, add a child to the Container widget - a Stack widget. We use a Stack mostly to add widgets one on top of each other, in a stack fashion. We'll be adding the icon and the circular progress indicator on top of each other but with different alignments.

Stack Widget

Proceed to add children to the Stack widget - in our case, let's first add an Icon widget, with a specific icon (Icons.terrain) and aligned at the center of the stack using an Align widget with the Alignment.center set to it. Add a white color to it and a size of 90px:

Align Icon

Add the second child to the Stack widget - the CircularProgressIndicator widget. This is a handy widget as it shows a spinning circle that you can use for indicating an ongoing process. Set its valueColor property to AlwaysStoppedAnimation(Colors.white) so it shows white.

We want to show it aligned to the bottom of the stack, so wrap it inside another Align widget, with an alignment: property set to Alignment.bottomCenter.

To help you out and save some typing, you can grab the code below that creates the Align widget with the CircularProgressIndicator inside:


Align(
  alignment: Alignment.bottomCenter,
  child: CircularProgressIndicator(
    valueColor: AlwaysStoppedAnimation(Colors.white)
  )
)

By now, your code should look as follows:

Align Circular Progress Indicator

The CircularProgressIndicatorWidget since it's aligned bottom center inside the stack, we should give it some breathing room, so we'll use a Container widget for it.

Wrap the CircularProgressIndicator widget inside a Container widget, and set its margin property to EdgeInsets.only(bottom: 80) to add 80px below the progress indicator to give it some space:

Container with margin

If you hit Run on DartPad, your app preview should look like this:

Splash Page

So far so good! Now I'll show you how to make a truly functional splash screen in the next step.

Now that we have our Splash Page almost ready, let's create the shell of the page that the Splash Page widget will navigate to after we simulate a short delay (i.e. fetching data from a backend, performing some process prior entering the app, etc.).

Create another class under the Splash Page widget called MountsApp. This will represent the landing page where we'll display all our info. For now let's put dummy content just to test the Splash Page. In the next codelab we'll flesh it out more:

Splash Page

Below is the code to help you out:


// add the landing page under the splash page code

class MountsApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Welcome to Mounts of the World')
      )
    );
  }
}

So what we'll do now is simulate a delay in the Splash Page, then navigate to our landing page. For that we'll make use of a Future, which is an object that represents a delayed computation. More on Futures here.

Back in our Splash Page widget, let's add a Future, and use one of the provided methods called delayed. This method takes a duration, and a callback, which gets executed once the duration (or delay) has ellapsed. We'll set a duration of 2 seconds, and when this time has ellapsed, it will execute our callback, which in turn will perform a Navigation to our landing page.

Add a Future.delayed inside the SplashPage widget's build method, right above the return statement, as such:

Future

Let's dissect this code. Future.delayed takes two parameters: a Duration object with is seconds property set to 2, and a callback. When the 2 seconds have ellapsed, it will call the callback.

The callback has inside a trigger to perform a navigation. We use Navigator.of to push a new route to the global navigation stack, using the BuildContext context to retrieve the closest navigator in the hierarchy, in our case, the one available by default from the MaterialApp. We use a MaterialPageRoute to wrap the widget we want to navigate to, in our case the MountsApp widget created earlier.

You can grab the code from below to help you out:


// paste this code inside the build method, at the beginning...

Future.delayed(const Duration(seconds: 2), () {
  Navigator.of(context).push(MaterialPageRoute(builder: (context) => MountsApp()));
});

Now, let's take our current implementation for a spin with what we have so far.

In DartPad, click Run. After a 2 seconds delay, you should see the SplashSpage widget slide out of view and sliding in comes the MountsApp widget, our landing page.

Run the App

Congrats in making it this far! In this codelab, we accomplished the following:

In the next codelab, we'll flesh out the landing page MountsApp widget and learn more about creating widgets. See you there!

In case you fell behind on this codelab, below is the whole code for this codelab in a way you can copy / paste directly into DartPad:

import 'package:flutter/material.dart';

//-----GLOBAL VARIABLES-----
final Color mainColor = Color(0xFFFF5656);


//-----MAIN METHOD-----
void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false, 
      home: SplashPage()
    )
  );
}

//-----PAGES-----
// splash page
class SplashPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    Future.delayed(const Duration(seconds: 2), () {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (context) => MountsApp()));
    });

    return Container(
      color: mainColor,
      child: Stack(
        children: [
          Align(
            alignment: Alignment.center,
            child: Icon(Icons.terrain, color: Colors.white, size: 90),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: Container(
              margin: EdgeInsets.only(bottom: 80),
              child: CircularProgressIndicator(
                valueColor: AlwaysStoppedAnimation<Color>(Colors.white)
              )
            )
          )
        ],
      )
    );
  }
}

// landing page
class MountsApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Welcome to Mounts of the World!')
      )
    );
  }
}