loading...

#3 — Build a More Complex App

nitya profile image Nitya Narasimhan Originally published at Medium on ・10 min read

This is from a series of articles I’m putting together for FlutterCamp 2018. Read more about it in the first article of the series here.

Woot! At this point you have your development environment setup — you verified it by creating and running your first app on a real or virtual device. Let’s keep going.

Next, we explore the simple Startup Namer app tutorial to build a custom app from the scaffold. We’ll learn a bit about how Flutter apps are structured, about the importance (and power) of widgets.

Don’t worry about the Dart syntax for now. We’ll do a deep dive on that soon. For now, know that if you have experience with Java, JavaScript or Swift, you can immediately grok the code and get a sense for the development patterns. That is all we want to achieve at this stage.

STEP-1: Goal = Startup Namer app

This is an app that generates potential names for a new startup. We can use this to learn the following key concepts:

  • Views & Layout  — we’ll build the one in this image
  • Interaction Events & Handling  — users can select/unselect names
  • Infinitely scrolling, lazily-loaded list  — add 10 at a time on scroll ups
  • Theming (how to change look and feel of UI)
  • Packages (and using them to to extend core Flutter functionality)

NN> Plus, a little bit about how Flutter apps are structured, the role of widgets, state management and stateful hot reload.

STEP-2: Starting Point = Scaffolded App

Walk through the basic steps of creating a new Flutter app as before, but call it startup_namer. Open lib/main.dart, delete existing code and replace it with this code as explained in Step 1.

import 'package:flutter/material.dart';

void **main()** => runApp(new MyApp());

class MyApp extends **StatelessWidget** {
  @override
**Widget build** (BuildContext context) {
    return new **MaterialApp** (
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          child: new Text('Hello World'),
        ),
      ),
    );
  }
}

NN> This requires nothing more than delete-save-reload. No issues for me. However some observations …

OBSERVATIONS:

  1. Functions vs. Classes: Note that the same file contains both top-level functions and top-level classes..
  2. Line 1 : `import 'package:flutter/material.dart' brings the relevant Dart packages into your codebase. In this case, we are importing the Material package which provides all the classes (visual, behavioral and motion-rich widgets) implementing Google’s Material Design for Flutter.
  3. Line 2: `void main() => runApp(new MyApp()); is the mandatory entry point for a Flutter app. It calls the runApp function which inflates the provided widget (here an instance of MyApp) and attaches it to the screen with constraints that force it to fill the full screen. To have a custom widget not fill the screen, wrap it in Center or Align widgets (which then fill it up)
  4. Line: MyApp extends StatelessWidget shows that your custom widget is a Stateless one; we’ll learn about Stateless & Stateful widgets later.
  5. Everything in Flutter is a widget; everything. The style (alignment, padding etc.), the layout, the behaviors (gesture, touch etc.) are all widgets. And UI development is all about widget composition. The key is the build function that describes how to assemble that widget’s subtree (from lower-level widgets). To learn how Flutter transforms a widget tree into pixels on a screen, watch this talk.
  6. MaterialApp, Scaffold & AppBar. MaterialApp is a stateful convenience widget that wraps a number of widgets commonly required for Material Design. It also works with the top-level Navigator to map routes correctly.

STEP-3: Dependencies = Add external package

Many third-party Dart packages for Flutter exist on pub.dartlang.org. Work through the Step 2 directions to add the english_words package and use that to randomly select a new english word to show in the onscreen message.

Steps:

  1. Add english_words dependency in pubspec.yaml
    2.
    When in that file, click get package icon (top right) to fetch the package
  2. Add import 'package:english_words/english_words.dart'; in main.dart
  3. Then use it e.g., final wordPair = new WordPair.random();

NN> This was a fairly painless process for me. No issues. Things to explore: what happens when an imported package has its own dependencies?

STEP-3: Stateful Widget = Add new

Work through the Step 3 directions to build out the first Stateful Widget.

NN> This was also a straightforward step. But let’s spend a few minutes here talking about some interesting aspects.

OBSERVATIONS:

  1. You can define multiple classes and functions in the same source file.
  2. The StatefulWidget does little more beyond creating its associated State instance; it’s the latter that does the heavy lifting on widget creation here.
  3. Both the StatelessWidget and StatefulWidget classes are immutable; their properties cannot be changed once created. The latter however has an associated mutable State object that persists for the lifetime of the widget.
  4. You can see how your UI can be iteratively designed and built by composing it out of lower-level stateful and stateless widgets. We’ll talk about state management later.

STEP-4: List View = Infinite Scroll

Work throughstep 4 of the tutorial to create a list of names that the user can scroll through, infinitely.

NN> This was also a straightforward step. But the interaction between the ListView, the ListView.builder and the helper “_build” functions can be a bit complicated to untangle.

OBSERVATIONS:

  1. Private Variables. In Dart, prefixing a variable with an underscore (_) automatically makes that a private variable. So here, a number of defined class variables and functions are visible only within the class/instance.
  2. Everything’s a Widget. And widgets can be composed of other widgets to create custom complex views. And so on until you get to the lowest-level (nodes) where you’ll likely be using a standard Flutter widget as core. So one way to develop UI is to start by returning a core widget from the build method — then refactoring just that method to wrap the core widget in another widget that provides additional capability; this could for example be a container widget which now lets you return a complex or managed layout instead of a single widget.
  3. ListView is a widget that represents a scrollable set of widgets arranged linearly —when using builder (itemBuilder) it takes a context and an index that increases monotonically. You simply return the ListTile widget for that index if you can create one — so you can create an infinitely scrolling list if you make sure you always return something for each index.

The main thing to look at and understand here is that the code looks more complex than it is because it has all been inlined. Later, we can see how we can make the code clearer to read by simply abstracting out some of this into private functions that are invoked instead.

STEP-5: Interactivity = Event handling

Work through step 5 of the tutorial.

NN> This was also a straightforward step. The key to understand here is the usage of setState() to trigger state changes, which in turn cause render tree to be re-evaluated and re-rendered with the updated state. In this case, it means we go from showing a clear heart to a colored heart (or vice versa) after each click.

STEP-6: Navigation = Route handling

Work throughstep 6 of the tutorial

NN> This is an interesting step to stop and think about since it’s the first place where navigation enters the equation. Let’s talk about Navigator, MaterialApp and the MaterialPageRoute first, then look at code.

The Navigator : is a Flutter core widget that manages a set of child widgets as a “stack” using an Overlay pattern. Overlay allows a widget to be floated above another, to showcase relative importance or provide visual transition effects. If widgets are full-screen, then Navigator effectively creates the illusion of moving from screen to screen (pages) by floating widgets up and down in the Overlay.

This is how routing gets handled (with widgets mapped to routes).This behavior will be very familiar to web developers particularly given the named routes use the path/a/b/c structure for named routes.

MaterialApp:You can create a Navigator for your app, or use one from MaterialApp or WidgetsApp (if they are managing the root of your widget tree). Use Navigator.push(context, new MaterialPageRoute( ... )) to push a new route onto the stack on demand; the MaterialPageRoute takes a builder function which can create the page (widget) to show, and also handle the Navigator.pop(context) behavior to return “back” to the previous screen.

If you use MaterialApp with Scaffold, it automatically adds a”back” button in AppBar that pops the Navigator stack for you in any context.

Routes can return a value. This is cool. It is analogous to startActivityFor Result in Android where, when you trigger the new screen (Navigator.push) you do so with the intent of taking the user to a new screen from which you expect a result to be returned when he is don (Navigator.pop).

There is also support for popup routes (modal part-screen overlays) and custom routes (to control the animations on transition for that route).

STEP-7: Theming = Changing look & feel

Work through step 7 of the tutorial

NN> Theme controls look & feel of the app. Changing an app’s theme is as simple as configuring the ThemeData class at the top-level widget (e.g., MaterialApp).

Exercise : Check out ThemeData capabilities and make other changes to see how you can customize the theme more rigorously. Here is the list of attributes that can be changed:

[ThemeData](https://docs.flutter.io/flutter/material/ThemeData/ThemeData.html)({

[Brightness](https://docs.flutter.io/flutter/services/Brightness-class.html) brightness, 
[MaterialColor](https://docs.flutter.io/flutter/material/MaterialColor-class.html) primarySwatch, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) primaryColor,
[Brightness](https://docs.flutter.io/flutter/services/Brightness-class.html) primaryColorBrightness, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) primaryColorLight, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) primaryColorDark, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) accentColor, 
[Brightness](https://docs.flutter.io/flutter/services/Brightness-class.html) accentColorBrightness, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) canvasColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) scaffoldBackgroundColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) bottomAppBarColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) cardColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) dividerColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) highlightColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) splashColor, 
[InteractiveInkFeatureFactory](https://docs.flutter.io/flutter/material/InteractiveInkFeatureFactory-class.html) splashFactory, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) selectedRowColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) unselectedWidgetColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) disabledColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) buttonColor, 
[ButtonThemeData](https://docs.flutter.io/flutter/material/ButtonThemeData-class.html) buttonTheme, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) secondaryHeaderColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) textSelectionColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) textSelectionHandleColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) backgroundColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) dialogBackgroundColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) indicatorColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) hintColor, 
[Color](https://docs.flutter.io/flutter/dart-ui/Color-class.html) errorColor, 
[String](https://docs.flutter.io/flutter/dart-core/String-class.html) fontFamily, 
[TextTheme](https://docs.flutter.io/flutter/material/TextTheme-class.html) textTheme, 
[TextTheme](https://docs.flutter.io/flutter/material/TextTheme-class.html) primaryTextTheme, 
[TextTheme](https://docs.flutter.io/flutter/material/TextTheme-class.html) accentTextTheme, 
[InputDecorationTheme](https://docs.flutter.io/flutter/material/InputDecorationTheme-class.html) inputDecorationTheme, 
[IconThemeData](https://docs.flutter.io/flutter/widgets/IconThemeData-class.html) iconTheme, 
[IconThemeData](https://docs.flutter.io/flutter/widgets/IconThemeData-class.html) primaryIconTheme, 
[IconThemeData](https://docs.flutter.io/flutter/widgets/IconThemeData-class.html) accentIconTheme, 
[SliderThemeData](https://docs.flutter.io/flutter/material/SliderThemeData-class.html) sliderTheme, 
[ChipThemeData](https://docs.flutter.io/flutter/material/ChipThemeData-class.html) chipTheme, 
[TargetPlatform](https://docs.flutter.io/flutter/foundation/TargetPlatform-class.html) platform 

})

ERROR: How do you know something’s wrong?

The awesome thing about Flutter development is that the simulator/emulator immediately provides feedback through the red screen of death. The descriptions are verbose giving you insights into where the problem lies.

The red screen of death

You can quickly explore ideas by undoing recent changes, commenting-out and modifying the sections identified (e,g, if the offending widget is RandomWords, start there..). Hot reload makes it possible to track and fix at least UI related errors, relatively quickly.

EXPLORATION IDEAS

Try out some of these things to get a better sense for the development environment:

  1. Change ThemeData properties and see how that affects
  2. Change the ListView builder to use a different kind of item widget. See how that impacts the scrolling and appearance.
  3. Explore routing options. Try to create a new route and associated widget and see if you get the default Back behavior.

OPEN ISSUES

I’ll use this section to capture any issues experienced by attendees at the workshop. In particular, I am interested in seeing if the process/experience for attendees on Windows or Linux development environments is as painless.


Posted on by:

nitya profile

Nitya Narasimhan

@nitya

PhD & Polyglot / I want to help translate awareness of technology into actionable learning with impact on career or software development. / Try-Fail-Learn

Discussion

markdown guide