Notes: 10. Understand the Red Screen of Death
The student materials have been reviewed and are updated as of SEPTEMBER 2022.
So far, we’ve encountered errors that displays its details mainly in the debug console and sidebar. The Overflow warning sign is shown on the UI to let you know that an overflow occured then you open up the debug console to get more info about the error.
When we encountered errors with scroll views, the widgets would not be updated on the UI. Instead an exception would be thrown. And once again you would open up the debug console or read the exception message in the debug sidebar to get more info.
Now, all these errors occur during the laying out phase of our widgets. This means that the build method has been successful and Flutter tries to lay out the widgets on the screen. Now, what happens when the build method fails to build?
Well, you get the Red Screen Error popularly known as the “Red Screen of Death.” It might look scary and intimidating at first but Flutter is just trying to tell you: “Hey! There is a problem during the build phase and i don’t know what to display.”
So Flutter invokes the ErrorWidget.builder callback which displays an error message widget. In debug mode, it shows the error with a red background and in release mode, it shows it with a gray background.
Before null safety was introduced in Dart, the common way the red screen error occured with null values. Like when you return null from a build method.
Or when a widget expects a non-null value like a string and then you pass in null. Before null safety, both of these examples would fail and the red screen would be displayed. Now, with null safety, these code samples would not even run because dart by default expects a value to never to be null unless explicitly stated. So the linter would report an error.
Now, if null safety eliminates the red screen error then in which other scenario can these error screen show up? Let’s head over to VSCode and explore an example.
A common scenario where you could encounter this screen is when working the BuildContext of a widget. Your code could be trying to use an object that does not yet exist in the current context.
In this example, we are returning a Container that takes half of the screen’s width and height.
We achieve this using the MediaQuery.of() static method. This method gives us access to the size and orientation of the screen.
And this possible because the MaterialApp above introduces the MediaQuery. And the MediaQuery class is an InheritedWidget so we can access its members from anywhere down the widget tree. The context argument is important because it contains information about the enviroment or scope of the widget.
So with this, it knows if a MediaQuery is in the current scope and in this case it is because the MaterialApp introduced it. Now, lets take take the contents of the body of the Scaffold and pass it directly to the home property of the MaterialApp.
home: Scaffold(
appBar: AppBar(
title: const Text('Wazobia App'),
),
body: Center(
child: Container(
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
color: Colors.amber,
),
),
),
We encounter an exception displayed with a red background which shows that we’re expecting a red screen. It also points to the code causing it. The error message is also self-descriptive. I’ll go ahead and hit the continue button.
And now we have the Red Screen error displayed in our UI. And thumbs up to you if you understood why that happened. Well, like I explained earlier, the context passed here needs to have the MediaQuery in scope but it doesnt. And this is because the context passed here is from the build method above and this belongs to MyApp widget.
And if you look above the tree, you wont find a MaterialApp or WidgetsApp above it as an ancestor. So this means that there is no MediaQuery information of the device at this point.
And these reasons makes Flutter unable to know how to build the widget so it displays the red screen. We could solve this by putting this code inside a widget just like it was before or we could wrap this code in a Builder if we dont want to create a separate widget for this code.
Let use the Builder class:
body: Builder(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Wazobia App'),
),
body: Center(
child: Container(
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
color: Colors.amber,
),
),
);
},
),
And the red screen error is gone. The basic explanation for this is that: when you use a Builder widget, the context you’re referring to inside it is the context from the method that just returned. And that is build method of the MyApp widget which definately contains the MediaQuery introduced by the MaterialApp.
And now, for a more detailed explanation. The Builder widget simply works by calling a closure to obtain its child widget. And this is the callback function that is passed in the builder property.
So the way closures work is that they have access to variables from where they were declared even after that method has been completed or returned. In this case, MyApp‘s build method widget would construct a new tree in which it’s context data is now available to the child of the Builder.
So with this, the context in the Builder would be referring to the one returned from the build method of the MyApp widget. And this contains the MediaQuery.