[MUSIC PLAYING] FILIP HRACEK: Hey there. In this codelab, you're
going to build this app. I call it Namer. It is an app that just
provides a bunch of random name combinations or word
combinations as names, like, for example, thoughteye,
or holdthreat, toughcheck, or cheaptail. And this app that
you'll be making is going to be
responsive like this. It's actually, of course, it's
also running on mobile screens. And it runs on the web as well. It runs on Mac,
Windows, and Linux. And you can actually
like or favorite things like stackring or hardridge. That's pretty cool. And then you have them
here in the favorites. So that's what we'll
be building today. We are going to
focus or we're going to follow this thing
somewhere here. OK. So the codelab you
will find if you go to
codelabs.developers.google.com. It's probably in the description
in this video as well. And you go to Flutter new. Actually, Flutter first. Your first Flutter app. And you'll find this. And you just click and start. And you'll get to the codelab. And we'll be following that. My name is Filip Hracek. I actually built the
app and the codelab so hopefully I won't have
too many problems duplicating that feat with you. Let's try and go hat. All right. That's it for the introduction. That was quick. I just wanted to tell you what
the app is going to look like. Blah, blah, blah. You can read all this but
let's go to the next page. And here is probably
the hardest part of this whole
codelab is installing everything and making sure
that it actually works. So this is important. It's not very fun
but it's important. We will use Visual Studio
Code throughout this codelab because that's just easier. And as far as I understand,
most people these days use Visual Studio Code anyway. So if you don't,
just download it. It's free. And then go through
this step carefully. You will need to choose
what to build for. Flutter lets you build apps
for any screen basically. Right. So you can build
apps for phones. You can build apps for browsers,
for Windows machines, whatever. But when you're developing,
you won't be developing for all of them at once. You will be developing
for something and then, of course, later
you can build your app for anything else. So you need to choose
your target device. And my strong suggestion
is that, for example, if you're developing
on a Windows laptop, your target device
is Windows, not a browser, not the web,
therefore not an Android device, but Windows itself
because, at that time, and I made this little
illustration here, you can actually have-- your device can also be
your development device and your target device,
both at the same time, which makes some things faster,
easier, and so on and so forth. So if you can, do that. Of course, I'm not your boss. You can do whatever you want. You can say, oh, you
know what, I want to do an Android or iOS app. But this will inform what
you do in this step, which is installing Flutter. So you click on this. And you get here. And you choose first
your development device. Right. So if you're on Windows,
you click Windows. I'm on a Mac, so
I'll click MacOS. And then it tells
you how you can install the Flutter SDK itself. Actually, let me go
back and choose Windows. I think most people choose
Windows most of the time. So you get the SDK. You install it according all
to all these things, including updating your path,
super important, running Flutter
Doctor, and so on. Then if you chose Android
as your development target, you do this. But if you didn't,
then you can skip this, which is a lot
of work by the way that you can skip for now. You can always add it later. And then if you
chose recommended, the Windows setup, then you
need to download either Visual Studio 2022, which is a
different thing than Visual Studio Code. Right. Visual Studio, the big thing,
or Visual Studio Build Tools, which is a smaller download. By the way, what I'm showing
here is as of today, of course, you should go to the page
that exists at the point where you're watching this video, and
choose the appropriate things, and read the appropriate things. I'm just trying to
tell you, if you're like, whatever--
if you're on Linux, you don't need to
set up everything. Web setup is
automatically there. And then you go to
set up the editor. You make sure that you have
Visual Studio Code selected here. And then unless you've already
done this, you install VS Code. And then you install the
Flutter and Dart plugins. For VS Code, that's
actually you just install the Flutter plugin. And it will install the
Dart plugin for you. So it's fine. They will tell you how to do it. US use Command Palette. We'll get to that later. But it's also pretty easy. And yeah. And then you install it. And then you can validate
that everything is fine. And at this point, when
you get to test drive, you can go through
that to make sure that everything is absolutely
top notch on your computer. But you can also
just go back to here. I've included a
bunch of frequently asked questions
from Stack Overflow. So if you, at any
point, get into trouble, just read through it
and see if there is-- one of those isn't
relevant to you. All right. So I will now-- you are
now able to pause me. And after you're done
with installation, we'll finally get to creating
your first Flutter app. You ready? All right. All right, let's go
to the next step. So you're set up beautifully. Everything works, right? There was no problem
at all, I hope. And everything
downloaded really fast. And you go to the next
step, create a project. And now, finally, we get
to do some actual work. Cool. I will open my own Visual
Studio Code over here. And it's the same
Visual Studio Code. It doesn't matter if
you have something open. That's fine. And I'm going to open the
Command Palette, which is F1 normally, or
Command-Shift-P, or something. And then I'm just going to
do Flutter, New Project. So you just start typing. And this is just-- if you've never used
Visual Studio Code, then this is how it works. You do basically everything
through the Command Palette, F1. And you say, yeah, I
want a new Flutter thing. I hit Enter. And then I-- yes, I want
to build an application. All right. And then I choose where
this application will go. So this is the-- this could be like
your Dev folder. Except I have a
bunch of stuff there. So what? I'll put it here. This is not where
your files will be. Your files will be created
in a subfolder of this one. So select the folder to
create the project in. And then name it namer_app. You can name it
differently, but then you will have to change, well,
one line of code later. But if you want to be
clear, you can just, yeah, name it namer_app and hit Enter. Now, things will
start happening. And it is very possible that, at
this point, you will get this-- you'll get this model window. And it just says, hey, do you
trust the authors of the files in this folder, meaning
the Flutter team? So I strongly suggest
that you do trust the Flutter team [LAUGHS]
because otherwise, this is going to be a
pretty short codelab. No, but you can still
build, I think-- I mean, you definitely
can build a Flutter app with using no or saying no. But then most or all of the
cool things about Flutter will not work. OK? So you should
probably say yes here. Anyway, so we have
a new project. Probably the main.dart
file will be selected. And you can read through this. In the interest of getting
you to the fun parts as soon as possible, we will now
just replace three files with different contents. And then we'll go to something-- do something cool, all right? So copy-paste the initial app. This is the very barebones
app that we'll be going from. So first, we find pubspec.yaml. In the left side, hopefully,
you have Explorer open. And you find in the
root of your project the pubspec.yaml--
not pubspec.log. I almost hit the wrong file. You go to pubspec.yaml. You select everything. And you delete everything. Also, why is this? I should have the
screencast mode open. So one more. Select everything, Backspace,
or delete everything. And then replace
it with whatever you find here in this codelab. Paste it there. And save. Always, always remember to save. Same thing with analysis op. So OK, what did we just
do here with pubspec.yaml? Pubspec.yaml is basically
this kind of like-- what is this app, or
package, or library about? So it has a name. It has a version. It has an environment
range and stuff like this. And dependencies--
what kind of packages does this package, or
library, or app depend on? So this is all here. And we don't need to go
too much into detail. Next up is analysis option. So once again, go to
left Explorer, find the analysisoptions.yaml file,
select everything, delete, and then paste whatever
you found in the codelab. This one is basically telling-- I didn't save here, I think. Or did I? This one-- sorry. This one tells the Dart analyzer
to basically take it easy. [LAUGHS] So the Dart analyzer
is a very powerful tool that lets you know about all
the different little things that could make your life
harder down the line of-- it is a static analysis engine. And there are a
bunch of links that are very good to have on if you
are a seasoned Dart or Flutter developer. But this is your first-- I assume this is your first
hour with Flutter and Dart. And therefore, you probably
want the linter to take it easy. Some of these things
really aren't that big of a deal for a small app. So we just say, don't
worry about this one. Don't worry about this one,
and so on, and so forth. So that's our analysis options. That will make our life
a little bit easier. Sorry about that. And switching. And then the last,
but not least-- libmain.dart. So we again copy here. And then you find
lib in a direct-- it's a top-level directory. And then main.dart is in there. And then again, I will select
everything, delete, and paste, save. And we are ready. All right? So we are not going to
go through this just yet. We are going to
first run the app. I'll modify it a little bit. And then we're going to go
through what all of this means. You will understand most of
this by the end of the codelab. All right. Next, we're at Add a Button. So first of all, we
need to launch the app to see it in action. So first, I will close this one
because that's already done. You don't have that yet. And we are going to go
to Visual Studio Code. And first of all, make sure
that, on the bottom right, you have selected
the right target. So we set at the very
beginning of the codelab that you need to
choose a target. In my case, it can be
a MacOS or a Chrome. I could start an iOS simulator
or an Android emulator. But I need to make sure that
the correct device is selected. In my case, I am on a Mac. And I want to build for
a Mac, at least for now. So I'm going to
have MacOS selected. So it's over here down. And now, I'm ready
to run the app. And that's over here
to the right side. It says, Start Debugging. I think the keystroke is F5. But I'm not sure. And to be honest, you'll
be only clicking it once in a very long time
because of hot reload. So we click it. And now, it's starting. So this took a few
seconds at least, maybe even a few minutes. But now, we have our
app running here. I'm just going to put it
here somewhere under my-- hopefully, under my face. Actually, may be a
little like this. And yeah, it doesn't do much. It says a random idea and
then something-- brief crowd, in my case. It will be something
else for you. And that's where we are now. All right. So name your app
with all of that. And it does work. Cool. So we are going to try the
thing that Flutter became famous for, I think, a few years back. And that is hot reload,
meaning that we can change the app as it's running-- a random, awesome idea. And I just save. And you can see that
it immediately changed. I didn't need to
recompile anything. And my random word is-- stayed the same. The state of the app-- that's
why it's called stateful hot reload-- the state of the
app stays the same, which is very powerful when
you're building something and you're like,
OK, I've clicked through all of this in my app. And now, I just want
to change something. I don't want to go back to
the beginning of the app and click through again. With stateful hot reload,
you can just do that. OK. So that's the random,
awesome idea change. Let's go back to this. There are, again,
frequently asked questions in case
something doesn't work. You can check these. And now, let's add a button. All right. So this, again, for now,
we'll just copy-paste because it's too-- I think it's just too much
work for what it teaches you. It doesn't teach you much yet. But don't worry,
we'll get there. So I just copy this
elevated button thing. It's called a widget. And I'll put it under
the second text. All right? And I save. And now, I have a
little button here. And watch this. When I click the button, bam. Here in the Debug Console,
it says, button pressed. And if I click several
times, it will not repeat it. But it will just say, hey,
this was repeated 11 times. All right. So you can do this. And it's pretty cool. And you can see how
there's little animation and stuff like this. But it's not really an app yet. It just says--
when you press it, it just does print into console. And that's it-- not very fun. So let's go. And before we do
something much cooler, I think, let's understand
Flutter, OK, in five minutes. Woo. All right, I scrolled
to the top of the file, main.dart, which
is-- by the way, is the only file that we
will be building today. And it will still be enough. And I'm going to go through
the code in main.dart and explain what things are. This is the exact same thing
that you will find here, except now, you'll be
hearing me talking. So you can-- if
you wish, you can skip this part of this
codelab and instead just read through the text that I wrote. Cool. So every app in Flutter
starts with a main function. And in this case, it just
runs, says to Flutter, hey, I have my app. Just run it. OK? My app is a widget. You'll see that we say
that everything in Flutter is a widget. And it's not really the case. But in terms of things that
you see on the screen, yes, they are all widgets. So my app is a
widget that we were running as the top-level
widget of the app. And every widget has a
build method, which says-- which tells Flutter, what does
this widget contain, basically. And in this case, we say, OK. So this widget contains, first
of all, a Change Notifier provider. It provides some
change notifier. We'll get to that later. We create a state
for the whole app. Again, we'll get to that later. And then this is a
Material app, which just means that there's
some themes in it, and so on, and so forth. This is its name. We use Material 3, which means
that our buttons, for example, will look a certain way. We have this color scheme,
which means we want-- like for example,
the button is trying to be compatible in
color to this color. It's a weird way to
say this, but yes. If-- for example,
if you have a logo and it has a certain color,
you can very easily say, hey, I want a color scheme
that goes well with my color. But, of course, you can also
build your own color schemes from scratch. And then the Home page of
our app is my Home page. Before we go to my Home page,
there is the MyAppState. Now, you can use different ways
to manage the state of your app in Flutter. This one is probably
the easiest to explain and to get going with. So we have this change notifier,
which is a normal class. And all the change notifier
part does is that it's-- you can say that,
oh, I have changed. Anyone who's listening
to me or watching me should take note and
maybe update themselves. So that's all that this
change notifier part does. And then our
MyAppState currently has nothing else in it than
the current word pair, which is, in my case, briefcrowd. [LAUGHS] And it
just initializes it randomly at start of the app. So for you, it
will be different. We will change this, of course,
so that people can actually ask for new random word pairs. Next, we have-- and
that's the last one. The MyHomePage is
a stateless widget. Again, a widget
with a build method. And this widget, it
asks, or basically, it watches the MyAppState
that we just covered. And by the way, this MyAppState
is created here in my app and provided through something
called a change notifier provider. All right. So that's how any
widget in our app, whatever, however,
wherever it is, is going to be able
to watch MyAppState because it will always be
below the Material app-- I mean, my app. So we have this appState. And what this does is
basically, it says, hey, I want to
rebuild every time-- every day-- I want to rebuild
every time this appState gets updated or changed. So whenever we will call
a method, called Notify Our Listeners, from this class,
this build method will rebuild. And then we actually are
using appState to get the current, which is this
one, this current word pair. And then we render
it as lowercase. So this is why it's
briefcrowd, all lowercase. We could change this. For now, like for example,
it could be Pascal case. And if I save, it's BriefCrowd
with B and C capitalized. But I'll just do lowercase. There's a bunch of other things,
like camel case and so on. Snake case also exists. And then there is
ElevatedButton. Oh, by the way--
oh, word pair comes from this package or library
called English Words. And so we don't need
to deal with it. And it's just something that's-- imagine that you just
import a library that does something for you. That's exactly what's happening. I don't want to teach you how
to work with words and strings in Flutter. I want to teach you how
to build apps in Flutter. And then later,
you can, of course, learn how to do
whatever you want. And so MyHomePage is-- basically
all this is MyHomePage. It has a scaffold, which is
this widget that provides a bunch of good things. Like for example, if you want
to show a little modal window, and stuff like this, or little
toasts, then Scaffold is good. Right now, we're not using it. But it's a good thing to have. Then there is a column. So basically, column widgets
one by another below themselves. And then there is the text,
the random, awesome idea, or whatever you changed it to. Then the next text is just
rendering our little word pair. And then there's
the elevated button. And you can see that in
ElevatedButton, there is another child. So a lot of times, the
widgets will be nested. And that child just says Next or
it's a text widget called Next. You can have anything else here. That's one of the cool things. You can just change the
widget to something else. And Flutter doesn't care. As long as it's a widget,
it's a valid thing to have. Cool. So for example you don't need
to have text in your buttons. There could be, well,
an image, obviously. There could be a whole
little app in your button. All right, cool. So that's Flutter. And I hope that I didn't
skip through anything. Yeah. There's this little
graphic that says, OK, so we attach
MyAppState to the MyApp and so therefore, anyone
here can watch and use it, which is nice. And there's-- definitely
go through this if you want to read it in more detail. And now, finally, we
can have some behavior. So we go to MyAppState. And we copy-paste this. Or I will actually probably
just write it there. No, no, I'll copy-paste it. So what this does,
if you save it and if you add it to
MyAppState, that's just a little method
that says, hey, assign current to a
new random word pair. So we will change the random
word pair to something new. And then we call
the notifylisteners. The notifylisteners
is, as I said, the only thing that change
modifier is actually providing. So we say, hey, people who
watch me or listen to me should probably know
that this changed. But we also need to actually
call getNext from somewhere. And so I'll just go here
and I'll delete print. And I'll do appState, which
again, we're watching here. So we have it ready-- appState.getNext. Now, save and bam. It works-- freshfaced,
and chiefwheat, shypan, and holdking-- all of that very good names,
as, hopefully, you agree. So we have our app, right? We have-- the basic
functionality is there. But you agree, probably, that
it's not looking that great. So the next section is about
making the app prettier. And in this case, it's
not just prettier, but also, this part of the
app is the most important one. It's the generated word. And it's basically lost. So we want to make this
something much more splashy, something bigger, and
nicer, and more eye-catching for this part of the app. So we will extract this part
of the app into its own widget. And then we'll work on it some. So extracting things
into their own widget is really important in
Flutter because a lot of time, you will have these
parts of your UI that are semantically important. And they should be
treated as one unit. And instead of just continuing
working on them inside another build method, it's a good
thing to first extract them into another widget,
a separate widget. And you'll be doing this a
lot in your Flutter journey. So we want to extract this
to a different widget. And we'll be using Visual
Studio Code's helper-- Flutter helper
methods to do that. But first, you will
notice that in order to render just this
one word pair thing, we're accessing appState itself,
which is not a good thing to do because when we
extract this widget, it will take with it all of
its dependencies or everything that it accesses-- in
this case, appState. So we'll have this
card or big card widget that is extracted,
but also just takes all of its appState with it. I'm not sure if I'm
making sense here. But basically,
what we need to do first is, as is suggested over
here, we need to first say, OK, our pair is
appState.current. That's our current word pair. And then instead of
saying appState.current, we just say pair here. And so in other
words, this line now just depends on this
one single variable and not on the appState
itself, not on all of it. This-- for our app, it
doesn't really matter. But again, for later,
it's good practice to make sure that your widgets
only really depend or only really take as parameters
what they really need. So that's what we did. So we are past this step. And then we're
going to refactor. We are going to use the helper. So I'm going to do
Command and Dot. Or I think on Windows,
it's Control-Dot. But you can also Right-Click. It's all in the codelab. And you say, extract method. So it gives you all
of these helpers. And I think the first one for
everyone is Extract Method. Just click on that. And then over here on the top-- no, not method. Why did I do method? So Escape and
extract the widget. Extract Widget, please. And I click on Widget. And now, instead of NewWidget,
I will be calling it BigCard. I suggest that you also call
it BigCard with capital B and C so that later you
don't get confused. And Enter. And now, you can
see two things-- first BigCard-- this
now says BigCard. So the line has been changed
to use our new widget. And then over here, at
the bottom of your file, there's a new stateless
widget called BigCard. And it takes in pair,
which then is part of it. And then it just says,
text pair as lowercase. And that's it. And if you save here, nothing
changes, as you would expect. It's just-- we just refactored
our code a little bit. But that's it. OK. So we have this. By the way, if you
are lost, you can also go to the codelab itself. And all of these things
are explained through GIFs. So hopefully, you can do that. And this just says
what happened. And now, we start adding things. So same as before, we are
going to click on Text here. And again, do the
Command-Dot or Control-Dot and say, I want to
wrap this with padding, another action that's over here. And just click on that. And if you just save,
you'll see, in the UI, that there's more padding
around the text now. Let's see what is going on. You can, of course,
change things a bit. So instead of padding
on all sides of eight, it could be something like 20. And there are other ways to
say what the padding should be. Like for example, you can
have padding only on one side, and so on, and so forth. You can play with this. But for now, this
is good enough. And now, we go to
the next one, which is wrapping it with a widget
that's not in the menu. So I'm selecting Padding. So my cursor is on Padding now,
all right, that we just added. And I do again, Command-Dot. And I want to wrap this
widget with a card. So the padding should
be wrapped with a card. It's not here. Card is not here. There's a bunch of widgets
that are obviously not here. And so I can do Wrap with
widget, dot, dot, dot. I click that. And now, I can-- as soon as I click it,
now, I can actually say what the widget should be. And it's just Card. Card is a widget that comes with
Flutter or with the Material Design Flutter part. And if I save, you'll see that
our padding, and therefore, also our text, are
in this little card with rounded corners,
which is pretty cool. Let's see what we can do next. So we have, basically, the same
thing that they have here-- or I had when I
wrote this codelab. And now, we can change the-- it's nice. But we want the color to
be much more bold, I guess. So let's do it. The way you do
things in Flutter, generally, is,
again, through asking your parents or the widgets
on the top what to do. In this case, we're going to
add more space here in build. And we're going to do var
theme equals theme of-- I'll explain-- context. And this is very similar to what
we had here with context.watch. It's basically the same thing. But this one, this
theme, .of, asks-- goes all the way,
again, to the top. And it basically takes-- watches theme data. Except we're not changing
anything about theme data during our app-- the life of our app
so far, at least. So but yeah, we want to make
sure that if our theme changes, we also know about it. So that's what this is. Instead of hard-coding
some colors here, we are going to use the theme
from the top of our app. And here, we give the card
the color from the theme. So the card, if you go to
the beginning of the card, before the child with padding,
you can add a new line and start saying,
the color will be from theme.colorScheme.primary. So this will make it a very-- I don't know, a very bold color. I don't know how else to say it. In my case, it's red. But you can-- or let me
actually show it to you. So if I now change the
colors to something like blue or any other color,
you'll see that I just saved. And everything is now more blue. So even the button now is blue. If I change it to green,
I change it to green. Of course, these
are just the colors that are predefined by Flutter. But you can use your own colors. I'll just show you because-- just so you know by
doing something like RGB, for example. Yeah, 0, 0, 125, opacity is 1. And we have this weird kind of-- is it blue? Well, probably not. Anyway, so yeah, you
can play with this. And as you can see,
everything in your app will kind of fall in line in
terms of the color scheme. But I'm going back to, I think,
deep orange, saving, going back to here. But now, our card
is in a bold color. Cool. OK, so now, our card
has a bold color. But the thing is the text is
not looking good on the red. So I go back to our codelab. Where is it? And I go through this. OK, we talked about this. We talked about changing colors. I don't know if we talked about
how the change of the color was animated, which is
called implicit animation, another cool thing
about Flutter. But it's not important now. Now, let's change
the color of the text to something that looks good on
that particular primary color. And for that, we will,
again, use the theme. We will ask the theme about-- OK, let's just copy-paste this. So under Theme.of, we add a
new one and save, which says, the style for our text-- we're not using it yet--
but the style of our text is also coming from
theme, from textTheme, which is the default.
Of course, we can change it over here if
you want to, but not now. We will use the theme
for display medium. Now, textTheme has
fonts and definitions-- font definitions for all
the different types of text that you can have in
an app, for example, like normal body text
or headline text. And display is one of
the biggest kind of texts that you can have. For example, in
typography, I mean. In typography, if you say
something is a display font or display style, it means that
it's a huge, big chunk of text. So what we are using
is display medium. We're using a null
aware operator here because it could be null. But we know that it's not null. Well, I know. You have to trust me. And then we also say-- OK, so it's going to
be a big piece of text. But we also say that
the color of the text should, again, take from
theme, from the color scheme. And it should take
this color that's basically a color that's clearly
legible when drawn on primary-- the color primary. This is exactly what we want. So it will choose-- the color scheme
would automatically have this color, probably
looking a lot white, in my opinion. But it's-- this is dynamic. So I don't-- I can't tell you if
it's really white, or if it's little off-white,
or maybe a little reddish white so that it's still
in the color scheme. Anyway, so we now
have this style. And then if I go back,
all we do is just say, in this text, this one, it
just says, OK, and by the way, this text should be
in the style of style that we already have here. And save. Bam. Nice. So we have this now. All right. And I can change it-- shyleave,
dogworth, and neatbeach. So let's go to the next step. In the codelab, it explains
a bunch of the things that I already explained here. But again, you can go and
read through it for more info. And you can also do a little
more things, like change the color to something else
from the scheme, for example, secondary instead of primary. And on secondary
instead of on primary. All of that is cool and good. Next up, accessibility. So Flutter apps are accessible
by default, basically. So if-- for example, if you
use a screen reader, which is to say if you, for
example, a blind person and you want to
look at this app, it will read the correct things
that you see here as normal. But sometimes, you-- it's
good to, first of all, try it. So there is a link
over here on how to try your apps on
different devices in terms of accessibility
and screen readers. But sometimes, you kind of
have to help Flutter understand what different things
are in terms of semantics so that, again, screen
readers can help people navigate the app better. In our case, our accessibility
problem is that neatbeach-- you and me, we can read it. And we understand what it is. But a screen reader could
get a little hung up on the plethora of
different characters. And it will probably
read it correctly. But it might not. And so we will help it
by saying, for the UI, we want to keep it as lowercase. But for screen readers, we
want to show something else. And so what we can do is,
again, go back to the text here. I will just add a comma
here, which, by the way, is a nice way to say to
Flutter or the Dart formatter to put things one
by one per line. So if you add a
trailing comma-- if you don't add a trailing comma,
it will just do this. But if you add a trailing comma
in parameter lists and save, it will change this to one
parameter per line, which is often nicer to work with. So that's what I did now. And I'm going to
say, semanticsLabel is going to be pair and not as
lowercase, but as Pascal case. And this is because
I know that if-- when it's in Pascal case, which
is like, for example capital N-eat, capital B-each. It will be read correctly
by basically every screen reader out there. So that you can do. Of course, Flutter
has more things that you can do with semantics. But we're not going
to be covering it in this particular codelab. Let's go back. So yeah. So again, if you
want to try it now with the screen
reader on your device, there is a link in the codelab
to a page that tells you how exactly you can turn them
on and how you can try the app. Now, we're almost
done with making the app a little bit prettier. And that is centering the UI. The whole time, we have
it in the left top corner. And we could have done
it at the beginning. But this-- now, you're ready
for all of this, I think. So first of all,
this column is-- basically, what I
mean by this column is this column doesn't know
how to deal with its children. So it just puts them all
at the top of itself. So the column is the only
child of the scaffold. So therefore, it is the
only child in the whole app. And it's like, oh, OK. So I'll just use-- because I'm a column, I will
use this vertical space. And I have these three children. I'll just put them
at the beginning. So this is very easy to change. You can just say,
main axis alignment. Main axis in a column
is the vertical. And you can say, main
axis alignment and say, center, for example. Or for example, in our
case, definitely center. You could say-- and you
could say, space around. You can play around with this. But really, what
you want is center. And now, another
thing here is we want to center it also horizontally. And I'm not going to use-- I just show it here because it's
not really, again, important. But there is something
called a widget inspector. So whenever you are confused
about anything in Flutter and the layout in
Flutter, in particular, you can open the
widget inspector. Actually, let me open it here. So it's over here. Again, you don't
need to do this now. But you can open
the widget inspector by clicking this little button. And then you can say, I
want to select Widget mode. So whenever I
select anything, it will select the widget instead
of clicking in the app. And now, you can see that
the column is actually just here because it's very modest. The column, yes, it takes
all of the vertical space. But generally speaking,
if you have a column, you don't want it
to take everything in the horizontal space. So it only takes as
much as it needs. And in our case, it
only needs as much as all its children
need, meaning the biggest one, the big card,
currently takes this space. And that's what
the column takes. So all we need to do
is say to column, hey, just go to the center. So how do we do that? Well, we once again use
the helper with wrapping. And we are going
to use the center widget, which is a special kind
of a line widget, by the way. So I'm just going to close this. And we take the column. And we just say, we
want it centered. So again, Command-Dot. And Center is one of the
widgets that's already here. So we don't need to write it. And Enter and save. And now, it's all centered. There you go. We have a
neatly-centered column. That's how you
generally center things. If you don't have
a row or column, which have this
main axis alignment. And by the way, they also
have cross-axis alignments. But if you have just
a widget and you want to center it somewhere,
and not in a column, or a row, or anything like this,
then you use the Center widget. Very useful. Sweet. Now, we have an app
that looks like this. And I have some more
suggestions here. You can remove this part. I think it is redundant
at this point. A random awesome idea is
kind of like, yeah, OK, we already have this here. So that's what I'm
going to do is I'm just going to remove that part. Save. I think it looks cleaner now. And the next
suggestion is to add a little bit of breathing
room between the button and the big cart. And you do this using another
widget called SizedBox height. You could also use it
with padding, by the way. But we're just going
to use SizedBox. So SizedBox is a widget just
that doesn't have anything by itself. It just has a size. And it will have a
height of, let's say, 10. And save. And you see that there's
more breathing room. I kind of like it. You could try
something different-- 20, meh. 10, yeah. 10 is better. By the way, these are
device-independent pixels. So it will look the same
whatever resolution you have. It's device-independent pixels. So yeah, whatever resolution
density or pixel density your device has, it will-- Flutter will make
sure that it's kind of the-- visually the same
size, device-independent pixels. Because otherwise, 10
pixels on a retina display is very different from 10 pixels
on an old, old device with 100 by 600-- 800 by 600 pixels. So that's why we have
device-independent pixels. All right. So our app now
looks pretty cool. At least, it looks better than
at the beginning of step five. Now, let's add a little
more functionality. We want to be able
to favorite things so that we can
get to them later. Because right now, we go
through all these suggestions and then we forget about
them because the next one comes around. So we should add
some functionality. Adding the business logic, we're
just going to copy-paste it. And I'm going to go through it. So go to MyAppState,
which is over here. MyAppState is your state class. And add this code. This code adds two things-- first, another field,
called Favorites, which is a list of word pairs. This is how you say-- in Dart, this is
how you say a list. And this is how you say,
only word pairs here. It's a generic. So you can't have
null, for example. If I add a null here, if I can
write, it will not compile. It will say, hey, there
can't be null in this list. There definitely can be
3, for example, here. So anyway, that's generics. This list will always
have only word pairs. And then we have a new
method, called toggleFavorite. And this one, if the
favorites already contain this current favorite,
then it will remove it. And if it doesn't, then,
well, it will add it. So the current favorite will
either favorite it or not. And then, again, we have to
remember to notify listeners. So anyone who is
interested in MyAppState should be rebuilt in case
that this is important for them or they are showing
something about the toggle. Cool. So that's this part. And this is just how-- yeah, I
talked about the collections, and so on, and so forth. Now, we should add a new button. So we want the button
to be in a row. We've already talked
about columns. But now, we need a
button that is either to the left or right of Next. And you do this in
Flutter by adding a row on top of the button
that you-- or the widget that you want to have
next to the next one. And then you add a
child, another child. So we are going
to, again, go here and Command or Control-Dot,
and Wrap with Row them. And then we will add-- yeah, actually, let
me show you this. If I save now, it will go
all the way to the left. And that is because now,
it's-- it's the same thing that happened with Column before,
but in the horizontal direction. So Row now takes all of its
available horizontal space. And then it has one child. It just tugs it to the left. We could say, hey, Row,
align to the center. But what I actually want
to do is I want to say, don't take everything, man-- Row. Just take what you need. So MainAxisSize should
be min, not max. So by default, it's
max because that's what mostly you want,
really, if you have a column. But in this case, we just
say, no, just take as much as you need and nothing more. And now, we add the Next button. I'm just going to check
how it works here. Yep. Same as before. But now, there is a row here. But we don't see it. And now, adding a Next button. Now, this is the part
where I'm in the codelab, and even here in
the video, I'm going to challenge you to
try and figure out how to add a button next
to the existing one. And you shouldn't
worry about the icon. You shouldn't worry
about anything. But you should worry about
hooking it up with the business logic that we added MyAppState. So try to-- when I say now,
try to pause this video and try to add a new
button that says something like Like and that,
when you click it, will add this, the current
one, to Favorites right now. So how did you do? Did you do it? I'm just going to
copy-paste this from here. It's basically-- because
I'm going with a little more here, including the icon there. So the way I did this-- and again I didn't ask you for
using the icon or anything. But the way I did this
was with adding to the top some logic that's like, what
is the icon going to look like? And that's basically if the
current pair is in Favorites, it's going to be this
icon, filled heart. And if it's not, then
it will be this icon, which is this outlined,
or border, of a heart. OK? So that's one. And then we are going
to add the icon to here. So we have it here now. And the ElevatedButton.icon,
which I haven't told you about, so you couldn't have known, it-- on press, it will
call toggleFavorite, which is what you
hopefully figured that that's what you should do. And then it has the
icon, using the icon data that we did before. And then it has a
text called Like. Yeah. And now, I also added-- because it's way too close,
I also added a sized box. So SizedBox of width-- this time, 10. Yeah. And so now, I can like
and unlike neatbeach. And I can go to sadaunt. Sadaunt is nice. What else? Vastmale, lightsearch. I've seen that before,
actually-- lightsearch. Good. We now have an app where
you can actually add things to favorite, which
is great, except you can't see those
favorites because we don't have a section
where you could see them. So that's our next step
is adding navigation, and therefore, a way to
get to a different page. We'll start with replacing my
Home page that we just worked on with two separate widgets-- MyHomePage and Generator page. Now, Generator page is
basically exactly the same. This should feel familiar. This is exactly the same
as my Home page was. It is this, literally
this, except it doesn't have the scaffold. But otherwise, it
has everything here. And MyHomePage has
new code in it, which we'll cover
as we go along. So what I'm asking you to
do is either just replace my Home page with all
of this, or if you have some cool things that
you added during the codelab so far, and you want to keep
them there, do the same thing, but then copy things that
you did into Generator page so it looks the same. But yeah, Generator
page is basically MyHomePage except one
widget, and that's it. So I didn't do anything cool. I will just copy-paste this-- I mean copy this. And I will go to MyHomePage,
get it, delete it, and paste the new two
widgets, and save. And now, what happens? You have a navigation
rail to the left. It doesn't do anything yet. And then this is the
same thing as before, except it has a
different background. The background comes
from the new MyHomePage because this all is the
same widgets as before. All right, let's have
a look at MyHomePage and what it looks like now. So again, it's just
called GeneratorPage for one part of it. But all the rest
is the new things that you haven't seen so far. It is-- it starts with a
scaffold, nothing new there. And then there's this
big, big row that comes-- that covers the whole app,
basically, from left to right. And the row has two children. The first one is this part. And the second one
is this big part. The first one,
we'll get to later. And the second one, it's-- on the top, you have Expanded,
which is a special widget that you use in rows and
columns, where it says, hey, give me as much
space as possible, please. So it's not like, oh, I just
want to use as much space as I need. No, I want-- it's greedy. I want as much as possible. So that's Expanded. Inside the Expanded,
there is a container. That container has a color. The color is primary container. That's this little off-white or
something reddish a little bit, maybe pinkish, in my case. Of course, if we change
the primary color to something else, then
it will be something else. And then the
container has a child. And that child is the
whole Generator page, which we know and love by now. OK. And the first
child is this part. And that is just a safe area. Now, safe area is
a special widget that you can use anywhere,
as any other widget you can use anywhere. But it's meant for
mostly mobile phones, but any other device where parts
of the screen can be obscured. You can see on this
mobile phone that it has a status bar and a little
camera notch on the top. And we don't want our
navigation or anything else to be covered by the
status bar or the notch. But we still want the app to be
below that so it looks nicer. We don't want to have the app
to end here, and that's it. So that's what we will do is
we grab the things that we need to make sure that are
visible and accessible for the user in a safe area. And safe area just basically
adds a little padding on the top if it's needed. So if there was a
little nudge here, it would add a padding
to this whole thing. And everything would
be a little bit more to the bottom of the screen. Then we have the
navigation rail. NavigationRail has
destinations which are defined. In our case, we just have to
define by an icon and a label. And we-- it also
has this parameter, called Extended, which
I can now just change. You don't need to do this. But if I change it to True,
you see what it looks like. It's like this more
desktoppy piece. And then if I do False, it will
go back to the more, I guess, tablet or phone look. And then we have selectedIndex,
which is currently just set to 0, which is the first. It's 0 index, so it's the first
navigation kind of button. I can change it again. I can just change it to show
you that it would change to 1 and back to 0. And then we have
onDestinationSelected, which is a callback that you
can give to NavigationRail. And that will be called
any time the user selects a new navigation. So if I select this
one, right now, we only have a print there that
says, selected 1 or 0. You can select it
multiple times. We will learn how to deal
with this in a minute. I wanted to show
you what happens when you crash your
app during development and how to get back from it. So you see that
the selectedIndex must be, obviously,
between 0 and 1 because we don't have more
than two destinations. So what happens if I
set it to 7 and save? Well, the app crashes. I mean, it doesn't really crash. It's still there. And I can still step through it. So I can-- on the
top of the screen, now, I have these kind
of debugging options, including Continue. So if I continue, it will say-- our app will have this
big, red screen that says, this is what happened. And this is terrible. [LAUGHS] As we suspected,
right, because if you select an index of 7 out of
2, then yeah, that's bad. That's bad. But the cool thing is that I
can go here and change it to 0, back to 0, and save, and
I'm back in business. Everything just works. And I think that's great. That will happen to
you once in a while. So just go, let
the error happen. And then change back to a place
where it didn't crash, save, and you'll be at the same place
with the same state as before. Everything is-- you
can still work on it. And you don't need to
restart or do anything like that, unless, of course,
your error is really, really bad. But that normally
doesn't happen. Good. Let's go to the next step. The next step is
talking about stateless versus stateful widgets. Again, this is a part where you
could just read through this and possibly get a
better idea of what I'm going to talk about. But let me try and tell
it to you through a video. So as you start, as your
app grows in complexity, you will often have
things like, OK, I want to make sure that when the
user clicks on the Favorites, first of all, the
selectedIndex will change to 1. And also, this part
will be different. So you could say,
oh, that's appState. So I will just add it to
my appState as a new thing, like int selectedIndexOnHomepage
and do all the things that we did with Favorites,
and with Current, and Get Next before. The problem here is that as
your app gets more complex, this is not really
where you would want to put things like these. Because in this particular
case, the MyHomePage is just one of many pages. And it doesn't really-- no other widget needs to know
what you have selected here. The Generator page will
either be here or not. So it doesn't care. And anything-- everything
else also doesn't care. So that's why we have
stateful widgets. Stateful widget means
that the widget-- as opposed to stateless
widgets, stateful widgets can change themselves. They can change--
can, not cannot, but can change something about
themselves and then rebuild themselves based on
the new information. They contain some
state that is theirs. And they manage that state. And then they rebuild when
that state is changed. OK. In our case, our
MyHomePage widget will have a selectedIndex
field, a state of its own. And it will just rebuild
any time this is changed. OK. So let me see if I-- so this is what a change
notifier MyAppState would look like if all
the widgets in your app used it for everything,
for every state. And you can see that my little
doodle says no because it would be pretty nasty. So instead of going that way, we
will create a stateful widget. And we will, again,
use a little helper. Let's go to Visual Studio Code. And let's click on
Stateless Widgets, be here, for MyHomePage,
and Command-- you already know
this, Command-Dot. And the one action
that is available is Convert to Stateful Widget. And we click this. And you can see that the
MyHomePage widget becomes a stateful widget. And almost all of it, or
basically all of it now, goes from here to here, to a new
class, called MyHomePageState, with an underscore,
which means-- in Dart, it means
that it's private. And it's a state. It's not a widget anymore. It's a state-- widgets, states. And the state has
a build method. But now, the build
method can actually access the changing state. And the build method can also
call something called setState that we'll get to quite soon. But if I save,
everything is the same. And let's see what the
next step asks us to do. This is about-- this is
what I've talked about. And now, we are going to add
a new property, selectedIndex. All right. So no. SelectedIndex is a new
property of our field on MyHomePageState. It's not on the widget. And now, we can use it here. But nothing will happen
because it's always 0. So it's still-- this
is still doing nothing. And then-- and I'm just
going to check back if there's anything more here. No, no, no. OK, good, good, good. And then all we need to do--
well, all we need to do is say, selectedIndex equals value. Except when I do this, and
I'm like, oh, I'm fixed it, and I start clicking,
nothing happens. This is because any time
you change something, you run any code that
changes the state, you have to wrap it in a
function called setState. This tells Flutter that
something important has happened and this
widget should rebuild. So we will set state, and put
this over there, OK, and save. And now, bam, I
can change what's selected on the left
in the navigation rail. Now, this itself is
not super useful. [LAUGHS] But we are almost
there because we now-- the selectedIndex will
always be either 0 or 1 based on what the user has selected. So now, all we need to do
is to change this based on what the user has selected. How do we do that? Well, we use the
selectedIndex in a similar way to how we use the icon. So I will just copy this
and show you how that works. So over here-- oh,
no, not over here. Wow. On top of scaffold,
between build-- start of build and
return Scaffold, you add this piece of code. And why is there a-- wow, that's a mistake. I should fix this. There is a semicolon. Hopefully, by the time
that you are doing this, the semicolon will not be there. Anyway, it doesn't do anything. So what this does
is it says, I'm defining a new
variable, called Page. And it's going to be a widget. And based on what
selectedIndex is, it's either call
one of these cases. If it's 0, then Page
is GeneratorPage, and you're fine-- break. If it's 1, the page
is Placeholder, which is a widget that
also comes with Flutter. And it's just-- it draws
a little box that's a placeholder. This is really nice when
you are working on your app and you're not yet ready
with one part of the UI. You just put a
placeholder there. And you know already
what that is. And then we have-- if it's not 0 or 1, we throw
an unimplemented error. Right now, we know that
this will never happen. But it's nice to have it
here in the switch statement so that when, at
some point, we will add a new item in the navigation
rail, and we start like-- we click on it, then we
will immediately know, oh, we need to change this. We need to add a new case with-- to here and assign to Page. So this helps. If it wasn't here,
actually, then we couldn't use Page because-- I'll show you after we're
actually using Page. OK. And so now, Page is assigned
to the correct widget. So instead of using
GeneratorPage here, we'll just use Page. All right. And now, it works. Bam. If you go to Favorites,
this is the placeholder. And if you go back
to Home, it's this. So works well. Now, I told you that I
will show you what happens. If I didn't have the
default, then Dart would correctly understand
that Page could be null. Dart is only null save,
which we haven't covered yet. But it's not a big deal. It's just that Dart can
prove that Page could be-- or rather, Dart cannot prove
that it will never be null. So it will be like, oh,
but what if selectedIndex happens to be 2, or 7, or -1? Then Page will never
get assigned to, and therefore, it will be null,
and therefore, a null error. Hmm. If you do this, then it will
know that oh, OK, So well, something else will
happen before we go here. So it's fine. You could also
say, you know what? Page is placeholder always. And then you don't need this
because then, Page will-- because Dart will
now know that OK, Page will always be
something rather than null. So but that's just a cool little
thing for people out there who like null safety. [LAUGHS] OK. Cool. So we now have a
functioning navigation rail. Let's see what else is in store. All right. Doo, doo, doo, doo, doo. Yep, that's what we just did. And that's what we just did. Yep. Yep. Yep. All right. And that's the end
of Navigation Rail. Let's go to the next page. Actually, yeah, it's just-- the only thing that is required
for our app to be amazing is to add the actual
Favorites page. So right now, we have
the placeholder there. And for that, I have another,
much bigger challenge, which is basically, hey, can
you try and do it yourself? If you are up to it,
I will stop talking because I think you can just
read through the pointers that I have here
that kind of give you some ideas of what you could do
there, and so on, and so forth. But you can pause. When I say now, you can
pause and try it yourself. And as always, if you try
something, even if you fail-- and you will probably fail
at least in some ways-- it is much better than if
you just watch me do it. So it's painful, but also
very, very, very educational. So if you want to, pause now. And hopefully you paused
and now, you're back. And you have some
amazing Favorites page that I didn't even
think is possible. [LAUGHS] But I will show
you what I came up with. So that's the Favorites page. So I'll just add it to
the bottom of main.dart. So that's it. And then here, instead
of Placeholder-- where was it? Yeah. Yeah. I'll just do FavoritesPage. Favorites, save. And now, yeah, we have-- so what the widget
looks like is-- it also depends on appState. It watches appState
because it must. It needs to know what
favorites are there. And if the favorites change,
it should be rebuilt, even though right now, you
cannot change the app state as the way I did it. But you could. In the future, you could add
a Delete button, for example, or a Clear button. And that would
change the app state. So if the Favorites
is empty, then we will just center the
text of No Favorites yet. That's exactly what happens now. If not, then we have a listView. ListView is like a column
that you can scroll. So it's important that
you use a listView because if it was
just a column, then if you had too many favorites,
you would basically-- the Flutter would not be
able to show all of it. And it would just--
and you wouldn't be able to scroll to
the ones that you want to see that are off-screen. So listView is how you have
something that is scrollable. And then, yeah, then you
have this many favorites and listTiles. These could be just normal
other widgets, like Text. But the listTile
gives you padding, and it gives you
some nice things too. So I'll just like meatsuit,
wrongsnow, and forkboy. And I go here. And now, you have
three favorites. And it lists them. And this is what
listTile looks like. It has a leading icon-- leading whatever, but
in our case, icon. And the title is here. And it has-- also, it uses a
little different style of text, again, coming from theme. So it looks a little
more interesting than if it was
just a normal text. So that's it. That's the Favorites. That's one way to do it. Of course, there are
a billion other ways, and much more probably
interesting than this one. And yeah,
congratulations, I think. You just did an
amazing little codelab with an app that is actually-- I made this app many years ago. And I still laugh sometimes
going through it and just generating new names
because it's fun-- walkhour. It's just like, I want to
create something that's called walkhour, either a startup
or, I don't know, a book. Anyway, so that's it. You did it. We've covered all these things. We've covered the basics
of how Flutter works. We've covered creating layouts,
connecting user interactions-- that's the-- if you click on
this, it's something happens in the appState. We talked about
extracting widgets, which is a big part of keeping
your Flutter code organized. We talked about making
your app responsive, at least a little bit
with the layout builder. And yeah, we used themes. So we achieved a consistent
look and feel of our app. Just to get back,
to hammer it home, now that we have all of this,
I can change it to green, or I don't know, and save. And now, this is green. This is green. This is green. Everything is now green. And everything is in
the same color scheme. So that's, I think, pretty cool. Next up, what can you do next? So you can experiment with
this app a little more. And I really recommend,
just experiment with it until you break it. It's much more fun. Or if you have something
in mind that you want to build with
Flutter, then this is your chance to try things
with this completed app that could nudge it towards
your direction, to wherever you want it. You can also look at the
advanced version of this app. So I will open it here. It's in DartPad. And I will copy it so
that we don't need to-- I mean, we can run it on-- I will just do. We can run it in the
browser, which is fine. But you can run, obviously,
the same code here as well. So I'll just-- yeah, I should
probably copy the whole thing, not just without the imports. And if I save, you can see
that things are a little bit different. But basically, this is the
app that we already know. So we have a little thing here. I'll just put it here. Oh, so it's more responsive. So instead of a navigation
rail to the left, it now has a bottom
navigation bar, to the bottom, if you're on
a mobile-like size of screen. But everything else
looks the same. We have a history. We can walk-- walk? We can scroll up and down the
history and favorite things. You can see how we are
using a little shader or a little gradient so
that things obviously fade into the past, I guess. But you can still go there. And favorites looks
like this now. We're using grid view
instead of list view. So it looks good, even
on a bigger screen. I think it looks
better this way. And you can delete
things, like this. And it's still--
oh, and the red-- I mean, the big card looks
a little bit different, but not much. It just has one part
is a different color than the other part. That's it. I think you will
have fun with Flutter if you give it a chance. And you already did. And last but not least, you
can go to flutter.dev/learn. And you have all
these ideas of what to do next if you're a beginner,
intermediate, advanced. And so yeah, go
ahead and have fun. Thanks for going
through this with me. And see you around. [MUSIC PLAYING]