Get Familiar with Dart Syntax and Flutter Classes

In this first section of the chapter, we’ll take a look at the Flutter CLI’s example code, review how to work with Dart code, and learn the basics of Flutter app architecture and implementation.

To create a new Flutter app, create a directory to contain the app’s source code and, inside it, another directory called lib. The lib directory is where we’ll add our Dart files defining what our app looks like and what our app does.

For now we won’t work in the CLI, but once we’re ready to build the app we will use flutter create to generate the files that are necessary for the app to build (which you’ll get to know and edit in the appendix about app configuration). In addition to the files and directories directly related to Flutter and your app, the root of your Flutter app must also contain an android and an ios directory containing the configuration for the build system for each platform.

Inside the lib directory, create a file called main.dart.

Outside lib, create a file called pubspec.yaml and paste this in it:

 name: ​flutter_example_name
 description: ​Example description of a flutter app that makes
 great things happen.
 
 dependencies:
  flutter:
  sdk: ​flutter
 dev_dependencies:
  flutter_test:
  sdk: ​flutter
 
 flutter:
  uses-material-design: true

The pubspec.yaml file contains information about your project and its dependencies, which Flutter will need to build your app. Actually this file is required for any Dart package, but it’ll take a while and a few chapters before we start looking at Dart packages that aren’t Flutter apps.

Inside main.dart, write the following:

 import​ ​'package:flutter/material.dart'​;
 
 void​ ​main​() => runApp(MyApp());
 
 class​ MyApp ​extends​ StatelessWidget {
  @override
  Widget build(BuildContext context) {
 return​ MaterialApp(
  title: ​'Flutter Demo'​,
  theme: ThemeData(
  primarySwatch: Colors.blue,
  ),
  home: MyHomePage(title: ​'Flutter Demo Home Page'​),
  );
  }
 }
 class​ MyHomePage ​extends​ StatefulWidget {
  MyHomePage({Key key, ​this​.title}) : ​super​(key: key);
 final​ ​String​ title;
 
  @override
  _MyHomePageState createState() => _MyHomePageState();
 }
 class​ _MyHomePageState ​extends​ State<MyHomePage> {
 int​ _counter = 0;
 
 void​ _incrementCounter() {
  setState(() {
  _counter++;
  });
  }
  @override
  Widget build(BuildContext context) {
 return​ Scaffold(
  appBar: AppBar(
  title: Text(widget.title),
  ),
  body: Center(
  child: Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
  Text(
 'You have pushed the button this many times:'​,
  ),
  Text(
 '​​$_counter​​'​,
  style: Theme.of(context).textTheme.display1,
  ),
  ],
  ),
  ),
  floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter,
  tooltip: ​'Increment'​,
  child: Icon(Icons.add),
  ),
  );
  }
 
 }

You’ll get a simple view with a counter and a floating action button which, when clicked, increments a counter that is shown at the center of the view, like in this screenshot:

images/FirstApp/def.png

If the app doesn’t work, try running:

 $ flutter create .

As mentioned in the Preface, flutter create is the command used to create Flutter app projects, and executing it with a directory containing Flutter code will create the files and directories necessary for the app to compile for Android and iOS (and, as they become more stable, will also include the files needed for desktop development).

Keep in mind that you can fix most build problems not caused by errors in the code, especially after multiple builds, by running:

 $ flutter clean

which will delete build files left by previous builds.

Analyze the Example Code

Let’s break down this code so that you can understand this example.

The pubspec.yaml File

Before moving on to main.dart, which contains the main app code, we should take a brief look at the pubspec.yaml file, which we will discuss in greater detail in Chapter 4, Beyond the Standard Library: Plugins and Packages and, in even greater detail, in Appendix 2, Apple-Like Look and Additional App Configuration .

It is important to understand that the pubspec.yaml file is not unique to Flutter apps: it is a feature of Dart packages and, as such, also contains all the information needed to make it a Flutter app.

In the first part we’ll specify some metadata about the Flutter app. In this section, two attributes are set:

  • The name, which is the Dart package name and it is the default app name that appears on the home screen or the app drawer. The one drawback with name is that it must be all lowercase and be a valid Dart identifier: it can’t start with a digit, it can’t contain any character other than letters and underscores (no spaces allowed) and it can’t be a Dart keyword (like class, if, try, etc.). Thus, it is recommended to edit the app files in the android and ios directories, as explained in Platform-Specific Setup, if you want an app name that doesn’t fit within those restrictions and if you want to change the launcher icon.
  • The description, which should be a brief, sentence or two explanation of the Flutter app.

In order to avoid build issues and as general good practice, the name of the directory that contains the root of the project (the lib directory and the pubspec.yaml file) should be the same as the Dart package name.

 name: ​flutter_example_name
 description: ​Example description of a flutter app that makes
 great things happen.

The rest of the file specifies the dependencies.

 dependencies:
  flutter:
  sdk: ​flutter
 
 dev_dependencies:
  flutter_test:
  sdk: ​flutter
 
 flutter:
  uses-material-design: true

Since we are not using any third-party packages, we are just specifying the Flutter SDK itself as a dependency and, with the lines:

 flutter:
  uses-material-design: true

we make sure that Material Design (Google/Android style) assets like icons are included.

The App Widget

 import​ ​'package:flutter/material.dart'​;

This brings in the Flutter classes we need to use. In this chapter you’ll learn about the simplest ones and how to work with them (we’ll look at many others as we continue in Chapter 2, Laying Out More Widgets as well).

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

This line means that, when our code gets executed, we will start an app, and this app’s behavior and appearance is specified inside a class called MyApp, which is defined as:

 class​ MyApp ​extends​ StatelessWidget {
  @override
  Widget build(BuildContext context) {
 return​ MaterialApp(
  title: ​'Flutter Demo'​,
  theme: ThemeData(
  primarySwatch: Colors.blue,
  ),
  home: MyHomePage(title: ​'Flutter Demo Home Page'​),
  );
  }
 }

Let’s break this down further.

Defining Widgets in Flutter

 class​ MyApp ​extends​ StatelessWidget

In Flutter every UI element or collection of UI elements is a widget, which is an immutable object. This means we can’t change a widget’s member variables: if there are any, they have to be declared as final or constant. You can find more information on Dart variables in Variables and Conditions.

There are two kinds of widgets:

  • Stateless widgets (subclasses of StatelessWidget), which get rendered when the app starts and when a parent widget gets re-rendered, like MyApp.

  • Stateful widgets (subclasses of StatefulWidget), which you can re-render at any time by calling the setState() function, like this app’s MyHomePage, which is covered in The Home Page Widget.

The app widget is the collection of every other widget, so it is a widget. In this case it is a StatelessWidget.

Working with Stateless Widgets

 @override
 Widget ​build​(BuildContext context) {
 return​ MaterialApp(
  title: ​'Flutter Demo'​,
  theme: ThemeData(
  primarySwatch: Colors.blue,
  ),
  home: MyHomePage(title: ​'Flutter Demo Home Page'​),
  );
 }

This is the build method, which gets called when the widget needs to be rendered and returns a Widget object, which is every widget’s superclass.

In this case it gets called just once: when the app starts, given that MyApp doesn’t have a parent widget (it is every other widget’s parent widget).

In MyApp’s build method it is specified that the MyApp widget (the app itself) is a MaterialApp, which means that our app will use Material Design features that are part of the standard library.

The code sets, using the MaterialApp constructor, an app title, a theme with a primary swatch (a range of similar colors, as specified in Google’s Material Design guidelines), and a home page, which will be of class MyHomePage and have the title “Flutter Demo Home Page”.

The Home Page Widget

This defines the aforementioned MyHomePage class, which is a StatefulWidget (a widget that has a state, unlike MyApp) with a title member and a state, which is _MyHomePageState:

 class​ MyHomePage ​extends​ StatefulWidget {
  MyHomePage({Key key, ​this​.title}) : ​super​(key: key);
 final​ ​String​ title;
 
  @override
  _MyHomePageState createState() => _MyHomePageState();
 }

In Dart, the underscore (_) in _MyHomePageState has an effect akin to the one private has in languages like Java and is explained in The underscore.

The Advantage of Stateful Widgets

Stateful widgets have the ability to be re-rendered programmatically, allowing the developer to build an app that reacts to changes in the data it is showing or representing.

Because of this, they are more complex than stateless widgets: a stateful widget has a state, which is represented by a State object, which contains the widget’s logic and mutable data. As I mentioned in the previous section, widgets themselves are immutable, so they need a different object (the State) to hold the mutable data.

They get re-rendered when you call the setState() function, which notifies the framework that the state of the widget has changed and it needs to be re-rendered (by calling its build method again) to show the changes to the user.

The MyHomePage Widget

The line:

 MyHomePage({Key key, ​this​.title}) : ​super​(key: key);

defines MyHomePage’s arguments (as explained in Constructors). Since they are wrapped in braces, they are named arguments, which is why, when calling MyHomePage’s constructor, the syntax was:

 MyHomePage(title: ​'Flutter Demo Home Page'​)

The unused key parameter is used by the framework to identify which widgets to re-render. You can provide it and it can be useful, but it’s optional, and we won’t need it for this app.

We’ll talk about keys in the next chapter in The Key.

You can find more information on Dart class and constructor definitions in Classes.

Inside _MyHomePageState is where the app’s logic is defined:

 class​ _MyHomePageState ​extends​ State<MyHomePage> {
 int​ _counter = 0;
 
 void​ _incrementCounter() {
  setState(() {
  _counter++;
  });
  }
  @override
  Widget build(BuildContext context) {
 return​ Scaffold(
  appBar: AppBar(
  title: Text(widget.title),
  ),
  body: Center(
  child: Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
  Text(
 'You have pushed the button this many times:'​,
  ),
  Text(
 '​​$_counter​​'​,
  style: Theme.of(context).textTheme.display1,
  ),
  ],
  ),
  ),
  floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter,
  tooltip: ​'Increment'​,
  child: Icon(Icons.add),
  ),
  );
  }
 
 }
 class​ _MyHomePageState ​extends​ State<MyHomePage>

_MyHomePageState is defined as the state of the widget MyHomePage.

 int​ _counter = 0;
 
 void​ ​_incrementCounter​() {
  setState(() {
  _counter++;
  });
 }

This declares a counter and defines a function that increments the counter and triggers a re-render by changing the state of the widget (calling setState()).

The Main build Method for the Home Page

 @override
 Widget ​build​(BuildContext context) {
 return​ Scaffold(
  appBar: AppBar(
  title: Text(widget.title),
  ),
  body: Center(
  child: Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
  Text(
 'You have pushed the button this many times:'​,
  ),
  Text(
 '​​$_counter​​'​,
  style: Theme.of(context).textTheme.display1,
  ),
  ],
  ),
  ),
  floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter,
  tooltip: ​'Increment'​,
  child: Icon(Icons.add),
  ),
  );
 }

Use of a Widget to Provide the Basic App Structure: The Scaffold

This time the widget we return is a Scaffold, the basic Material Design visual layout structure; in other words, it is a generic container for all of our app content.

A Scaffold has an AppBar, which (using a Text widget) displays the title which was set earlier in MyHomePage’s constructor.

Build the App’s Body

The body of our Scaffold is wrapped inside a Center widget, which centers our content in the middle of the page.

The Center’s child (the content which it centers) is a Column, which displays its children in a vertical stack.

The first Text widget simply displays a String, whereas the second displays the counter with a built-in theme called display1.

Add the Floating Action Button

Not centered (but inside the Scaffold, outside the body, in its own floatingActionButton attribute) is the FloatingActionButton which, when pressed, calls the _incrementCounter function we saw previously.

Quite easy to understand are the tooltip code and the Icon widget (again, everything is a widget here) inside of it (its “child”) which is a simple plus (or “add”, as it is called) sign.

Every time we click the FAB the counter is incremented and the state changes, the setState() call triggers the build method again and the UI gets reloaded with the updated counter.

What We’ve Seen and Where We’re Going

This is a very simple app: it’s just a MaterialApp that contains a Scaffold, with a simple FAB in the lower right and, in the middle, a Column with two Text widgets showing the number of times the FAB has been clicked.

This is very simple, but the only way to really understand how something is done is by doing it, so the next section is going to focus on adding new features to the existing code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset