Building your first Flutter App - with a Codelab!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[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]
Info
Channel: Flutter
Views: 420,407
Rating: undefined out of 5
Keywords: Building your first flutter app, how to build your first flutter app, build your first flutter app, flutter latest, flutter updates, what's new in flutter, how to use flutter, introduction to flutter, intro to flutter, flutter tutorial, flutter developer, flutter developers, google developer, google developers, google, flutter, dart, dash
Id: 8sAyPDLorek
Channel Id: undefined
Length: 92min 4sec (5524 seconds)
Published: Wed Mar 01 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.