Building a Language Learning Game with FlutterFlow & Firebase
Pooja Bhaumik
GDE (Flutter) |
Developer Advocate at FlutterFlow
@pooja_bhaumik�
Eilidh Southren
Enterprise Engineer at FlutterFlow
Create your account at app.flutterflow.io
Starter Project
bit.ly/match-starter
PRE-WORKSHOP MATERIALS
This Slide Deck
bit.ly/match-ff
Workshop Objectives
Section 1: Create reusable UI Components�
Section 2: Implement game logic�
Section 3: Add Firebase for user and game data
Let’s take a look at the finished game!
Set the Color theme of your project
This is already done in your Starter Project
Set the Loading Indicator based on your design theme
This is already done in your Starter Project
Go to Typography & Icons and set the Secondary Font Family to Pacifico
This is already done in your Starter Project
Home page and Game page will already be created for you but if you want to create a new Page later, this is how you can do!
Tip: To rename a page, just double click on it
And type the new name.
Building a custom Button
Page: Home
To align the Stack and its children to the Center, go to the Alignment property and click on the center grid so it is horizontally and vertically aligned in the center.
The button properties we changed:
Add Left and Right padding to 10 each.
The first version of the Button will look like this:
But let’s add a 3D effect like the following:
Add a Container to Stack and move it around so it is the first child of Stack and behind Button.
The Container properties:
Now click on the Button from Widget Tree, the Button properties will open on your right.
Choose the second button in the top right and click on Actions.
This will open the Actions properties where you can assign various user Actions or various logic actions
Lets click on Open on Action Flow Editor
A dialog will open with an Add Action button, click on that and it will take you to this screen where you have a ‘Not Defined’ action with a drop down open.
Choose “Navigate To” and then choose “Game” as you want to Navigate to Game Page on Start Button click.
Let’s run the app to Test
Tip: While it loads, you can also check out the generated code here Developer Menu > View Code if you’re curious.
Convert to Component so you can reuse this button in various pages
Name the component & Create!
Since this is a reusable component now, we must make sure all hard coded data is provided dynamically through component parameters.
Create 3 Component Parameters
with the following properties. �Make sure to click Confirm to set these params.
Now instead of hard coding the value “Start”, it should be taken from label parameter
Now instead of a hardcoded value, you see a [variableName] in the button Text
You can also use the isDisabled parameter to determine the button appearance
The action should also be determined by the page using the component. So let’s remove the current Navigation action from the custom button and add a dynamic action.
Let’s provide these values from the Home Page.
label: “Start”
isDisabled: lets keep it false / null for now, this can be taken from a local variable later
onTap: Same “Navigate To” action to Game page
Let’s add the UI for the Game Page now.
Now add a Row of two Columns that will contain our list of Word Tile
Add the Expand property for each Column so they expand the width
Since we will be repeating the widget used for Word Tile, we can directly create a Custom Component (called WordTile) and then build its UI
This is already present in your starter project
Component Parameters for WordTile:��word (String)�onTap (Action)�isSelected(Boolean)�inCorrectState(Boolean)
inIncorrectState(Boolean)
Now go back to your left Column in Game page and on the right side, you can see the icon to Generate Children from Variable, this will create dynamic children for your List widgets based on a List data. ��We are getting this List from App State that is already created for you.
Do the same for Right column.
Now add the WordTile component as a child to the Column and all the children in that Column will now be rendering the WordTile widget.
onTileTap ActionBlock
In the Game page, tap on one of the WordTile widgets in the tree.
On the right, tap on the Actions icon, select Page Level, then tap onTileTap.
This action block is called every time a tile is tapped on.
There’s a lot happening here!
When we tap on a tile, we want to check:�
Exit the action block by tapping Close.
Head back to the list of action blocks and tap checkTileMatch.
We want to compare the left word with the right word, see if they match, then do more actions from there.
To do step 1, tap Add Action, scroll to Custom Action, and tap isWordMatch.
Next, tap Edit Custom Action to have a look at the code.
Add the following code to the function.
wordBank is a map of native to translated words.
leftColumnWords and rightColumnWords are the current values for each column.
Click Save.
Back to the checkTileMatch action…
Add the leftTileIndex and rightTileIndex parameters to the isWordMatch action.
Call the Action Output (the result of the action) isMatch.
Tap the + icon, then tap Add Conditional.
Set the condition to be Action Outputs -> isMatch.
If isMatch is true, we will call onCorrectMatch.
If false, we will call onIncorrectMatch.
Make sure to pass the parameters to each action block (leftTileIndex and rightTileIndex).
Now we need to complete the logic for the onIncorrectMatch action block.
Find it in the list of page-level action blocks.
When a match is incorrect, we want to:�
For the first block, select Update Page State.
Update currentHighestCombo and set the Update Type to be Code Expression.
The code expression will take two arguments: currentCombo and currentHighestCombo. Name them current and highest.
The code expression will use the Math package:
math.max(highest, current).
Tap Check Errors, then Confirm.
For the next block, select Update Page State.
Update leftColumnSelection and rightColumnSelection to be -1.
Also set currentCombo to zero.
Third, add an Update App State block.
Set leftColumnIncorrectIndexes by adding leftTileIndex.
Set rightColumnIncorrectIndexes by adding rightTileIndex.
Next, add a Wait action block and set the value to 800ms.
We set this because we want tiles to remain ‘incorrect’ for 800ms before they are tappable again.
Lastly, add a final Update App State.
Set the left and right column IncorrectIndexes and remove the leftTileIndex and rightTileIndex.
At the end, your complete onIncorrectMatch action block will look like this:
Now, we need to create the logic of the onCorrectMatch action block.
Find it in the page-level list of action blocks.
In this block, we want to:
First up, create an Update Page State block.
Increment matchesCount and currentCombo by 1.
In the same block, update leftColumnSelection and rightColumnSelection to -1.
For the second block, update the left and right CorrectIndexes variables and add leftTileIndex and rightTileIndex to each list.
Then, add a Wait block and set the value to 1200ms.
Finally, we will need to create a new custom action.
Select Custom Action -> Create New Action.
Select Edit Custom Action.
In the code, we:�
Click Save.
Now, we’ve completed most of the tapping logic.
Let’s make it more of a game with a countdown timer.
In the UI Builder tab, search for Timer, and drag it onto the Game page.
In the widget properties on the right, set the Countdown Time to be 45000 ms.
Next we need to start the timer as soon as this page loads.
When the timer finishes, we want to end the game.
Tap the Game page, then in the Actions tab, click Open.
We have 2 actions here already to initialize the game.
Add one more, and set it to Timer -> Start Timer.
Now, something needs to happen when the timer finishes.
First, tap on the Timer widget in the UI.
Then, open the Actions and tap Open in the Action Flow Editor.
For the first block, select Update Page State and set the currentHighestCombo to Code Expression.
As before, our expression will take currentCombo and currentHighestCombo as the parameters.
The expression will be math.max(current, highest).
Tap Confirm when finished.
For the final block, select Navigate to -> GameEnd.
Pass the two required parameters: matchCombo and highestCombo.
The next challenge is up to you:
Adding Firebase
SECTION 2
Let’s create a Firebase project from within FlutterFlow!
Tap Settings, then Firebase, then Create Project.
Give the project a name, set the region to eur3, then tap Sign in with Google.
That’ll take a few minutes. In the meantime, we’ll enable Authentication for the app.
Back in Settings, tap Authentication, and ensure the Enable Authentication toggle is turned on.
Set the authentication type to Firebase.
Set the entry page to Home, and the logged in page to Game.
We need to enable authentication for our Firebase project. Head back to the Firebase settings, and click Enable Auth on Firebase, which will take you to the Firebase console.
In the console, tap Get Started, then enable Anonymous authentication.
This will permit us to create user account from within FlutterFlow.
Tap Save.
Now, let’s create a Schema for the data we want to store in Firebase.
Tap the Firestore menu option, and tap the + icon to add a new Collection.
Call the new Collection User, and tap Create.
Tap Yes to the ‘would you like us to prepopulate it?’ prompt.
With prepopulation, FlutterFlow creates fields in the schema, such as email, display name, and phone number.
We’ll use some of these, but need to add one more.
Create a field called twitter_or_email, with type String. Make sure to tap the green tick to confirm your new field.
Next, we need a schema for each game.
Create another collection and call it game.
Add the following fields:
Firebase setup is complete!
Now, we want to create a new user each time a game is played. We’ll give players the opportunity to enter a name, which we will later show in a high score board.
On the Homepage, tap the button and on the right, tap Open on the onTap actions.
Delete the Navigate to Game action block, and replace it with Bottom Sheet. Select the DisplayName as the component.
In the page/widget list, select the DisplayName component.
In the widget tree, tap Form.
On the right hand side, scroll down to Validation properties of the form.
For each text field, select the checkbox and set a minimum required number of characters.
Next, add a CustomButton to the component. Change the label to Start Game, then tap Open on the onTap parameter. (You may have to add the button in case it is missing as the Custom Button is not part of your starter project)
We’ll create three action blocks when the button is tapped.
First, we’ll validate the form (which will check both fields have content).
Second, create a Log in block. Select Create User Document. Set the provider to Anonymous.
Lastly, create an Update Document block.
We’ll select the Authenticated User as the user record to update.
We’ll be setting the displayName field of the user scheme to the value of the displayName text field.
Do the same for the twitterOrEmail parameter.
We don’t need to specifically define a navigation action to the Game page: because we set up authentication to navigate to Game when logged in, this has been handled for us by FlutterFlow!
Next, we’ll create a Game document when the game is finished.
Navigate to the Game page, and tap on the Timer widget.
On the timer, navigate to the Actions on the right and tap Open.
Create a new Create Document block and move it to the middle of the chain.
We’ll create a game document.
Populate each field from either:
Lastly, create a Log Out action block, followed by a Navigate to -> GameEnd block.
Our final task is to populate the high score leaderboard on the home page.
Navigate to the Home page, and search for the DataTable widget. Drag it onto the canvas.
In the table properties, set the number of columns to 3.
Change any other colours and settings to your liking.
Change the table headers to Name, Score, and Highest Combo.
We’ll be populating the table with Game collections.
In the BackendQuery property tab, tap Add Backend Query.
Query the game collection, with a List of Documents type. Order by match_count, descending. Lastly, tap Confirm.
Select the first row of the Name column.
Set the text value as a variable, and use the document property display_name.
Populate the other three fields in the same way, using the match_count, highest_combo fields.
Our workshop is complete - the only thing left to do is try it out!
We can run the code locally by tapping the Lightning Bolt in the top right.
If you have any compile-time bugs, they will be highlighted in red in the bug icon. These will need to be fixed before running!
Thank you for joining!
For more FlutterFlow resources, check out: