This tutorial shows how to create apps that have multiple screens. In App Inventor, you can have a screen open a second screen. Later the second screen can return to the screen that opened it. You can have as many screens as you like, but each screen closes by returning to the screen that opened it. The screens can share information by passing and returning values when they open and close. The screens also share the same TinyDB data, which they can use to store and shared values.
Building an app with multiple screens is a lot like creating several individual apps. Every screen that you create has its own components in the Designer window. In the Blocks Editor, you will be able to see only the components of the screen currently selected in the Designer. Similarly, the blocks of code related to a screen cannot refer to blocks of code in another screen.
The demonstration app is a simple drawing program called ColoredDots. It has two screens. ColoredDots is similar to PaintPot, but it uses a second screen to let the user create new colors by providing numerical values for the red, green, and blue color composition (RGB). In the PaintPot app, a user can only paint with one of the three predefined colors. Adding more colors to Paintpot would have required new buttons on the screen, thereby reducing the amount of space available for painting. In ColoredDots, the ability to choose a new color is implemented with a second screen called Settings. Once a new color has been created in the second screen, its value is passed back to the first screen. You can also save and name the colors you create in the second screen, and use them in painting.
Here are the two screens for the ColoredDots app:
You start building a multiscreen app by creating a new project, just as with any other app. As usual, App Inventor automatically creates the main screen and names it Screen1 and you can add components. Here's the Designer and the Components panel when all the components for Screen1 have been added.
The components are:
Component Type | Palette Group | What you'll name it | Purpose of Component |
Label | Basic | TitleLabel | Shows the title "Paint with Colored Dots" |
Label | Basic | ColorLabel | Shows the text "Current Color" |
Label | Basic | ColorSample | Blank label whose background color is the current color |
Label | Basic | DotSizeLabel | Shows the text "Dot size" |
Label | Basic | DotSizeValue | Shows the current dot size |
Canvas | Basic | Canvas1 | Shows dots at the places you touch |
Button | Basic | EraseButton | Clears the canvas |
Button | Basic | SettingsButton | Launches the second screen |
Button | Basic | GetColorButton | Loads the color from the data base |
Notifier | Other stuff | Notifier1 | Shows a dialog for entering a color name |
TinyDB | Basic | TinyDB1 | Stores color names and values |
We'll look at the blocks for Screen1 below. But first let's add the other screen.
To add a new screen to your app, click the Add Screen button in the top toolbar of the Designer window. A dialog window will appear, in which you can provide a name for the new screen.
Note: You should make the name of the new screen something meaningful when you add it. Do that now, because once a screen has been added, its name cannot be changed -- the Rename button for the screen component itself is disabled -- it's good to have meaningful names for the parts of your program. One limitation here is that Screen1 cannot be renamed. Remember also that the screen's title (shown in the title bar when the app is running) is different from its name. You can change the screen title in the Properties pane.
When you create a new screen, the Designer window switches to display it. Initially there will be no components, just like with a new app, and you add new components by dragging them from the Palette, just as with any screen. Here's the Designer window for the second screen, named SettingsScreen, after the components have been added. Notice that SettingsScreen is a dark green color, meaning SettingsScreen is current screen.
The components for the settings screen are:
Component Type | Palette Group | What you'll name it | Purpose of Component |
Label | Basic | RedLabel | Shows the text "Red" |
TextBox | Basic | Red | For entering the amount of red in the color (0-255) |
Label | Basic | GreenLabel | Shows the text "Green" |
TextBox | Basic | Green | For entering the amount of green in the color (0-255) |
Label | Basic | BlueLabel | Shows the text "Blue" |
TextBox | Basic | Blue | For entering the amount of blue in the color (0-255) |
Button | Basic | TestColorButton | Press to create the new color |
Label | Basic | LabelTestColorSample | Blank label whose background color is the new color |
Label | Basic | DotRadius | Shows the text "Dot size" |
TextBox | Basic | RadiusTextBox | Shows the current dot size |
Button | Basic | SaveAndQuitButton | Returns to the main screen with the new color and dot size |
Button | Basic | StoreColorButton | Names the color and stores it for use in the main screen |
Notifier | Other stuff | Notifier1 | Shows a dialog for entering a color name |
TinyDB | Basic | TinyDB1 | Stores color names and values |
Here and in general with multiple screen apps, only the components that belong to the current screen (in this case SettingsScreen) are shown in the Designer and the Components pane. You can switch between screens by pressing the SettingsScreen and the Screen1 buttons, and the view in the Designer will show the corresponding screen. The same is true for the Blocks Editor: When you're working on a screen you'll see only the blocks for that screen.
None of the components, variable definitions, and procedures that you define in one screen will be accessible from any other screen. We'll see below how to pass information between screens.
Warning: When you switch between screens, you are effectively telling the Blocks Editor to load a new project, and this takes a little time. Wait until the Blocks Editor is done loading the blocks for the new screen before changing things. Most especially, wait until the new screen has finished loading before clicking on blocks or links with the mouse. If you don't wait, you may get errors, or even lose blocks from your project.
The Screen1 is essentially a drawing program. When you touch the Canvas, the app draws a dot of the current radius and color. The color is specified by the background of the ColorSample label. The erase button clears the canvas. When the screen opens, it initializes the radius to 3 and the color to black. Here are the corresponding blocks:
The multiple screen aspect of the app involves getting the settings: the new color and the new dot radius. It's the job of SettingScreen to create these values and return these to Screen1 as a list of two items: the color and the dotsize. We'll see how SettingsScreen does this below, but from the perspective of Screen1, all it needs to do is open SettingsScreen and get the result.
Here's how this happens: when the Settings button is pressed, Screen1 uses open another screen to open SettingsScreen. When SettingsScreen returns, it will signal the other screen closed event, which provides the name of the screen (here SettingsScreen) and the value returned (here the two-element list). The event handler for Screen1 extracts the two items from the list and sets the ColorSample background and the DotSizeValue text properties. Here are the blocks:
In general, a screen opens another screen with open another screen and gets a result back though the when other screen closed event. It's also possible, although ColoredDots doesn't use this, for a screen to open another screen and pass it a value, by means of open another screen with value. The other screen can then access the value using get start value.
Besides opening screens and returning values, the different screens in a multiple screen app can communicate through TinyDB. To do this, give every screen its individual TinyDB component. Even though these will be "different" TinyDB components, they will in fact all share the same keys and values: If one screen stores a value under a key, the other screen can get that value by using the same key.
ColoredDots uses TinyDB to let you name the colors you create and save them to later use. The saving and naming will be done in SettingsScreen, as shown in the blocks below. When you press the GetColorButton in Screen1, the app uses the Notifier to ask you to enter the name of the color you want, and looks it up in TinyDB. If there's a color stored under that name, it sets the drawing color to the result.
The main job of SettingsScreen is to create a color from the red-green-blue values entered in the text boxes and provide that color to Screen1. When you click the TestColorButton, the color is created for you as the background of LabelTestColorSample. When you click SaveAndQuit the new color and the new dot size (taken from the RadiusTextBox) are returned to Screen1 as a two-item list.
To return the list to Screen1, SettingsScreen uses close screen with value, which takes as an argument the value to be returned (in this case, the list). When a screen closes, the app returns to the screen that opened it.
Here are the blocks that do this. SettingsScreen uses the procedures CheckColor and LimitRange to ensure that the values entered in the text boxes are valid numbers for colors and dot size. We'll examine these procedure below.
The next blocks show how SettingsScreen associates a name with a color for use by Screen1. When you click StoreColor the Notifier ask you to enter a name for the color. When you make your entry, the color is stored in TinyDB, using the name you entered as a key. Screen1 can then retrieve the color using its own TinyDB component.
The only thing left is to show how SettingsScreen checks that it's using good values for colors and dot size. Each of the red, green, blue values should be a number between 0 and 255. But you could have entered anything in those text boxes. The checkColor procedure takes a value and limits its range to between 0 and 255: If it's less than 0 (or not a number at all) the result will be 0. It it's greater than 255, the result will be 255:
The final bit is LimitRange. This is a general procedure that takes an input, a lower limit, and an upper limit, and restricts the input to lie within that range (and it returns the lower limit if the input is not a number). The procedure is simple, but tricky: To restrict the range, take the maximum of the input and the lower limit, then take the minimum of the result and the upper limit. This programming "trick" has little to do with multiple screens or App Inventor. You might want to take a few moments to convince yourself that it really works.
You can have many screens in an App Inventor app, but a screen always returns to the screen that opened it. On the other hand, you can get the effect of screens switching to arbitrary other screen by setting up a "manager screen" that is used for opening all the other screens. When a screen wants to switch, it returns to the manager with a value saying which screen to open next.
In the current version of App Inventor, it is not yet possible to test a multiple-screen app in live development mode. You can test each screen separately, but you cannot test the communication between two screens. When try to switch between screens, you'll get an error message. To test whether the screens are properly communicating, you'll need to Package for Phone and open the installed app.
This tutorial is based on work by Eni Mustafaraj of Wellesley College. Done with Colored Dots? Return to the other tutorials here.