I created my very first app with my understanding of Classes, Methods, & Conditionals in Dart called Destini. I did not create this by scratch however, I completed a challenge from the course I’ve been taking with Angela Yu on Udemy. The objective is to create a choose your own adventure story. Depending on the choices the user makes as they read along with the story, the story data that appears on the screen changes along with it.
Recalling information you learn from tutorials and utilizing them in real life is difficult to say the least, but not impossible.
I took this step by step and eventually got through it, checking in on the solutions as I went along to see if I was getting it right. I’m pretty sure that is practically how every app gets done, Googling things to get the solutions from stackoverflow.com or something, so I’m not being hard on myself for not recalling everything I’ve learned.
First of all I’ll run through the steps so that I can better understand what I created, because even though I followed these steps I need to better clarify what it is I’m actually doing. If you’re a new reader I’ll tell you now that I’m learning how to program so that I can build out apps of my own and I’m writing out what I’m learning in blog posts so that I better understand what it is that I’m learning.
Step 1: Download the project file using version control. That’s where you copy and paste the GitHub repository into Android Studio and it copies everything onto your computer so that you can access all the files when you ‘Get Dependencies’ when you load the project from your system.
The project file had set up the Scaffold, but I had to put in the background image. Which after looking it up was not hard.
class _StoryPageState extends State<StoryPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(‘images/background.png’),fit:BoxFit.cover
You use the property decoration: and declare BoxDecoration( image: DecorationImage( and finally image: AssetImage(‘images/filename.png, fit:BoxFit.cover.
After setting that up I needed to create a story.dart file with a class that held some objects. That class is called Story:
class Story { // creation of class
String storyTitle; // creating object variable holding a String called storyTitle
String choice1; // creating object variable string for the users first choice called choice1
String choice2; // creating object variable for the users second choice called choice2
Story({this.storyTitle, this.choice1, this.choice2}){ // creating a method that holds the objects above using this.storyTitle refers to the above variables
storyTitle = storyTitle; // the method declares storyTitle to equal the variable passed in the input of the method
choice1 = choice1; // the method declares choice1 to equal the variable passed in the input of the method
choice2 = choice2; // the method declares choice2 to equal the variable passed in the input of the method
}
}
This is sort of confusing at first glance so I tried to break it down with a // comment next to each object and method to explain it as best as I could. I am going to import this story.dart file into the story_brain.dart file I’m going to create next. You will see how it communicates with this other file next. I practically created this to be like the working mechanism that will pass data into the main.dart file appropriately, you’ll see.
Now let’s take a look at the final story_brain.dart file, since I write these blogs after I’m done the challenge it’s easier to just explain to you what I can. Read the // comments next to the emboldened text to follow along.
import ‘story.dart’; // importing story.dart or the story class to work wit the data.
class StoryBrain { // creating the BRAIN of the app, where all the data is stored.
int _storyNumber = 0; // this variable will load the first story in storyData (below) in the List on the screen, and then be updated as the user chooses.
List<Story> _storyData = [ // creating a List with a <widget> referencing the <Story> class and calling it _storydata, this holds all my data for the app that will load on the screen
Story(
storyTitle: // using storyTitle from the Story class we created in story.dart
‘Your car has blown a tire on a winding road in the middle of nowhere with no cell phone reception. You decide to hitchhike. A rusty pickup truck rumbles to a stop next to you. A man with a wide brimmed hat with soulless eyes opens the passenger door for you and asks: “Need a ride, boy?”.’,
choice1: ‘I\’ll hop in. Thanks for the help!’, // using choice1 from the Story class we created in story.dart
choice2: ‘Better ask him if he\’s a murderer first.’), // using choice2 from the Story class we created in story.dart
Story(
storyTitle: ‘He nods slowly, unphased by the question.’,
choice1: ‘At least he\’s honest. I\’ll climb in.’,
choice2: ‘Wait, I know how to change a tire.’),
Story(
storyTitle:
‘As you begin to drive, the stranger starts talking about his relationship with his mother. He gets angrier and angrier by the minute. He asks you to open the glovebox. Inside you find a bloody knife, two severed fingers, and a cassette tape of Elton John. He reaches for the glove box.’,
choice1: ‘I love Elton John! Hand him the cassette tape.’,
choice2: ‘It\’s him or me! You take the knife and stab him.’),
Story(
storyTitle:
‘What? Such a cop out! Did you know traffic accidents are the second leading cause of accidental death for most adult age groups?’,
choice1: ‘Restart’,
choice2: ”),
Story(
storyTitle:
‘As you smash through the guardrail and careen towards the jagged rocks below you reflect on the dubious wisdom of stabbing someone while they are driving a car you are in.’,
choice1: ‘Restart’,
choice2: ”),
Story(
storyTitle:
‘You bond with the murderer while crooning verses of “Can you feel the love tonight”. He drops you off at the next town. Before you go he asks you if you know any good places to dump bodies. You reply: “Try the pier”.’,
choice1: ‘Restart’,
choice2: ”)
];
String getStory(){ // this function is called in main.dart to load the next storyTitle in the List above.
return _storyData[_storyNumber].storyTitle;
}
String getChoice1() { // this function is called in main.dart to load the next choice1 in the List above.
return _storyData[_storyNumber].choice1;
}
String getChoice2(){ // this function is called in main.dart to load the next choice2 in the List above.
return _storyData[_storyNumber].choice2;
}
void nextStory({int choiceNumber}){ // this function determines what story loads in the _storydata List. Passing {int choiceNumber} into the function allows us to pass an integer into the function when calling it. Like so: storyBrain.nextStory(choiceNumber: 2);
This tells the function in main.dart what choice has been selected, and what to do if we pass a 1 or a 2 into that choiceNumber variable
if (choiceNumber == 1 && _storyNumber == 0) {
_storyNumber = 2; // if user chooses choice 1 and it’s _storyNumber is 0 then change _storyNumber to 2
} else if (choiceNumber == 2 && _storyNumber == 0) {
_storyNumber = 1; // else if user chose choice2 and the _storyNumber is equal to 0 then change _storyNumber to 1
} else if (choiceNumber == 1 && _storyNumber == 1) {
_storyNumber = 2; // depending on what user choice above and ran either of those conditional statements do the following and then the following else if statements that follow the story line..
} else if (choiceNumber == 2 && _storyNumber == 1) {
_storyNumber = 3;
} else if (choiceNumber == 1 && _storyNumber == 2) {
_storyNumber = 5;
} else if (choiceNumber == 2 && _storyNumber == 2) {
_storyNumber = 4;
} else if (_storyNumber == 3 || _storyNumber == 4 || _storyNumber == 5) {
restart(); // then when user reaches the end run a function called restart() we create below
}
}
bool buttonShouldBeVisible() { // hiding the bottom button on the last _storyNumber in the List
//You could also just check if (_storyNumber < 3)
if (_storyNumber == 0 || _storyNumber == 1 || _storyNumber == 2) {
return true; // if _story number is equal to 0 or 1 or 2 return true, or keep the button visible
} else {
return false; // if it’s not less then 3 then keep the button
}
}
void restart(){ // set the storyNumber back to the beginning by loading the 0 index of the List
_storyNumber = 0;
}
}
This is what my completed story_brain.dart file looks like and the explanations of how each of these corresponds to the main.dart file that displays the widget trees of the app.
Let’s take a look at main.dart to see what’s going on in there. I can not stress enough how important this is for me to write about, I’m having a moment of clarity right now.
Here is main.dart:
Again, follow the // comments to understand how I’m breaking this apart for you to understand
import ‘package:destini_challenge_starting/story_brain.dart’;
import ‘package:flutter/material.dart’;
import ‘story_brain.dart’;
// importing the story class from story_brain and story.dart
void main() => runApp(Destini());
// main return your runApp function and input my app Destini()
class Destini extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(), // this tells the app what color it should be
home: StoryPage(), // this is going to hold the part of our app that a will change appearance in the StatefulWidget below
);
}
}
StoryBrain storyBrain = StoryBrain();
// this can be confusing, but we’re calling our class with the first StoryBrain and we are creating an object in our main.dart called storyBrain and its going to hold or equal the StoryBrain(); class data. This tells the main.dart to interact with all objects in that class in the file story_brain.dart
class StoryPage extends StatefulWidget {
_StoryPageState createState() => _StoryPageState();
}
// this is where the app will change it’s appearance using this StatefulWidget allows changes to happen on screen
class _StoryPageState extends State<StoryPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(‘images/background.png’),fit:BoxFit.cover
)
), // container holding a background image in our assets folder
padding: EdgeInsets.symmetric(vertical: 50.0, horizontal: 15.0),
constraints: BoxConstraints.expand(),
child: SafeArea( // keep things within the SafeArea
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
flex: 12,
child: Center(
child: Text(
storyBrain.getStory(), // this loads the story into the center of the screen, notice it’s within a widget tree starting with the SafeArea, a Column, and the children in that column within an Expanded, that holds centered text.
style: TextStyle( // this is changing the style of the text loaded from getStory();
fontSize: 25.0,
),
),
),
),
Expanded( // beginning of our button
flex: 2, // I need to understand how flex works a bit better, sorry.
child: FlatButton( // our first button that tells us the user chose choice1
onPressed: () { // button action
setState(() {// this allows the button text to change, gotta use setState if something is changing on the button
storyBrain.nextStory(choiceNumber: 1); // calling storyBrain, run the nextStory method we made, and so you know which choice it was they made, we input (choiceNumber: 1);
});
},
color: Colors.red, // color of the button
child: Text(
storyBrain.getChoice1(), // calls the method in storyBrain to getChoice1 String data.
style: TextStyle(
fontSize: 20.0,
),
),
),
),
SizedBox(
height: 20.0, // this is the spacing between the two buttons
),
Expanded( // button 2
flex: 2
child: Visibility( we use this to hide the button on the last storyTitle
visible: storyBrain.buttonShouldBeVisible(), // we created a function in storyBrain to check to see if the button should be visible or not
child: FlatButton(
onPressed: () {
setState(() {
storyBrain.nextStory(choiceNumber: 2); // I’m calling it to load the nextStory based on the choice made by the user with the input (choiceNumber: 2)
});
},
color: Colors.blue,
child: Text(
storyBrain.getChoice2(), // this get’s the String data for choice2
style: TextStyle(
fontSize: 20.0,
), …
That’s what main.dart is like. I’m confident in my understanding of it after I did this diagnosis of the Dart files. It’s important for me to do these blog posts for myself rather than a motivation to do it to provide for you. Honestly I don’t think anyone is reading this far unless you’re a beginner programmer that also reads lengthy blogs on the subject, that’s a micro niche, I’m sure your out there. Hi! Thanks for reading.
I hope you got some good insight out of this, I did. If you have any questions or comments or you think I might have got something wrong in my explanations leave a comment below or reach out to me on Instagram @kyleknob.