Get unlimited access to all of programming knowledges for less than 30 min
Chapter 5 Making Things Happen

Chapter 5

Making Things Happen

IN THIS CHAPTER

check What happens when you press buttons on a device’s screen

check The truth about widgets’ states

check How to remain anonymous

check How to move variables from one place to another

The day is October 20, 1952. In Kenya, the British colonial governor declares a state of emergency. In Philadelphia, actress Melanie Mayron (granddaughter of Frances Goodman) is born. In the US, an installment of “I Love Lucy” becomes the first TV episode ever to be broadcast more than once.

What? “I Love Lucy”? Yes, “I Love Lucy.” Until that day, television reruns (also known as “repeats”) didn’t exist. Everything on TV was brand-new.

Since then, repeat airings of TV programs have become the norm. So much of television’s content is a rehash of old video that broadcasters no longer advertise a “new episode.” Instead, they announce the airing of an “all-new episode.” The word new is no longer good enough. Common household products aren’t new; they’re “new and improved.”

Of course, hyping things as “new,” “the best,” or “the latest” can backfire. In fact, hyping of any kind can backfire. Consider the case of Stanley’s Swell Shaving Cream. Back in 1954, Stanley’s was the market leader. A year later, when sales were slowing down, advertisers rebranded it Stanley’s Neat New Shaving Cream. The year after that, it became Stanley’s Superior Shaving Cream. Sales of the product were okay for the next few years. But in the early 1960s, sales slumped and Stanley’s advertisers were in a bind. What could possibly be better than “Superior Shaving Cream”? Better than Best Shaving Cream? After several long meetings, a genius in the marketing department came up with Stanley’s Sensational Shocking Pink Shaving Cream — a brightly colored mixture of soap, glycerin, emollients, red dye number 2, and probably some slow-drying glue. That was the end of the line. The idea of shaving with a pink-colored cream wasn’t popular with consumers, and Stanley’s company went bankrupt. Consumers talked about Stanley’s Slimy Soap, Stanley’s Ruby Rubbish, and, worst of all, Stanley’s Disgusting Dung.

You may ask, “What in the world does Stanley’s Shaving Cream have to do with developing Flutter apps?” My point is, there’s a danger in overhyping a product, and overhyping an app development concept is no better. In Chapters 3 and 4, I use glowing terms to describe Flutter’s programming strategies, with its constructors, functions, and other good stuff. But here in Chapter 5, I cast aspersions on those introductory examples because none of them allows the user to change anything on the screen. An app that always displays the same old text is boring, and users will rate the app with zero stars. An interesting app interacts with the user. The app’s screen changes when the user enters text, taps a button, moves a slider, or does something else to get a useful response from the app. Making things happen is essential for any kind of mobile app development. So, in this chapter, you begin learning how to make things happen.

Let’s All Press a Floating Action Button

When you create a new Flutter project, Android Studio makes a main.dart file for you. The main.dart file contains a cute little starter app. Listing 5-1 has a scaled-down version of that starter app.

LISTING 5-1 Press a Button; Change the Screen

import 'package:flutter/material.dart';
 
void main() => runApp(App0501());
 
class App0501 extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
 
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
 
class _MyHomePageState extends State {
String _pressedOrNot = "You haven't pressed the button.";
 
void _changeText() {
setState(_getNewText);
}
 
void _getNewText() {
_pressedOrNot = "You've pressed the button.";
}
 
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(
_pressedOrNot,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _changeText,
));
}
}

Warning The code in Listing 5-1 captures the essence of the starter app in the October 2019 version of Android Studio. By the time you read this book, the creators of Flutter may have completely changed the starter app. If the stuff in Listing 5-1 bears little resemblance to the starter app you get when you create a new project, don’t worry. Just do what you’ve been doing. That is, delete all of Android Studio’s main.dart code, and replace it with the code in Listing 5-1.

When you launch the app in Listing 5-1, you see the text “You haven’t pressed the button” and, in the screen’s lower right corner, a blue circle. (See Figure 5-1.)

Snapshot of before pressing the button.

FIGURE 5-1: Before pressing the button.

That blue circle is called a floating action button. It’s one of the widgets that you can add to a Scaffold. When you click this app’s floating action button, the words on the screen change to “You’ve pressed the button.” (See Figure 5-2.)

Snapshot of after pressing the button.

FIGURE 5-2: After pressing the button.

At last! A Flutter app is making something happen!

To understand what’s going on, you have to know about two kinds of widgets. To learn their names, read the next section’s title.

Stateless widgets and stateful widgets

Some systems have properties that can change over time. Take, for example, your common, everyday traffic light. If it’s functioning properly, it’s either red, yellow, or green. Imagine that you’re hurrying to get to work and you stop for a red light. Under your breath, you may grumble, “I’m annoyed that this traffic light’s state is red. I wish that the state of that system would change to green.” A system’s state is a property of the system that may change over time.

Tip This tip has nothing to do with Flutter. If you meet someone from another country, ask them the color of the middle bulb on a traffic light. During a brief conversation with five people, I got yellow, amber, gold, and orange. See how many different color names you can collect.

The app in Listing 5-1 has a home page (named MyHomePage), and that home page is in one of two states. One state is shown in Figure 5-1. It’s the state in which the Text widget displays “You haven’t pressed the button.” The other state is shown in Figure 5-2. It’s the state in which the Text widget displays “You’ve pressed the button.”

In Listing 5-1, the first line of the MyHomePage class declaration is

class MyHomePage extends StatefulWidget

You want the look of the MyHomePage widget to be able to change itself nimbly, so you declare MyHomePage objects to be stateful widgets. Each MyHomePage instance has a state — something about it that may change over time.

In contrast, the App0501 class in Listing 5-1 is a stateless widget. The app itself (App0501) relies on its home page to keep track of whatever text is being displayed. So, the app has no need to remember whether it’s in one state or another. Nothing about an App0501 instance changes during the run of this code.

Think again about a traffic light. The part with the bulbs rests on a pole that’s fastened permanently to the ground. The entire assembly — pole, bulbs and all — doesn’t change. But the currents running through the bulbs change every 30 seconds or so. There you have it. The entire assembly is unchanging and stateless, but a part of that assembly — the part that’s responsible for showing colors — is changing and stateful. (See Figure 5-3.)

Snapshot of a riddle representing how a Flutter program is like a traffic light.

FIGURE 5-3: A riddle: How is a Flutter program like a traffic light?

Widgets have methods

In Listing 5-1, the declaration of the App0501 class contains a function named build. A function that’s defined inside of a class declaration is called a method. The App0501 class has a build method. That’s good because there’s some fine print in the code for StatelessWidget. According to that fine print, every class that extends StatelessWidget must contain the declaration of a build method.

A stateless widget’s build method tells Flutter how to build the widget. Among other things, the method describes the widget’s look and behavior. Whenever you launch the program in Listing 5-1, Flutter calls the App0501 class’s build method. That build method constructs a MaterialApp instance, which, in turn, constructs a MyHomePage instance. And so on. From that point onward, the MaterialApp instance doesn’t change. Yes, things inside the MaterialApp instance change, but the instance itself doesn’t change.

How often does your town build a new traffic light assembly? Where I live, I may see one going up every two years or so. The metal part of a traffic light isn’t designed to change regularly. The town planners call the traffic light assembly’s build method only when they construct a new light. The same is true of stateless widgets in Flutter. A stateless widget isn’t designed to be changed. When a stateless widget requires changing, Flutter replaces the widget.

What about stateful widgets? Do they have build methods? Well, they do and they don’t. Every stateful widget has to have a createState method. The createState method makes an instance of Flutter’s State class, and every State class has its own build method. In other words, a stateful widget doesn’t build itself. Instead, a stateful widget creates a state, and the state builds itself. (See Figure 5-4.)

Snapshot of stateful widgets that are not built in a day.

FIGURE 5-4: Stateful widgets weren’t built in a day.

A typical traffic light’s state changes every 30 seconds or every few minutes, and thus, the state of the light gets rebuilt. In the same way, the build method that belongs (indirectly) to a stateful widget gets called over and over again during the run of a program. That’s what stateful widgets are for. They’re nimble things whose appearance can easily change. In contrast, a stateless widget is like the pole of a traffic light. It’s a rigid structure meant for one-time use.

Pay no attention to the framework behind the curtain

A program that displays buttons and other nice-looking things has a graphical user interface. Such an interface is commonly called a GUI (pronounced “goo-ey,” as in “This peanut butter is really gooey”). In many GUI programs, things happen behind the scenes. While your app’s code runs, lots of other code runs in the background. When you run a Flutter app, code that was written by the creators of Flutter runs constantly to support your own app’s code. This background support code belongs to the Flutter framework.

Listing 5-1 has declarations for functions named main, build, createState, _getNewText, and _changeText, but the code in Listing 5-1 doesn’t call any of these functions. Instead, Flutter’s framework code calls these functions when a device runs the app.

Here’s a blow-by-blow description:

  • The Dart language calls the main function when the code in Listing 5-1 starts running.

    The main function constructs an instance of App0501 and calls runApp to get things going. Then …

  • The Flutter framework calls the App0501 instance’s build function.

    The build function constructs an instance of MyHomePage. Then …

  • The Flutter framework calls the MyHomePage instance’s createState function.

    The createState function constructs an instance of _myHomePageState. Then …

  • The Flutter framework calls the _myHomePageState instance’s build function.

    The build function constructs a Scaffold containing a Center with a Text widget and a FloatingActionButton widget.

To understand the Text widget’s constructor, look at a few lines of code:

String _pressedOrNot = "You haven't pressed the button.";
 
// Later in the listing …
 
child: Text(
_pressedOrNot,
),

Initially, the value of the _pressedOrNot variable is "You haven’t pressed the button." So, when the app starts running, the Text widget obediently displays “You haven’t pressed the button.”

But the floating action button’s code is a different story.

void _changeText() {
setState(_getNewText);
}
 
void _getNewText() {
_pressedOrNot = "You've pressed the button.";
}
 
// Later in the listing …
 
floatingActionButton: FloatingActionButton(
onPressed: _changeText,
)

The constructor for the FloatingActionButton has an onPressed parameter, and the value of that parameter is _changeText. What’s that all about?

The onPressed parameter tells Flutter “If and when the user presses the button, have the device call the _changeText function.” In fact, a lot of stuff happens when the user presses the floating action button. In the next few sections, you see some of the details.

The big event

In GUI programming, an event is something that happens — something that may require a response of some kind. The press of a button is an example of an event. Other examples of events include an incoming phone call, the movement of a device to a new GPS location, or the fact that one app needs information from another app.

An event handler is a function that’s called when an event occurs. In Listing 5-1, the _changeText function is a handler for the button’s onPressed event. In and of itself, the code onPressed: _changeText doesn’t call the _changeText function. Instead, that code registers the function _changeText as the official handler for floating action button presses.

Remember A call to the _changeText function would look like this: _changeText(). The call would end with open and close parentheses. The code onPressed: _changeText, with no parentheses, doesn’t call the _changeText function. That code tells the device to remember that the name of the button’s onPressed event handler is _changeText. The device uses this information when, and only when, the user presses the button.

Call me back

A phone rings four times. No one answers, but I hear a recorded announcement.

  • “This is Steve Hayes — executive editor at John Wiley and Sons. I’m sorry that I’m not here to take your call. Please leave a message, and I’ll get back to you as soon as I can.” <beep>
  • “Hello, Steve. This is Barry. The Flutter For Dummies manuscript is coming along nicely, but it’s going to be several months late. Please call me so we can discuss a new timetable. Don’t call me at my regular phone number. Instead, call me at my hotel in Taha’a, French Polynesia. The number is +689 49 55 55 55. Bye!”

My phone number in Taha’a is a callback number. In the same way, the functions _changeText and _getNewText in Listing 5-1 are callbacks. The line

onPressed: _changeText

tells the framework, “Call me back by calling my _changeText function.” And the line

setState(_getNewText)

tells the framework “Call me back by calling my _getNewText function.”

Callbacks are useful

You may have written programs that have no callbacks. When your program starts running, the system executes the first line of code, and keeps executing instructions until it reaches the last line of code. Everything runs as planned from start to finish. (Well, in the best of circumstances, everything runs as planned.)

A callback adds an element of uncertainty to a program. When will an event take place? When will a function be called? Where’s the code that calls the function? Programs with callbacks are more difficult to understand than programs with no callbacks.

Why do you need callbacks? Can you get away without having them? To help answer this question, think about your common, everyday alarm clock. Before going to sleep, you tell the alarm clock to send sound to your ears (a callback) when the 9 A.m. event happens:

on9am: _rattleMyEarDrums,

If you didn’t rely on a callback, you’d have to keep track of the time all night on your own. Like Bart and Lisa Simpson in the back seat of a car, you’d repeatedly be asking, “Is it 9 A.m. yet? Is it 9 A.m. yet? Is it 9 A.m. yet?” You certainly wouldn’t get a good night’s sleep. By the same token, if a Flutter program had to check every hundred milliseconds for a recent press of the button, there wouldn’t be much time for the program to get anything else done. That’s why you need callbacks in Flutter programs.

Technical Stuff Programming with callbacks is called event driven programming. If a program doesn’t use callbacks and, instead, repeatedly checks for button presses and other such things, that program is polling. In some situations, polling is unavoidable. But when event driven programming is possible, it’s far superior to polling.

The outline of the code

One good way to look at code is to squint so that most of it’s blurry and unreadable. The part that you can still read is the important part. Figure 5-5 contains my mostly blurry version of some code in Listing 5-1.

Snapshot of the state management strategy.

FIGURE 5-5: What to look for in Listing 5-1.

According to Figure 5-5, this is the state management strategy in Listing 5-1:

  1. Register _changeText as a callback function and wait for the user to press the floating action button.

    When, at last, the user presses the floating action button, …

  2. Have _changeText call setState, and pass _getNewText as the one-and-only parameter in the setState function call.

    The setState function calls _getNewText. When it does, …

  3. The _getNewText function does whatever it has to do with some text.

    The setState function also gets the Flutter framework to call build. When it does, …

  4. The stuff on the user’s screen is rebuilt.

    The rebuilt screen displays the new text.

There’s nothing special about the state management strategy in Listing 5-1. You can copy-and-paste this strategy into many other programs. Figure 5-6 shows you the general idea.

Snapshot of the name of the callback function in Flutter programs.

FIGURE 5-6: What to look for in many Flutter programs.

According to Figure 5-6, these steps form a state management strategy:

  1. Register a function as a callback function for an event and wait for that event to take place.

    In Figure 5-6, the name of the callback function is _handlerFunction. Like all such functions, the _handlerFunction takes no parameters and returns void.

    When, at last, the event takes place, …

  2. Have the callback function call setState and pass another function as the one-and-only parameter in the setState function call.

    In Figure 5-6, the name of this other function is _getNewInfo. Like all such functions, the _getNewInfo function takes no parameters and returns void.

    The setState function calls _getNewInfo (or whatever name you’ve used, other than _getNewInfo). When it does, …

  3. The _getNewInfo function changes something about the state of a widget.

    The setState function also gets the Flutter framework to call build. When it does, …

  4. The stuff on the user’s screen is rebuilt.

    The rebuilt screen displays the widget in its new state.

And so it goes.

C’mon, what really happens?

When you run a program that has a graphical user interface, lots of stuff happens behind the scenes. If you want, you can look at the framework’s code, but that code can be quite complex. Besides, with any decent framework, you shouldn’t have to read the framework’s own code. You should be able to call the framework’s functions and constructors by knowing only the stuff in the framework’s documentation.

I know for sure that, when Listing 5-1 runs, the setState call results in a call to _getNewText. I know this because, when I comment out the setState call, the text doesn’t change. But, I confess, I’m never completely comfortable with any GUI framework’s magic. I want some sense of the framework’s inner mechanisms, even if it’s only a rough outline. (I’m the same way with everything. I’m not sure that the light goes out when I close the refrigerator door.)

To that end, I present Figure 5-7. The figure summarizes the description of event handling in the previous few sections. It illustrates some of the action in Listing 5-1, including a capsule summary of the code in the setState function. Make no mistake: Figure 5-7 is an oversimplified view of what happens when Flutter handles an event, but you might find the figure useful. I learned some things just by drawing the figure.

Snapshot of the flutter responding to the press of a button.

FIGURE 5-7: Flutter responds to the press of a button.

Enhancing Your App

The code in Listing 5-1 is a simplified version of Android Studio’s starter app. That’s nice, but maybe you want to know more about the starter app. To that end, Listing 5-2 includes a few more features — features that enhance the look and behavior of the simple Flutter demo program.

LISTING 5-2 Inching Toward Android Studio’s Starter App

import 'package:flutter/material.dart';
 
void main() => runApp(App0502());
 
class App0502 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ’Flutter Demo’,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
 
class _MyHomePageState extends State {
int _counter = 0;
 
void _incrementCounter() {
setState(() {
_counter++;
});
}
 
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Listing 5-2"),
),
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),
),
);
}
}

Figures 5-8 and 5-9 show a run of the code in Listing 5-2. Figure 5-8 is what you see when the app starts running, and Figure 5-9 is what you see after one click of the floating action button. On subsequent clicks, you see the numbers 2, 3, 4, and so on.

Snapshot of before pressing the first button.

FIGURE 5-8: Before the first button press.

Snapshot of after pressing the first button.

FIGURE 5-9: After the first button press.

Whenever the user clicks the floating action button, the number on the screen increases by 1. To make this happen, Listing 5-2 has three references to the variable named _counter. Figure 5-10 illustrates the role of the _counter variable in the running of the app.

The app’s Text widget displays the value of the _counter variable. So, when the app starts running, the Text widget displays 0. When the user first presses the floating action button and the Flutter framework calls setState, the _counter variable becomes 1. So, the number 1 appears in the center of the app’s screen. When the user presses the action button again, _counter becomes 2, and so on.

More parameters, please

Listing 5-2 introduces some tried-and-true constructor parameters. For example, the MaterialApp constructor has title and theme parameters.

  • The title (in this example, Flutter Demo) appears only on Android phones, and only when the user conjures up the Recent Apps list.
  • The value of theme is a ThemeData instance (thus, the use of the ThemeData constructor in Listing 5-2).

    Snapshot of updating the Text widget.

    FIGURE 5-10: Updating the Text widget.

    In the world of app design, themes are vitally important. A theme is a bunch of choices that apply to all parts of an app. For example, “Use the Roboto font for all elements that aren’t related to accessibility” is a choice, and that choice can be part of a theme.

    The choice made in Listing 5-2 is “Use the blue color swatch throughout the app.” A swatch is a bunch of similar colors — variations on a single color that can be used throughout the app. The Colors.blue swatch contains ten shades of blue, ranging from very light to very dark. (For a look at some pretty swatches, see https://api.flutter.dev/flutter/material/Colors-class.html.)

    As an experiment, run the code in Listing 5-2, and then change Colors.blue to Colors.deepOrange or Colors.blueGrey. When you save the change, all elements in the app suddenly look different. That’s cool! You don’t have to specify each widget’s color. The theme maintains a consistent look among all widgets on the screen. For a big app with more than one page, the theme maintains a consistent look from one page to another. This helps the user understand the flow of elements in the app.

In Listing 5-2, a Text widget’s style parameter uses a roundabout way to get a TextStyle instance. The code Theme.of(context).textTheme.display1 represents a TextStyle with large text size. Figure 5-11 shows you the options that are available when you use Theme.of(context).textTheme.

Snapshot of Flutter's TextTheme styles.

FIGURE 5-11: Flutter’s TextTheme styles.

Warning A particular style may be too large for the screens on some phones. For example, to create Figure 5-11, I ran an emulator with a Pixel 3 XL virtual device. But with a plain old Pixel 3 in portrait mode, the word display4 is too large for the width of the screen. The digit 4 appears on a line of its own.

As it is with the MaterialApp theme, the notion of a text theme is mighty handy. When you rely on Flutter’s Theme.of(context).textTheme values, you provide a uniform look for all the text elements in your app. You can also take comfort in the fact that you’re using standard values — nice-looking values chosen by professional app designers.

On the web Names like display1 in the Flutter API don’t correspond exactly to the names in Google’s Material Design specifications, and I suspect that the options available in Flutter’s API will change soon. For more on the Material Design specs, visit this page:

https://material.io/design/typography/#

Finally, the floating action button in Listing 5-2 has tooltip and child parameters.

  • The tooltip string shows up when a user long-presses the button.

    When you touch the screen and keep your finger in the same place for a second or two, you’re long-pressing that part of the screen. The app in Listing 5-2 displays the word Increment whenever the user long-presses the floating action button.

  • For the button’s child, you construct an Icon instance.

    The Icon instance displays a tiny image from Flutter’s Icons class; namely, the Icons.add image. Sure enough, that image is a plus sign. (Refer to Figures 5-8 and 5-9.)

    On the web For a list of images in Flutter’s Icons class, visit

    https://api.flutter.dev/flutter/material/Icons-class.html

You can read more about parameters in Listing 5-2 and discover other useful parameters by visiting Flutter’s documentation pages. For a brief introduction to those pages, refer to Chapter 3.

The override annotation

The line @override, which appears several times in Listing 5-2, is called an annotation. In Dart, an annotation begins with the at-sign (@).

A statement, such as _pressedOrNot = "You’ve pressed the button.", tells Dart what to do during the run of a program. But an annotation is different. An annotation tells Dart something about part of a Dart program. An @override annotation reminds Dart that the class you’re extending has a matching declaration.

For example, consider the following code in Listing 5-2:

class App0502 extends StatelessWidget {
@override
Widget build(BuildContext context) {

The line @override says “The StatelessWidget class, which this App0502 class extends, has its own build(BuildContext context) method declaration.” And indeed, according to this chapter’s earlier sidebar “I’m talking to you, stateless widget — you must have a build method!” the StatelessWidget class in the Flutter API code has a build(BuildContext context) method with no body. It all works out nicely.

Listing 5-2 has @override annotations, but Listing 5-1 doesn’t. Look at that! You can get away without having @override annotations! So, why bother having them?

The answer is “safety.” The more information you give Dart about your code, the less likely it is that Dart will let you do something wrong. If you make a mistake and declare your build method incorrectly, Dart might warn you. “Hey! You said that you intend to override the build method that’s declared in the StatelessWidget class, but your new build method doesn’t do that correctly. Fix it, my friend!”

Crossreference You can make Dart warn you about methods that don’t match with their @override annotations. For details, visit https://dart.dev/guides/language/analysis-options.

What does <Widget> mean?

In Listing 5-2, the column’s list of children starts with some extra stuff:

children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
]

The <Widget> word, with its surrounding angle brackets, is called a generic, and a list that starts with a generic is called a parameterized list. In Listing 5-2, the <Widget> generic tells Dart that each of the list’s values is, in one way or another, a Widget. According to Chapter 3, every instance of the Text class is an instance of the Widget class, so the <Widget> generic isn’t lying.

In many situations, the use of generics is a safety issue. Consider the following two lines of code:

var words1 = ["Hello", "Goodbye", 1108]; // No error message
var words2 = <String>["Hello", "Goodbye", 1108]; // Error message!

You may plan to fill your list with String values, but when you declare words1 and words2, you accidentally include the int value 1108. The words1 list isn’t parameterized, so Dart doesn’t catch the error. But the words2 list is parameterized with the <String> generic, so Dart catches the mistake and refuses to run the code. An error message says The element type ’int’ can’t be assigned to the list type ’String’. To this, you should respond, “Good catch, Dart. Thank you very much.”

Anonymous functions

In the Dart programming language, some functions don’t have names. Take a look at the following code:

void _incrementCounter() {
setState(_addOne);
}
 
void _addOne() {
_counter++;
}

Imagine that your app contains no other references to _addOne. In that case, you’ve made up the name _addOne and used the name only once in your app. Why bother giving something a name if you’ll be using the name only once? “Let’s give this ear of corn the name ’sinkadillie’. And now, let’s eat sinkadillie.”

To create a function with no name, you remove the name. If the function’s header has a return type, you remove that too. So, for example,

void _addOne() {
_counter++;
}

becomes

() {
_counter++;
}

When you make this be the parameter for the setState function call, it looks like this:

void _incrementCounter() {
setState(() {
_counter++;
});
}

That’s what you have in Listing 5-2.

A function with no name is called an anonymous function. When an anonymous function contains more than one statement, those statements must be enclosed in curly braces. But if the function contains only one statement, you can use fat arrow notation. For example, in Listing 5-2, the following code would work just fine:

void _incrementCounter() {
setState(() => _counter++);
}

What belongs where

In Listing 5-2, the _counter variable’s declaration is inside the _MyHomePageState class but outside of that class’s _incrementCounter and build methods. A variable of this kind is called an instance variable or a field. (It depends on whom you ask.)

Why did I declare the _counter variable in that particular place? Why not put the declaration somewhere else in the code? I could write a whole chapter to answer the question in detail, but you don’t want to read all that, and I certainly don’t want to write it. Instead, I suggest some experiments for you to try:

  1. Starting with the code in Listing 5-2, add a reference to _counter inside the MyHomePage class. (See Figure 5-12.)

    Snapshot of references to the boldface _counter variable that are valid only inside the gray box.

    FIGURE 5-12: References to the boldface _counter variable are valid only inside the grey box.

    Android Studio marks this new reference with a jagged red underline. The underline shames you into admitting that this additional reference was a bad idea. You’ve declared the _counter variable in the _MyHomePageState class, but you’re trying to reference the variable in a different class; namely, the MyHomePage class.

    Whenever you declare a variable inside of a class, that variable is local to the class. You can’t refer to that variable outside the class. In particular, you can’t refer to that variable inside a different class.

    Crossreference Don’t you hate it when authors contradict themselves? There is a way to refer to a variable outside of its class’s code. I cover it in detail in Chapter 7.

  2. Remove the reference to _counter that you added in Step 1. Then move the declaration of _counter to the end of the _MyHomePageState class. (See Figure 5-13.)

    Snapshot of references to the boldface _counter variable that are valid inside the gray box.

    FIGURE 5-13: References to the boldface _counter variable are valid inside the grey box.

    Near the start of the _MyHomePageState class, you do _counter++. But you don’t declare the _counter variable until the end of the _MyHomePageState class. Nevertheless, the program runs correctly. The moral of this story is, you don’t have to declare a variable before you refer to that variable. Nice!

  3. Move the declaration of _counter so that it’s inside the body of the _incrementCounter function. (See Figure 5-14.)

    When you do, you see an error marker on the occurrence of _counter in the build function. You’ve declared the _counter variable inside the _incrementCounter function, but you’re trying to reference that variable in a different function; namely, the build function.

    Whenever you declare a variable inside a function, that variable is local to the function. You can’t refer to that variable outside the function. In particular, you can’t refer to that variable inside a different function.

    Snapshot of references to the boldface _counter variable that are valid only inside the gray box.

    FIGURE 5-14: References to the boldface _counter variable are valid only inside the grey box.

  4. Keep the declaration of _counter inside the _incrementCounter function, and add another _counter declaration inside the build function. Initialize the build function’s _counter variable to 99. (See Figure 5-15.)

    When you do this, the error message from Step 3 goes away. So the code is correct. Right?

    No! The code isn’t correct. When you run the code, the number in the center of the device is 99, and its value never changes. Pressing the floating action button has no effect. What’s going on?

    With this revised code, you have two different _counter variables — one that’s local to the _incrementCounter function and another that’s local to the build function. The statement _counter++ adds 1 to one of these _counter variables, but it doesn’t add 1 to the other _counter variable. It’s like having two people named Barry Burd — one living in New Jersey and the other in California. If you add a dollar to one of their bank accounts, the other person doesn’t automatically get an additional dollar.

    Snapshot of the user can refer to one _counter variable only in the upper region and can also refer to the other _counter variable only in the lower region.

    FIGURE 5-15: You can refer to one _counter variable only in the upper grey region; you can refer to the other _counter variable only in the lower grey region.

  5. Have only one _counter declaration. Put it just before the start of the _MyHomePageState class. (See Figure 5-16.)

    After making this change, the editor doesn’t display any error markers. Maybe you click the Run icon, anticipating bad news. Either the app doesn’t run, or it runs and behaves badly. But, lo and behold, the app runs correctly!

    A declaration that’s not inside a class or a function is called a top-level declaration, and a top-level name can be referenced anywhere in your program. (Well, almost anywhere. There are some limits. In particular, see the later section “Names that start with an underscore.”)

  6. Have two _counter variable declarations — one at the top level, and another inside the _MyHomePageState class. Initialize the top-level _counter to 2873 and the latter _counter to 0. (See Figure 5-17.)

    Before testing this version of the code, end the run of any other version. Start this version of the code afresh.

    Snapshot of using a top-level name anywhere in the .dart file.

    FIGURE 5-16: Use a top-level name anywhere in your .dart file.

    When this modified app starts running, the number in the center of the screen is 0, not 2873. The top-level declaration of _counter has no effect because it’s shadowed by the declaration in the _MyHomePageState class.

    The _counter declaration in the _MyHomePageState class applies to the code inside the _MyHomePageState class. The top-level _counter declaration applies everywhere else in this file’s code.

Technical Stuff This section is all about classes, methods, and variables. The section describes an instance variable as a variable whose declaration is inside of a class, but not inside any of the class’s methods. That’s almost a correct description of an instance variable. To be precise, an instance variable’s declaration is one that doesn’t contain the word static @@ a word that you encounter in Chapters 7 and 8. Until you read Chapters 7 and 8, don’t worry about it.

Names that start with an underscore

Someday soon, when you’re a big-shot Flutter developer, you’ll create a large, complicated app that involves several different .dart files. A file’s import statements will make code from one file available for use in another file. But how does this work? Are there any restrictions? Figure 5-18 says it all.

Snapshot of initializing the top-level _counter to 2873 and the latter _counter to 0.

FIGURE 5-17: The Shadow knows!

Snapshot of an import statement, the amount variable tha is available in both one_file.dart and another_file.dart.

FIGURE 5-18: “I got plenty numbers left.” (Google it.)

A variable or function whose name begins with an underscore (_) is local to the file in which it’s declared and can’t be referenced in other .dart files. All other names can be imported and shared among all the files in an application. In Figure 5-18, the _number variable can be used only in one_file.dart. But, because of an import statement, the amount variable is available in both one_file.dart and another_file.dart.

Technical Stuff If you’re used to writing code in languages like Java, forget about access modifiers such as public and private. The Dart language doesn’t have those things.

Whew!

This is a heavy-duty chapter. If you’ve spent the evening reading every word of it, you’re probably a bit tired. But that’s okay. Take a breather. Make yourself a cup of tea. Sit in your easy chair, and relax with a performance of The Well-Tempered Clavier (Praeludium 1, BWV 846).

Chapter 6 continues the theme of widgets responding to user actions. In that chapter, you slide sliders, switch switches, drop dropdown lists, and do other fun things. Go for it (but don’t forget to unwind a bit first)!