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 share 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.
This program, ColoredDots, 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, reducing the amount of space available for painting. In ColoredDots, the new color is chosen on a second screen called Brush_Picker. Once a new color has been created in this 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, then use the saved colors later when painting. By using ListPicker to store the colors you create, it eliminates the need for the user to remember the name of the color they saved and spell it correctly in accessing it again. This makes the database of stored colors more accessible, making it easier to share the app among a group of people.
Here are the two screens for the ColoredDots app:
By the end of this tutorial, you should have a understanding of:
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 Viewer 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||Component Settings|
|Label||User Interface||TitleLabel||Shows the title "Paint with Colored Dots"||Change Properties to FontBold and set FontSize to 16, TextAlignment to center, and Width to Fill parent|
|HorizontalArrangement||Layout||HorizontalArrangement1||Holds the following 4 labels:||Set Width to Fill Parent and AlignHorizontal to Center|
|Label||User Interface||ColorLabel||Shows the text "Current Color"|
|Label||User Interface||ColorSample||Blank label whose background color is the current color which we initialize to be black|
|Label||User Interface||DotSizeLabel||Shows the text "Dot size"|
|Label||User Interface||DotSizeValue||Shows the current dot size. We will initialize this to 3.|
|Canvas||Drawing and Animation||Canvas1||Shows dots at the places you touch||Set the width to Fill parent and the height to 300 pixels|
|HorizontalArrangement||Layout||HorizontalArrangement2||Holds the following 2 Buttons and ListPicker||Set Width to Fill Parent and AlignHorizontal to Center|
|Button||User Interface||EraseButton||Clears the canvas||Set Text to "Erase"|
|Button||User Interface||openBrushPicker||Launches the second screen||Set Text to "Customize Brush"|
|ListPicker||User Interface||ListPicker1||Loads the list of colors from the data base||Set Text to "Stored Colors"|
|TinyDB||Storage||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 practice 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 Brush_Picker, after the components have been added. Notice that Brush_Picker is selected in the dropdown on the top bar outlined in red. This is because it is the current screen.
The components for the brush picker screen are:
|Component Type||Palette Group||What you'll name it||Purpose of Component||Component Settings|
|Label||User Interface||RedLabel||Shows the text "Red"|
|TextBox||User Interface||Red||For entering the amount of red in the color (0-255)||Set NumbersOnly|
|HorizontalArrangement||Layout||RedHorizontalArrangement||Holds RedLabel and Red TextBox|
|Label||User Interface||BlueLabel||Shows the text "Blue"|
|TextBox||User Interface||Blue||For entering the amount of blue in the color (0-255)||Set NumbersOnly|
|HorizontalArrangement||Layout||BlueHorizontalArrangement||Holds BlueLabel and Blue TextBox|
|Label||User Interface||GreenLabel||Shows the text "Green"|
|TextBox||User Interface||Green||For entering the amount of green in the color (0-255)||Set NumbersOnly|
|HorizontalArrangement||Layout||GreenHorizontalArrangement||Holds GreenLabel and Green TextBox|
|VerticalArrangement||Layout||VerticalArrangement1||Holds Color HorizontalArrangements|
|Canvas||Drawing and Animation||Canvas1||Displays the size and color of the test color||Set Width and Height to Fill parent|
|HorizontalArrangement||Layout||HorizontalArrangement1||Holds VerticalArrangement1 and Canvas1||Set Width to Fill parent|
|Label||User Interface||DotRadius||Shows the text "Dot Size"|
|TextBox||User Interface||RadiusTextBox||Shows the current dot size||Set NumbersOnly|
|HorizontalArrangement||Layout||RadiusHorizontalArrangement||Holds DotRadius and RadiusTextBox|
|Button||User Interface||TestColorButton||Press to create the new color and prompts the user to store this color||Set Text to "Test Color"|
|Label||User Interface||LabelTestColorSample||Blank label whose background color is the new color||Set Width to Fill parent|
|HorizontalArrangement||Layout||TestHorizontalArrangement||Holds TestColorButton and LabelTestColorSample||Set Width to Fill parent|
|Button||User Interface||ResetColorButton||Resets all the values to the default color, black||Set Text to "Reset Brush Selector"|
|Button||User Interface||ReturnToPainting||Returns to the main screen with the new color and dot size||Set Text to "Return to Painting" and FontSize to 16|
|VerticalArrangement||Layout||VerticalArrangement2||Holds ResetColorButton and ReturnToPainting||Set Width to Fill Parent and AlignHorizontal to Center|
|Notifier||User Interface||Notifier1||Shows a dialog for saving the color and entering a color name|
|TinyDB||Storage||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 Brush_Picker) are shown in the Designer and the Components pane. You can switch between screens by toggling between Brush_Picker and Screen1 using the dropdown, 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.
Screen1 is essentially a drawing program. When you touch the Canvas, the app draws a dot of the current color and radius. 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:
It is worth noting that the Initialize block uses the procedures TinyDB1.GetValue and TinyDB1.StoreValue. First, we look in TinyDB for any existing colorList that has been stored. If nothing is there, we set the colorList to just the default color, black. Just in case there is nothing stored, we call TinyDB1.StoreValue again to store the default black color.
Also note that the colors will be stored as a list of lists. Each sublist contains the color name, its internal RGB color value, and its radius. So, initially, we store a list that contains a single list of the information for the default black color.
Testing the behavior: Test to make sure the drawing behavior acts as you expect it to. You won't be able to add new colors yet, but see what happens when you try to draw dots of the page.
The multiple screen aspect of the app involves getting the "new brush": the new color and the new dot radius. It's the job of Brush_Picker to create these values and return these to Screen1 as a list of two items: the color and the dotsize. We'll see how Brush_Picker does this below, but from the perspective of Screen1, all it needs to do is open Brush_Picker and get the result. It also needs to update the stored list of colors, as more colors are added.
Here's how this happens: when the Customize Brush button is pressed, Screen1 uses open another screen with start value to open Brush_Picker and pass the current color and radius. When the user finishes with Brush_Picker, it other screen closed event is triggered. It provides the name of the screen that has closed (here Brush_Picker) and the result returned (here the two-element list for the new color and size). The event handler for Screen1.OtherScreenClosed extracts the two items from the list and sets the ColorSample background and the DotSizeValue text properties. Here are the blocks:
ColoredDots uses open another screen with value to open Brush_Picker with values passed to it from Screen1. The other screen then accesses these opening values using get start value blocks. In general, a screen opens another screen with open another screen and gets a result back through the when other screen closed event.
Besides opening screens and returning values, the different screens in a multiple screen app can communicate through TinyDB. To do this, every screen must have a TinyDB component. Even though these will be "different" TinyDB components, they will in fact all share the same storage space, and tags and values. If one screen stores a value under a tag, the other screen can get that value by using the same tag.
ColoredDots uses TinyDB to let you name the colors you create and save them to use later. The saving and naming will be done in Brush_Picker, as shown in the blocks later. When the user opens ListPicker1 (labelled Stored Colors) in Screen1, the app opens the list of colors that you have stored in TinyDB. The user chooses one and then the drawing color and dot size is set based on the choice. The elements are added to this list by the procedure populateList. As shown in these blocks, populateList iterates through the all of the stored entries in the database up to the number of stored colors and adds them to the list tinyDBlist. It returns this list, which ListPicker1 points to.
Now that populateList has been created, add the following to Screen1.Initialize and Screen1.OtherScreenClosed :
When ListPicker1 is opened, the user will see a list of all the colors that have been created on the Brush_Picker screen. ListPicker1.AfterPicking gets the sublist for the chosen color from the main colorList, and then pulls out the color value and dot size so any dots drawn will use that color and size.
The main job of Brush_Picker is to allow the user to create a color using red-green-blue values in the text boxes and to choose a dot size. The values will be passed back to Screen1 and also appended to the TinyDB color list.
When Brush_Picker initializes, the current color and current radius will be set using variables, based on the start value passed from Screen1. Remember that is was passed as a list of two elements. Once those values are set, the will appear in the TestColorSample and drawn on the Canvas. We'll make a procedure, called drawColor.
Brush_Picker needs to check is 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 the user 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. If it's greater than 255, the result will be 255.
The checkColor procedure calls the limitRange procedure. 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.
Below are the blocks for checkColor and limitRange. Remember that procedures are mutator blocks and we can use the mutator button to add (and change the names of) parameters to our procedure. To review mutators, check out the mutators page.
When TestColorButton is clicked, the color is set as the background of LabelTestColorSample and the Canvas displays a circle of the appropriate size and color. Notifier1 is then used to give the a choice to save the color or not using the Notifier1.ShowChooseDialog block. The response from this will trigger another Notifier event so that the user can name and save the color.
Here are the blocks that do this. The procedures CheckColor and LimitRange are used to ensure that the values entered in the text boxes are valid numbers for colors and dot size.
The Notifier1.ShowChooseDialog block triggers a Notifier1.AfterChoosing event. In this event, the if-else block checks the response to whether or not the color should be saved. If the response is "Yes", Notifier1.AfterChoosing calls Notifier1.ShowTextDialog which prompts the user to input a name for this color and dot size.
Once the user makes their entry, Notifier.AfterTextInput is triggered, and the color and dot size is appended as a two-item list to the stored color list in TinyDB. Screen1 can then retrieve the updated list from TinyDB.
When the users clicks ReturnToPainting, the current color and the current dot size are passed back to Screen1 as a two-item list, using close screen with value. When a screen closes, the app returns to the screen that opened it.
The Reset button allows the user to clear all of the entries in the red, green, and blue fields so they can start over easily.
Note: you can use copy paste (ctl-v and ctl-c) after creating the first set Red.Text then you can use the dropdown to change from red to green to blue.
You can have many screens in an App Inventor app, but a screen always returns to the screen that opened it when it is closed. On the other hand, you can get the effect of screens switching to arbitrary other screens 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.
Testing the behavior. App Inventor 2 allows multi-screen app testing during live development.
Now that you are able to create your own colors and access them for drawing, try some variations!
In this tutorial you have learned:
This tutorial is based on work by Eni Mustafaraj of Wellesley College.
Done with Colored Dots? Return to the other tutorials here.