Leave a rating/review
A key part of programming is state. Rather than start with the computer science definition of state, let’s go with something that might be a little more familiar: the dashboard of a car.
The most noticeable parts of a car dashboard are probably its gauges and odometers. They show the car’s current speed, fuel level, distance traveled, and so on, each of which is some kind of numeric quantity.
Dashboards also have warning lights, such as the low oil warning light, or the need maintenance “it’s time to take the car to the shop for some maintenance” light. Each of these lights is either on, indicating that there’s a problem that needs the driver’s attention, or off. This “on/off”, “true/false” information is called a boolean value in programming.
So the information on a car’s dashboard — such as speed, fuel level, whether or not the car needs maintenance — taken all together, is a visualization of the car’s state.
Keep in mind that the dashboard isn’t the car’s actual state - it’s just a visualization of it.
To see what I mean, think about what happens when the driver changes the car’s state. For example, the driver presses the accelerator, and the car starts moving faster. The dashboard then updates to show the new speed. So the car’s state is how fast the car is actually moving - and the dashboard is just helping the driver visualize that fact.
Internal circumstances can also change the car’s state. For example, as you drive the car. It consumes fuel. The car’s state is how much fuel remains either in the tank or in the battery, and the dashboard hopefully updates the fuel indicator.
But what happens if the car’s state and dashboard aren’t in sync? For example, what if your dashboard breaks, and doesn’t accurately show your car’s speed. Well that could be a big problem - you might get a ticket - or worse!
It turns out that this type of mistake is quite common while developing an app. That is, for your user, interface might not accurately represent the internal state of your app. You may have sometimes come across an app with a bug like this - for example, an app says you have 5 new messages, but when you check you actually have a different amount.
One of the nice things about Flutter, is that you develop your apps in such a way that your user interfaces and your state are always consistent, which prevents these frustrating types of bugs.
Let’s take another look at Bullseye, and think about what our app state would look like if we want to track whether or not a popup alert is visible when the user taps the Hit Me button.
Open your project in progress or download the starter app for this episode. In this demo, we’ll keep track of when the popup is visible. For this, we’ll add a variable to track state. In _GamePageState, add the following variable.
class _GamePageState extends State<GamePage> {
bool _alertIsVisible = false;
We’re creating a variable called alertIsVisible that can either be true or false. Remember the preceding underscore indicates it’s a private variable. It means it’s only to be used by the GamePageState and no one else. The bool tells Dart that the variable is a boolean, or a true/false value. Remember, Dart can infer the type so you can use the var keyword.
var _alertIsVisible = false;
It turns out that if when it comes to displaying a popup alert, there are only two possible conditions. Either the popup alert is on the screen, or it isn’t.
When the app first starts, the popup alert isn’t visible, so the state variable _alertIsVisible is false.
What we want to happen is that when the user taps the Hit Me button, we’ll change the state to reflect the fact that the alert is now visible and also write a method to the show the alert.
Now to update the alertIsVisible variable in the onPressed property of the TextButton. Add the following:
TextButton(
child:
const Text('Hit Me!', style: TextStyle(color: Colors.blue)),
onPressed: () {
_alertIsVisible = true;
},
),
Since we aren’t using the printHello method, feel free to delete it. Now to write the showAlert button. First we’ll define a private method that takes in a context.
void _showAlert(BuildContext context) {
}
A context represents the current widget in the widget tree and we can use it to access parent widgets or find out about screen location information. Now we’ll create a new button to dismiss the popup.
var okButton = TextButton(
child: const Text('Awesome!'),
onPressed: () {
Navigator.of(context).pop();
_alertIsVisible = false;
print('Awesome pressed! $_alertIsVisible');
},
);
The Navigator pop method does the actual dismissing, using the context and then sets the alertIsVisible property to false. Notice the print statement. The dollar sign allows you to put your variables in text known as strings. Now call the showDialog method.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Hello there!'),
content: const Text('This is my first pop-up.'),
actions: [
okButton,
],
elevation: 5,
);
},
);
This takes in a context and also provides a builder. In the builder, we return an AlertDialog widget that has a title, content and we provide some actions. The elevation is the z-position of the dialog that is how close or far away the dialog from its parent.
Now, update the Text Button to call showAlert.
onPressed: () {
_alertIsVisible = true;
_showAlert(context);
},
This now shows the alert dialog. Granted there’s been a lot of code and some of it seems really complex at the moment, but soon, you’ll be able to write this on your own. It just takes time and practice.
Now, build and run your app. Now tap the button. Look at that, you have a dialog. Nice work!