Observable Flutter: FlutterFlow

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
CRAIG LABENZ: Hello, everyone. And welcome to another episode of "Observable Flutter." My name is Craig Labenz. I'm your host. And today, we've got a pretty special episode. I'm excited. Of course, it's not a secret what it is. It literally says it on the screen right now. So I won't waste a ton of time. I'm really looking forward to getting into this one. But first, a reminder. Flutter folks, our community has a great reputation. Let's live up to it. Let's embody it. We can do this. All right. And without further ado, today's guest is Pooja Bhaumik, a Flutter GDE-- no. I'm going to let her introduce herself. Pooja? Well, I'm going to find the button somewhere. Welcome to the stream. Would you like to introduce yourself? POOJA BHAUMIK: Absolutely. First of all, thank you for having me on the show. This has been a dream to be on the Flutter channel for sure. So yeah, it's a debut for sure. And yeah, about me-- I am Pooja Bhaumik. I've been a Flutter GDE since 2019. I basically have been doing Flutter since 2018. I think Craig hadn't even joined the theme then, I guess. So it's way back then. I've been doing Flutter. I've been loving it. I've fallen in love with it. I have a small YouTube channel where I sometimes post videos. Sometimes, I write on Medium. And recently, I've be doing more talks around the world. So it's been very exciting to talk about Flutter with the community. And currently, I'm working at FlutterFlow as a Developer Advocate. We'll talk about FlutterFlow later, but yeah. It's been an amazing journey because it's still my favorite community. So I'm so glad to talk to you all about whatever we want to talk about today. CRAIG LABENZ: Yeah. Welcome. A few thoughts popped in my head as you were saying all that. This isn't your first time on the Flutter YouTube channel. You are a returning personality. POOJA BHAUMIK: Big screen? No, I think. We have had reels. Yes. But big screen, I think I was there on the live for Flutter Live, but I don't think the Flutter YouTube channel was there then. So I don't remember. Yeah. CRAIG LABENZ: Interesting. Yeah. Because you did Ask Flutter at Flutter Live, right? POOJA BHAUMIK: Yes, yes. CRAIG LABENZ: Yeah. POOJA BHAUMIK: But yeah, it was 2018, but I don't think Flutter YouTube channel was there. CRAIG LABENZ: And that was in 2018, you said? POOJA BHAUMIK: Yeah, December 4. CRAIG LABENZ: OK. So gosh, that's so funny. I discovered Flutter in November of 2018. So you were already on the precursor to the Flutter YouTube channel. And I was so new to Flutter, I didn't even know that event was happening. I didn't watch. I had no idea. POOJA BHAUMIK: Oh. CRAIG LABENZ: So you predate me by quite a bit in this community. And now, you're at FlutterFlow, which we're both pretty excited to talk about today. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Let's see. I'm going to quickly read some chat messages. Excuse me. Randall-- never stopped believing. You put on your journey hat and you knew that this show would happen today. Now, when you say you never stopped believing something would show up in this time slot, did you-- I intend for it not to be a mystery. Was it a mystery? Because if so, then I don't do enough to advertise upcoming episodes. So please let me know what you knew. Actually, everyone, did you know that this was going to happen before we started? I hope you all did. Ooh. No-Code Start-Up-- this entire account is committed to the FlutterFlow way. Their very name is indicating that they would love FlutterFlow. And they do. It lets you build apps really fast. Let's see. Anyone else? Doo-doo-doo. All right. Oh, we've got one answer here. I didn't know I got a YouTube alert. Interesting. I wonder how we could do a better job? POOJA BHAUMIK: Yeah, I guess the notification was on for him, but yeah. CRAIG LABENZ: And Pooja is the Flutter GOAT. Indeed. I recently used that with a non-native English speaker. And they were very, very confused. POOJA BHAUMIK: I know. So John is actually a colleague in FlutterFlow. And I've told him that if you use this in India, goat is not a very good compliment for Indians. [LAUGHTER] Yeah. CRAIG LABENZ: Yeah. It wasn't here either until someone just noticed that the acronym for Greatest Of All Time was spelled GOAT. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Before that, it was just like, you call someone a donkey or something. It'd be like-- POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK. Thanks. Many have been canceled. This one was promoted only when I saw it show up on the live upcoming slot, and that was yesterday. That is probably about when it went live. So all right. Anyway, yeah. Always still brainstorming how to make sure folks who care, folks who want to show up are able to. OK. All right. Last thing before we get started, some more Pooja love. POOJA BHAUMIK: Thank who. CRAIG LABENZ: Who among us could claim anything different? OK. So it's going to be-- oh, yeah. I need to share my screen today. I forgot. I do this differently when there's a guest. So I'm going to share my screen-- an entire screen. It will be this one. There we are. And I'll add it. And there we go. Pooja, our backgrounds look similar. It's like we could be next to each other in the same room. We aren't. POOJA BHAUMIK: Yeah, mine is-- CRAIG LABENZ: Yeah. Mine's not. I'm sitting in front of a green screen. OK. So Pooja, today, we're going to build something with FlutterFlow, as of course you know, but for everyone else, Pooja is going to guide me through it. I have seen the same FlutterFlow resources on the YouTube channel that you all have seen. I have never used it. And today, that streak ends. I just created my account. And Pooja advised me that I should have a Firebase project ready to go. So I clicked through those buttons as well, and haven't done a single other thing with that Firebase project. I don't even know what we're building today. Pooja-- POOJA BHAUMIK: Yes? CRAIG LABENZ: What are we building today? POOJA BHAUMIK: All right. So I think that you build a lot of games in this segment. So I was thinking, what's the most iconic game that we have played in our lives? And that can be, since you're learning it for the first time, something that's also easy. And also, we can use all nice animations and Firebase and stuff like that. So do you remember tic-tac-toe? The XO game? CRAIG LABENZ: I certainly do. POOJA BHAUMIK: Yeah. CRAIG LABENZ: I was wondering what you were going to say for most iconic game we've played in our lives. And tic-tac-toe is probably-- POOJA BHAUMIK: No? CRAIG LABENZ: Yeah. No, I think that's a good pick. I think it's a good pick. POOJA BHAUMIK: Yeah? That's how I grew up. That's the game that I've played growing up. Yeah. At least when teachers used to have boring classes, that's all we used to do. So yeah. So we can build tic-tac-toe, but I think we can have a lot of features where it's not just playing with two players, but also playing with AI, maybe using the OpenAI API, or even having a game, a list page where we can show all the games that's being played around the world. We can use Firebase to host that data. So we can do that. And yeah, we will see what we can cover in this segment. CRAIG LABENZ: Guess the number I'm thinking is probably the only simpler game, or which hand has the candy? [CHUCKLING] POOJA BHAUMIK: Yep. CRAIG LABENZ: OK. Already an interesting question about FlutterFlow. Pooja, to you, can I use FlutterFlow in MySQL or another database? Does Firebase have the highest priority? POOJA BHAUMIK: Yeah, we do have Firebase as one of-- it was the first database that we integrated with FlutterFlow natively. And we have Supabase as well. We do have SQL not in public right now, but mostly for enterprises, but then we are trying to bring it to public as well. But at this point, yeah, natively, no. If you want to use custom code or you want to use something customization, you can, probably, but I know it will be a little tricky. But yeah. CRAIG LABENZ: So you can always write your own code. POOJA BHAUMIK: Yes. CRAIG LABENZ: So you could just write whatever code you need to connect to a database. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Right, right, but you have a lot of helpers in place to make it very quick to get going on Firebase? POOJA BHAUMIK: Yes, yeah. So the integration is more native of Firebase and Supabase. So yeah, you will not have to write so much of custom code for most of it, but yeah, that's an option. CRAIG LABENZ: Yeah. Now, Vladimir asked, can I use MySQL or another DB? Of course, Supabase is Postgres under the hood. And I would always recommend we use Postgres over any of the other open source relational databases. A bunch of chat messages came in. Same answer for this, I imagine. You can use Mongo? Yep-- because you can write your own code in the back end. OK. So we're going to make tic-tac-toe. And I was thinking we might call this tic-tac-flutterflow. POOJA BHAUMIK: Nice. All right. CRAIG LABENZ: Now, what should I click here? POOJA BHAUMIK: Yeah. I want to tell you about this. So when you're creating a project on FlutterFlow, we do come up with a lot of templates. So if you know what is the kind of apps-- maybe you want to make a home finding app, or you see the things that are there, like push notification custom code. So it gives you a head start if you know if one of them makes sense for your use case. But for now, we would want to start from scratch. CRAIG LABENZ: Go blank. Yeah. POOJA BHAUMIK: Yes. CRAIG LABENZ: We can do this later, maybe, but this is 10 screens. I want to see those screens. How do I do that? POOJA BHAUMIK: You can basically open up-- CRAIG LABENZ: Just click New sample? POOJA BHAUMIK: Yeah, yeah, yeah, yeah. CRAIG LABENZ: I'm not committing to anything if I do this? POOJA BHAUMIK: No. It will just create a project. And then you can create a new blank project later. CRAIG LABENZ: Oh, but I already used the name tic-tac-flutterflow, and I'm attached to that. [LAUGHS] POOJA BHAUMIK: Oh, yeah, yeah. Yes. Now, you might have to-- CRAIG LABENZ: Oh, package name. Oh, I can change it here. All right. This is just exploration. Observable. That's how you spell observable. Exploration. Now, OK. The package name-- kilochat.app. Is this that the template name, essentially? POOJA BHAUMIK: Yeah, it's the initial package name that it came with, but you can change it according to your project name. CRAIG LABENZ: OK. Set up Firebase. I'm going to not do that now. I think we are going to do that with tic-tac-toe? POOJA BHAUMIK: Yeah. CRAIG LABENZ: All right. Let's start building here. OK, so yeah. I want to just see those 26 screens. POOJA BHAUMIK: So to see that, if you see on your left, there's the purple icon. The second one is the one that will show you all the screens. CRAIG LABENZ: All the screens. POOJA BHAUMIK: So you see all the pages? Yeah. So if you scroll down, you'll see all the pages that's part of this template. CRAIG LABENZ: Oh, this looks nice. My one bit of feedback here already out of the gate is it'd be nice to be able to peruse the templates in a lighter way than by making these projects because I imagine these are really impressive things. And someone's not going to make 12 projects to look at the 12 templates, but they might peruse them in a different way. Anyway-- POOJA BHAUMIK: We do have a solution for that, too. CRAIG LABENZ: Brilliant. OK. So let's back out of this. And we'll make the real project. POOJA BHAUMIK: Yeah, you can click on the-- yes. CRAIG LABENZ: Here we go. Create new. Now we're back to tic-tac-flutterflow. And we want the blank one. POOJA BHAUMIK: Mm-hmm. CRAIG LABENZ: Welcome. Congrats. Love it. Oh, no. That's not what I want. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Pooja, you're my tutorial. POOJA BHAUMIK: Yes, exactly. CRAIG LABENZ: All right. Here we are. POOJA BHAUMIK: All right. OK. So we have a blank project. And I think you should know what this tool looks like and what's happening around here. So before we even move to the code and stuff like that, I think getting to know the tool would be great. So if you can see, what you're scrolling at is the widgets catalog. And this helps you basically do a drag and drop. So you can just drag something and drop it into the canvas. And yeah. Do you want to drop something in the canvas just to try it out, maybe a text? CRAIG LABENZ: Yeah, sure. Yeah, yeah. Hmm. Oh, there it is. OK. So now, ooh. I have so many questions, but you go first. What are you going to say? POOJA BHAUMIK: No, I'm just saying that once you have this clicked, you can see on your right that the widget properties are mentioned. So you can just change the properties. Yeah. So padding, the font, and stuff like that. CRAIG LABENZ: What did I do? How did I go-- POOJA BHAUMIK: What did you do? CRAIG LABENZ: Oh, I just clicked on the scaffold. POOJA BHAUMIK: Yeah, yeah, yeah, yeah. CRAIG LABENZ: OK, there we are. All right. I do want to change the padding. There it is. Oh, top. Left. OK. So is there a way-- oh, it's locked now? POOJA BHAUMIK: Yeah, so when you have it locked, it'd be-- CRAIG LABENZ: Oh, I type here. POOJA BHAUMIK: Yeah. So when it's locked, that means that-- CRAIG LABENZ: It's the same. Wonderful. POOJA BHAUMIK: Yep, yep. CRAIG LABENZ: Right. What's the easiest way for me to wrap this in a center widget? POOJA BHAUMIK: So we have the alignment here just after that, just after padding. You have the alignment. You can click on the center. CRAIG LABENZ: Frick. I keep clicking off it. All right. So here we go. Centered. POOJA BHAUMIK: Yes. So if it's in a column, you might want to have the center alignment to the column. So technically, we are not using the center widget to do this. We're using the align widget. So there is no expose of the center widget only, but we have the align widget, which you can align it to any of the positions. So technically, if you want to see what exactly is happening in the tree, you can go-- CRAIG LABENZ: I do. POOJA BHAUMIK: --that second tab in the left side. CRAIG LABENZ: This one? POOJA BHAUMIK: Yeah. CRAIG LABENZ: Widget tree? POOJA BHAUMIK: The widget tree. Yes. So if you see, this is inside a column. So that's why it was not centered exactly to the center of the screen. So yeah, you can wrap it with a container or whatever you want. CRAIG LABENZ: Interesting. OK. One thing I'm going to do a few times here, Pooja-- and I hope I don't derail it so much that we don't get anything done-- is just ask questions to address my own hesitation FlutterFlow as someone who just loves writing code and loves the control, and thinks back to old Dreamweaver days when I didn't like that boundary between myself and the HTML. So is there a way for me to not just see the widget tree represented here? What if I know I need a center widget? Can I go into the editor and add it myself? POOJA BHAUMIK: So right now, just to show you first the code, if you see in the top right, there is a code sign just beside the green button, literally the top in the app bar. Yes. Yeah, yeah. Yeah. CRAIG LABENZ: This one. POOJA BHAUMIK: Yes, yes. So when you click on it, you can first view the code. You can download the code later as well. And yes, your idea that oh, you want to add a particular widget in a way-- it might not be possible through this particular UI way because when you write, you can download the code. You can add something in a way, but that code is not getting back to FlutterFlow because we are overriding your local changes. However, if you really want to build a custom widget, then you can just use the custom section of FlutterFlow and build your widget the way. But adding it on the native widget that we have on FlutterFlow, which is exposed to FlutterFlow-- that may not be exactly possible. CRAIG LABENZ: Got it. And if I make my own custom widget, can I then capture it as this thing that I can keep reusing later? POOJA BHAUMIK: Yeah, yeah. CRAIG LABENZ: Oh, OK. Great. I think that-- POOJA BHAUMIK: Yeah. CRAIG LABENZ: --solves it nicely. POOJA BHAUMIK: So you can drop it to the canvas, the custom widget itself. And technically, you can use it in any of the screens that you build. Yeah. And if you have a theme account, you can always build some custom code and share it across projects. CRAIG LABENZ: Gotcha. OK. Also, folks, by the way, keep asking questions. I'm peeking over at the chat, and I'm starring a bunch of them later. So your questions aren't being ignored, but at opportune moments, I'll surface them for Pooja to answer. So OK. I have a few of those starred up. Thank you, everyone who's asking good questions. So I'm done distracting you for now. So the idea is you've got the builder. It has the controls that it has. Doesn't have every control, but there is a place to just make your own completely custom widget, write whatever code you want. And then that thing that you create now becomes a thing that you can use elsewhere in FlutterFlow. Is that all right? POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK, cool. So I'm going back to the app. And yeah, so back to tic-tac-toe. What should we do next? POOJA BHAUMIK: All right. So let's see what we have on the Connect tab of the left side. We saw the tree, but if you see the first tab, after that, there is a storyboard, but since we do not have a lot of pages right now, it only makes sense when we have a lot of pages and navigation is happening. And you can just view the entire flow of your application, but right now, it's just one screen. So do you want to see the-- so the first one is for Firestore. When we actually do implement Firestore, we can check that out. The second one after Firestore is the data types. So if you want to create your own model classes, you can do that here. CRAIG LABENZ: Nice. So what are you thinking? A game class? POOJA BHAUMIK: We can have a game class. Yes. CRAIG LABENZ: A board or-- POOJA BHAUMIK: Yeah. I guess if you want to save this game object to Firebase later for all the games that people play, I think game object is fine. CRAIG LABENZ: Game. Love it. OK. Let's add some fields. So what is the right way to store-- you could imagine a string that's just nine characters long. And it's either blank spaces or Xs or Os. And then you'd just map that to the 3-by-3 grid of tic-tac-toe. Have you thought about any more sophisticated ways? POOJA BHAUMIK: Yeah, so I was thinking game could only store the player name and the winner information, but for the grid, we can have a different data type because we don't-- CRAIG LABENZ: Love it. POOJA BHAUMIK: Yeah. So game could just be-- because I don't think it makes sense to store anything of the grid to the Firestore. So that could be a different data type, and game could just be player X, player O. CRAIG LABENZ: Got it. OK. Oh, yeah, right, right, right, right. POOJA BHAUMIK: Yeah. So player X name-- CRAIG LABENZ: It's a list. It is not. POOJA BHAUMIK: It's not a list. And player O. CRAIG LABENZ: And this is a string as well. Oh. POOJA BHAUMIK: Yep, you got it. CRAIG LABENZ: I'm not even trying to butter you up or anything here, but this does feel-- it feels pretty good. POOJA BHAUMIK: OK. CRAIG LABENZ: OK. So we have our game. And oh, you're thinking don't even bother persisting the grid to Firestore. Oh, the winner is also a thing you were saying, right? Winner. POOJA BHAUMIK: Yeah, we can add the winner name. Yeah. Yeah. It could be X and O, or whatever it is. So for the grid, I think it could be a different data type-- maybe a grid item or something, which takes X and O as a string or blank. It could also be blank, of course. So yeah. Game board could be the parent, I think, but it should have a list of another data type, right? CRAIG LABENZ: So what I was thinking for the game board is literally just who's played what? So the nine squares and which ones are Xs and which ones are Os. POOJA BHAUMIK: Yeah. So I want to add more to just the fact that-- OK, we have an X and O, but there could be more information we might want to add to each grid item. So that's why we could have a child data type under game board-- so a board item or something. CRAIG LABENZ: OK. I'm going to do whatever you want on this because I think you have a vision in mind. So do you like the name game board, or would you a different name? POOJA BHAUMIK: Yeah, that's fine. That's fine. So we can for now cancel it because we need to create the other data type first. So let's create another one. CRAIG LABENZ: OK. So game item, you want me to call it? POOJA BHAUMIK: Yeah, board item or something, or grid item. Whatever you want. Yeah. CRAIG LABENZ: All right, grid. I think that's a good idea. So grid item. All right, what are you thinking of putting in here? POOJA BHAUMIK: Yeah, this can have a mark or a label or whatever you want to call it for X and O or blank. CRAIG LABENZ: Mm. I see. What should we call this? Are you imagining-- there's a couple things here, right? It could be the coordinates and the value-- POOJA BHAUMIK: Oh, the position, you mean? The position will be held by the list view or grid view itself. So the index-- we don't have to store it here. Since this is going to be a list of grid item, the index, we will know when we-- CRAIG LABENZ: Oh, I see-- where it is in the list. POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK. POOJA BHAUMIK: It'll be a list. CRAIG LABENZ: So label. And then this is just going to be X or O. So we can do just string-- POOJA BHAUMIK: String, yeah. CRAIG LABENZ: --I guess, here? Yeah. There's not an enum? OK. POOJA BHAUMIK: No. CRAIG LABENZ: All right. String. Looks good. And this is not a list. POOJA BHAUMIK: No, this is not a list, but in game code-- CRAIG LABENZ: OK. POOJA BHAUMIK: No, yes, game mode. CRAIG LABENZ: Oh, so how do we add our thing? POOJA BHAUMIK: Data type at the end. CRAIG LABENZ: Oh. POOJA BHAUMIK: And get item. CRAIG LABENZ: Get item. And so this can be items? POOJA BHAUMIK: Yeah. So it's a list. It's a list. CRAIG LABENZ: Oh, stinky. POOJA BHAUMIK: Yeah. We can't edit it. CRAIG LABENZ: How do I edit this? POOJA BHAUMIK: No. CRAIG LABENZ: There's no editing? POOJA BHAUMIK: No. You can-- CRAIG LABENZ: In the Dumpster with you. [LAUGHTER] All right. So this was items. It's interesting that it starts on data type, but I have to select data type again to actually get this other dropdown. So that's a grid item. And it's a list. There we go. OK. POOJA BHAUMIK: Awesome. CRAIG LABENZ: Nice. POOJA BHAUMIK: Yes. CRAIG LABENZ: All right. POOJA BHAUMIK: All right. CRAIG LABENZ: What's next? POOJA BHAUMIK: So we have our data types in place. Now, we want to make certain global states, like the game object, suppose. So if you see the third one, I think the third one is the app state. Yes. So here you can create whatever initial global states that you want. So game could be a global state that is used across pages-- the game object that we probably will be storing also to Firestore later. So this could be the game-- yes, yes. CRAIG LABENZ: Persisted? Yes? POOJA BHAUMIK: No. Every time you play a new game, it should-- yeah, make a new one. Yes. CRAIG LABENZ: Oh, so persisted isn't to Firestore. It's a local storage? POOJA BHAUMIK: Shared preferences. Yes. CRAIG LABENZ: I see. OK. POOJA BHAUMIK: Yeah. So all right. So now, we can have the game. So basically, we want to initialize the grid with blank strings. And we might want to have one app state for the game board object or the board. CRAIG LABENZ: OK. So should we make that in the game or-- POOJA BHAUMIK: No, outside. CRAIG LABENZ: --it's its own thing, another thing? POOJA BHAUMIK: Yeah, outside. Yes. Outside. CRAIG LABENZ: So what are you imagining we call this one? POOJA BHAUMIK: Board. Just board. CRAIG LABENZ: OK. And this is also not a list. And we're also not-- POOJA BHAUMIK: Not a list. Yeah. Not a list. Yes. CRAIG LABENZ: OK. POOJA BHAUMIK: All right. So I guess if we need any other app state variables, we can create that later. CRAIG LABENZ: We'll come back. Yep. POOJA BHAUMIK: Yeah. So this was a type of grid item, right? Yeah, I think yeah. All right. CRAIG LABENZ: Yeah, grid items are on the board. POOJA BHAUMIK: Yep, got it. So I think the fourth one is for API calls, but we will not do it right now. We can do it later, but you set up your API calls here. We don't have any assets. Then we have the custom code. We can get to it as well later, but do you want a walkthrough of the custom code right now or later? CRAIG LABENZ: I do expect we'll get to it later. So maybe we'll just peek at it real quick here, but then not spend a ton of time on it. So custom functions, widgets. OK. Oh, this is a dropdown. And we don't have any. So that's fine. And then we'd add something up here. And we pick which one we add. And then it appears. OK. This makes sense. POOJA BHAUMIK: Yep, yep, yep. CRAIG LABENZ: All right. POOJA BHAUMIK: All right. So I think yeah, so just more other features-- theme. CRAIG LABENZ: Yeah, we're ready to build. POOJA BHAUMIK: I do want to talk about the theme in the last, second one. CRAIG LABENZ: OK. POOJA BHAUMIK: Yeah. So when you're building your project, you will have some color scheme and stuff like that. And if you go into your colors, yes, you can do that as well. So if you go into the Colors tab-- yeah. So here you can decide what is your light mode theme and dark mode themes for primary, secondary, tertiary, whatever it is. CRAIG LABENZ: Nice. POOJA BHAUMIK: And yeah. You don't have to-- basically, this will create that theme class for you. Yeah. This is our AI feature. If you want to try it out, you can think of a certain-- oh, I don't think that is enough information. CRAIG LABENZ: You don't think this is enough information? POOJA BHAUMIK: What is a splashy tic-tac-toe game? CRAIG LABENZ: We're going to find out. [LAUGHS] POOJA BHAUMIK: It's bright. CRAIG LABENZ: It is bright. POOJA BHAUMIK: It's bright. CRAIG LABENZ: It is bright. POOJA BHAUMIK: Yeah. CRAIG LABENZ: This accent color is-- oh wow. It is literally quintessential green. Look at this hex code. [CHUCKLING] Blow this up for everyone. POOJA BHAUMIK: All right. CRAIG LABENZ: We are getting the platonic ideal of green here. Amazing. POOJA BHAUMIK: What's the dark mode like? CRAIG LABENZ: Dark mode. POOJA BHAUMIK: Oh, that's the same. OK. Oh, if you want to save it, you can save it. CRAIG LABENZ: Let's do it. It's a splashy tic-tac-toe game for Millennials. All right. POOJA BHAUMIK: OK. All right. So yeah, you can also set your typography icons, theme widgets. So if you have certain widgets that are created for your theme, you can create it here and then use it in your screens and stuff. But for now, we don't need to get to that. We can go back to our tree on your canvas. CRAIG LABENZ: OK. POOJA BHAUMIK: Second one. Yep. CRAIG LABENZ: All right. POOJA BHAUMIK: Yeah, Ander says that's splashy. Yes. Yeah. All right. CRAIG LABENZ: Welcome, Ander. OK. POOJA BHAUMIK: Awesome. CRAIG LABENZ: So we've made a theme. We've made some data classes. And then we hooked up some app state to-- well, we attached some of those classes to our top level app state. And I know a lot of people are going to be wondering, what is the state management approach in a FlutterFlow app? How unconstrained is it? People love their very specific setups. And can they use them? I don't know if we're ready to answer that yet, but I know it's on everyone's mind. POOJA BHAUMIK: Just to answer it brief, we are using provider to basically-- as our state manager solution for the application. There are certain questions about-- oh, can we use different architecture or different state manager solution? Technically, it's not possible because we are generating the code for you. So you cannot just plug in that-- hey, I want true code instead, and whoo, there's a magically generated code. Not yet. Not yet. So we don't have that kind of a thing. We are basically taking away your time span of thinking about the architecture and the solution. And let us think about it. You just do what you want to build. And the generated code will have a good, nicely written architecture and uses provider. CRAIG LABENZ: Mm-hmm. Mm-hmm. I do want to talk about one thing. There are some folks in the chat who are wondering why someone would use a tool like this, concerns it can delay learning, that kind of thing. And I do just want to say to everyone out there personally, I don't think someone would really be successful with this tool unless they have a sense of how Flutter works already. And maybe Pooja, you feel differently, but I see tools like this as things for fairly experienced developers to-- you already know what's under the hood, right? Just like back in math class, it's like, the teacher doesn't give you a calculator until you can do long division on paper. And that is a good practice in third grade. And I think it's still a good practice. Using an ORM is best after you understand SQL, but then these tools that just do get the boilerplate out of the way for you and help you think about-- everyone loves to say, get back to building what's unique to your app. I think those tools always still have a place. And there's different strokes for different folks, as they say in English. So if FlutterFlow's not your speed, that's totally OK. But I think this is going to strike a lot of people as, oh, this does seem maybe nicer, maybe easier. And I hope some folks discover it in that vein and whatnot and get some enjoyment out of it, but I hear you all out there wondering, is this going to prevent me from learning Flutter? If you start with this, it could be a challenge. Any other thoughts on that thread, Pooja? POOJA BHAUMIK: Yeah. For sure I think the idea that this is for learning Flutter-- it's not correct because this is not for learning Flutter. If your entire goal is to learn Flutter or any programming language, then this is not the thing that you probably will be doing, but this is for builders who want to build products and get to the market as soon as possible. And also, having a little bit of knowledge of programming will help you do a lot of customizations. And that's where your Flutter knowledge will be very useful, but I love coding, myself. And I like doing it from scratch, but a lot of times, I am never able to finish a side project and stuff like that or a project because coding does take more time. And when I'm talking about startups who need to get to the market as soon as possible, companies who need to reach the users as soon as possible, for them, the approach doesn't matter. It's the product, at the end of the day, if it's out there or not. So if you're thinking as a developer that you like coding and you're learning Flutter or want to learn Flutter, then this is not the right tool for you, but if you want to build products and you want to build products for your startup or company and you want to reach the users as soon as possible, then you can think about it. So it's the approach that might be different. Are you thinking as a developer, or are you thinking as a product builder? CRAIG LABENZ: Yeah. Yeah, I agree. I agree with everything you just said, Pooja. I'm also approaching FlutterFlow and this episode as someone who enjoys coding so much that I've always felt resistant to using these tools, but I just want to finally give FlutterFlow a really serious look and see what all it offers. And anyway, so those are my thoughts on all of that. POOJA BHAUMIK: All right. CRAIG LABENZ: OK. How do we build a tic-tac-toe board? POOJA BHAUMIK: OK. So we need a grid view, of course. And maybe we don't need an app bar. So we can remove the app bar from the canvas, if you like. I don't think app bar makes sense here. So yep. CRAIG LABENZ: That was great. POOJA BHAUMIK: All right. CRAIG LABENZ: We probably don't need to-- get rid of the column as well? POOJA BHAUMIK: We can have the column. Yeah, we can have it because if you want to show the text for who's playing what and stuff like that-- so we need a column, maybe. Yes, we have a grid view. Awesome. CRAIG LABENZ: We've got a grid. And it's below the text. I imagine we can just reorder like this? POOJA BHAUMIK: Yeah. So if you want to-- CRAIG LABENZ: --wanted to. POOJA BHAUMIK: So yes, there is different colorings, if you see-- the orange and the purple one. So one of them makes it as a sibling. One of them makes it as a child. CRAIG LABENZ: Oh, I see. OK. I don't think this is what I wanted to do. Yeah, I'm actually struggling to put the text back on top. POOJA BHAUMIK: Yeah, you can just-- the orange-- yeah. Sometimes, I also get confused with the colors, but I think the orange one is for the child. So if you put it on column, it will-- so yeah. You drag it to the column. The CRAIG LABENZ: Orange is child. So purple is-- there we go. POOJA BHAUMIK: Yeah, yes. CRAIG LABENZ: OK. All right. Whew, did it. POOJA BHAUMIK: Yeah, I know. CRAIG LABENZ: OK. We've done our grid view. POOJA BHAUMIK: All right. So now, we need a child, which will be repeating nine times. So yeah, to create a child, you can click on that Plus button, just the Plus button. Yes. So now, maybe a container with a text? CRAIG LABENZ: OK. POOJA BHAUMIK: Yeah. CRAIG LABENZ: And so I'm going to click this again. And that'll text. Ooh, nice. OK. POOJA BHAUMIK: Yep. You can center the containers, child so everything is in center. So if you click on Container, Properties-- yes. OK. CRAIG LABENZ: Oh, oh. Nice. OK. That actually did fully center it. So the align widget within a container was able to center it. POOJA BHAUMIK: Yep, yep. All right. Now, let's see if you want to do some properties of the container. In the container, we can add a border radius or something that we use. So if you scroll down and if you see the border radius, you can add-- CRAIG LABENZ: But I probably will. POOJA BHAUMIK: It's right there. The cursor is on it. CRAIG LABENZ: Oh, wonderful. Been a snake, it would've bit me. POOJA BHAUMIK: So yeah. CRAIG LABENZ: Ooh. Love it. POOJA BHAUMIK: And if you want to only have a radius for one of the corners, then you can open the lock and just have it for up, top, bottom, right, left, or something like that. CRAIG LABENZ: Right. So we could get real cute and only have border radius-- make our whole game look just the top left square has border radius in the top left corner and do that all around, but I'm not going to do that now because it would probably take a bunch of time. POOJA BHAUMIK: Yeah. Yeah. CRAIG LABENZ: OK. So we need nine of these. Should I just add eight more? POOJA BHAUMIK: Oh, no. So that would be hard coding it. We would want to generate it from a list or a variable, right? So you can do that. However, it doesn't make sense. So [LAUGHS] CRAIG LABENZ: Pooja, your shade. Freezing in this shade, Pooja. [LAUGHTER] You could do that if you were super dumb. [LAUGHTER] POOJA BHAUMIK: I didn't say that. CRAIG LABENZ: But it's what I heard. OK. So I do love the idea of doing this from a list. So how do I do that? POOJA BHAUMIK: All right. So you can go back to your grid view in your tree. Click on the grid view in your tree. Yeah, but you want to be in your tree. Yes. CRAIG LABENZ: Yeah. POOJA BHAUMIK: And now, go to your right. And you'll see there's more properties in the tabs there-- I think six tabs. Yeah. So the first tab is the Property. Second is the Actions. Third is-- I forgot what it's called, but fourth is for generating dynamic children. Yes. CRAIG LABENZ: Seems like what we want. POOJA BHAUMIK: So you can click on that. Yep. CRAIG LABENZ: OK. POOJA BHAUMIK: So now, do we have a list? OK, maybe before we create a list, just to see if this is working, you can create a variable name-- whatever you want to create. And then we can check what value. CRAIG LABENZ: --would be here? POOJA BHAUMIK: It would be grid item because so basically, it's each item. CRAIG LABENZ: So index, maybe. POOJA BHAUMIK: It will already have an index variable. CRAIG LABENZ: Oh. OK. POOJA BHAUMIK: Item, anything. Item. Maybe just item. Yeah. CRAIG LABENZ: OK. POOJA BHAUMIK: Then you have the-- no. You can go back. CRAIG LABENZ: I keep doing that. I don't know what I'm doing. I keep clicking out and getting-- it didn't confirm, so it didn't save. POOJA BHAUMIK: Yep, yep. CRAIG LABENZ: I don't know where-- where am I clicking to do that? I think it's just-- POOJA BHAUMIK: You're clicking on the canvas. CRAIG LABENZ: --board. POOJA BHAUMIK: Yes, yeah. CRAIG LABENZ: Oh, it's not even-- didn't do it that time. POOJA BHAUMIK: Maybe you clicked on the-- CRAIG LABENZ: Interesting. POOJA BHAUMIK: All right. So-- CRAIG LABENZ: Item. And I'm going to confirm. POOJA BHAUMIK: No. First, you have to give the value. So for now, just to try it out, we can do a random data because nothing is initialized yet. We don't have it initialized yet. So if you see the random data-- here is where you can attach your variable with a dynamic data type or global property or whatever it is. So in this case, yeah, list of random strings. It could be whatever values you want to put there. CRAIG LABENZ: Can I leave them blank? POOJA BHAUMIK: I guess it will-- let's see if it has a default value or not. It doesn't probably matter because we won't be really using this variable. That's fine. No further changes. All right. So now, you confirm. So this means that now, your list view or whatever view, or flex view is going to be attached to that variable list. And now, you can access the data of that list in your list view. CRAIG LABENZ: I see. Oh, interesting. OK. So in the text widget, we want to set the text to be that item. POOJA BHAUMIK: So if you see a little bit of orange thing on top of it-- yes. You can click on that. And it will open the same dialog. And inside item item, you can open the available options. So it's the string. It's a string, so no further changes. Yeah. CRAIG LABENZ: Oh, OK. And how do we use the value? Or we just already are going to use the value. POOJA BHAUMIK: This is a list of strings, so there is no-- it's just going to be-- yeah. Yeah. So now, if you want to run this just to see-- CRAIG LABENZ: I do. POOJA BHAUMIK: --if things are working or not, see the lightning thing at the top? CRAIG LABENZ: I do. POOJA BHAUMIK: Lightning? Yes, yeah. Click on that. So now, it's going to run your project in web in an iframe. And let's just check if you've done things right or not. And yeah. It's going to take some time for the first load for sure. So you can go back, if you want to. It's going to take a two-minute time. CRAIG LABENZ: Mouaz points out-- this is low code/no code in general. So for folks who love low code/no code solutions, look no further. This is a great point. All right. So it's cooking here. Actually, this is one of the moments where I like to ask questions. So here was one. These questions may go back half an hour. Can people make their own public/private templates for others? POOJA BHAUMIK: Yeah. We have a marketplace where you can have your templates out for the marketplace people. Whoever is using FlutterFlow-- they can just get it from marketplace. You can have paid or free. We just released marketplace V2. So yeah, it's possible that you can have that. CRAIG LABENZ: That's very cool. I would love to view this app state right now. Can I do that? POOJA BHAUMIK: No. What do you mean? CRAIG LABENZ: Well, I'd like to just see my list of strings. POOJA BHAUMIK: Yeah. I want to see if there's an error there. Can you go back to your dashboard and see if we created more than one item? CRAIG LABENZ: Well, I think my minimum-- yeah, my minimum length-- POOJA BHAUMIK: Was 0. CRAIG LABENZ: --was 3, right? Or was it 0? POOJA BHAUMIK: Just go back to that same. Oh, hmm. CRAIG LABENZ: Is that not what I wanted to do? Did I just do it wrong? POOJA BHAUMIK: So no. You just have two tabs open. One was for the test mode, and one was for the thing. So yeah, you can keep both open. CRAIG LABENZ: Whoa. So should I just click this lightning again? POOJA BHAUMIK: You can click it. And it will give you a warning that it's already open. And it will open that link again. So yeah, click on it. Yeah, just open session. And keep these two tabs open. CRAIG LABENZ: There's that. OK. Love it. OK, so back here on the grid view-- POOJA BHAUMIK: Let's see the list of random strings. Yeah, click on that. CRAIG LABENZ: There we go. OK. So this wasn't a good idea. POOJA BHAUMIK: Yes. CRAIG LABENZ: Let's go 3 to 9. POOJA BHAUMIK: Yeah. And you can also have them-- CRAIG LABENZ: I want to see the app. POOJA BHAUMIK: You can confirm it. And on the max items, you can set a number as well. No, no, no. Just before the value-- it's fine. This is fine. Close this. And yeah, here you can set 4 or 5 or something. CRAIG LABENZ: So that feels like it's the same as this. POOJA BHAUMIK: It's going to take the minimum item first-- CRAIG LABENZ: Of those two? POOJA BHAUMIK: It will take 4, but if you want to do 6 between that range-- you just want to hard code-- this is for testing only for just hard coding it, then you can do that. CRAIG LABENZ: I see. POOJA BHAUMIK: Suppose if you want to build a list where you only want to show 10 items per pagination, then you want to have the max item. CRAIG LABENZ: Interesting. Oh, because that's what renders versus the underlying data structure. Right. Got it. Seems obvious now that you've explained it. POOJA BHAUMIK: Yeah, yeah. CRAIG LABENZ: Would you look at that? POOJA BHAUMIK: Awesome. Now, CRAIG LABENZ: Think this-- yeah, it's wonderful. This restarted before I put the max item 6 in here. So I'm expecting now, if I go back and instant reload again, we should only see six things, right? POOJA BHAUMIK: Yeah, it should. If it doesn't, then it's-- CRAIG LABENZ: As long as I saved. Good. Did. It's not asking me to save. POOJA BHAUMIK: It's 6. OK. CRAIG LABENZ: 6. Great. POOJA BHAUMIK: Yay. CRAIG LABENZ: All right, so let's go back to 9 because that's the tic-tac-toe we all know and love. All right. POOJA BHAUMIK: Now, we are not going to do anything with random strings. We have to create our own. So basically, you can initialize the board with nine items. So now, I think it's time we can create it in custom code because I think it's easier there. You can create it from the UI as well, but it's probably more clicks, I would say. So I think the audience also wants to see some code. So let's go to Custom. CRAIG LABENZ: They do. POOJA BHAUMIK: Let's go to Custom. CRAIG LABENZ: Give the people what they want. Where is that? Oh, that was in the bottom left, right? POOJA BHAUMIK: Yeah. CRAIG LABENZ: There we go. POOJA BHAUMIK: Yes, yes. Right. CRAIG LABENZ: Ooh. I'm excited. OK. What do I want to add here? POOJA BHAUMIK: Action. We want to do an action. Yes. CRAIG LABENZ: OK. POOJA BHAUMIK: I'll tell you why, but you can name the new custom action as initialize-- CRAIG LABENZ: Initialize game? POOJA BHAUMIK: Yeah, initialize. Some initialized. CRAIG LABENZ: --of board. POOJA BHAUMIK: Yep. CRAIG LABENZ: Initialize. Generic. Smart. OK. Save the action. POOJA BHAUMIK: Oh, I think not-- I wouldn't want to call it initialize just because then it doesn't-- I'm just talking about code variable names. So maybe since we are initializing the board variable, I would say no. Then it's confusing because we have a game object as well. You got it? CRAIG LABENZ: Yeah. What are we initializing right now? POOJA BHAUMIK: Board, board. CRAIG LABENZ: The board. POOJA BHAUMIK: Board, yes. CRAIG LABENZ: OK. Save. POOJA BHAUMIK: So oh, wait. CRAIG LABENZ: Oh, and I have to write it. POOJA BHAUMIK: You can. Wait, wait, wait, wait, wait. CRAIG LABENZ: Get it wrong? [CHUCKLING] POOJA BHAUMIK: You can do that, but so-- CRAIG LABENZ: No. POOJA BHAUMIK: So here you see the green thing on the right? On the right, there's a green code on the bottom, on the bottom of the action settings. CRAIG LABENZ: Here? POOJA BHAUMIK: Yeah. Yeah. So this just creates the-- yeah. You click on it. And it just creates the boilerplate code for you with the written items and whatever you want. So you can copy to Editor. Yeah. CRAIG LABENZ: Pretty good. Pretty good. POOJA BHAUMIK: And yeah. Now, we don't have any arguments. We don't have any pub.dev that we want to add. Yeah. We don't have any written value. We're just initializing it. So what we can do is that we can initialize the board with a list of grid items that has the label as empty string. And now, your question is, how do I access the app state? I'm hoping. CRAIG LABENZ: Yes. And also, which of these imports brings in the data structures that we defined? POOJA BHAUMIK: I think it will be in your utils, I guess. So it is already imported. So if you just type FFAppState, it's basically called FFAppState-- all the app states. So all capital. Yeah. Yeah. And basically, you can just add the brackets. And then you can find the game board object. No, no. CRAIG LABENZ: Brackets like this? POOJA BHAUMIK: No, no, no, no. Just a normal constructor. CRAIG LABENZ: Just make a new one? POOJA BHAUMIK: You don't need to do that. You can just set it up. So right now, it's basically in your app state file where this is an FFAppState class. And you're just accessing the variables in it. CRAIG LABENZ: Oh. So there's already been an object of this initialized. POOJA BHAUMIK: Yes, yes, yes. So you do FFAppState bracket dot game mode. So bracket-- CRAIG LABENZ: But you didn't want these brackets. POOJA BHAUMIK: Round brackets, round brackets. The constructor brackets. Yeah. CRAIG LABENZ: I guess I'm a little confused. I thought we weren't initializing one? POOJA BHAUMIK: So you just have the board. You access it. And now, you can basically set it with any value. So yeah, if you just do a list of generate nine items with-- initialize the values. So maybe we can do a list or generate. CRAIG LABENZ: Now, we had board.items, right? So is this what we want to set? Or do we need to create a board as well? Is there a way to see-- POOJA BHAUMIK: It's already set with-- CRAIG LABENZ: --the code that already has been run? POOJA BHAUMIK: Not in the code tool that I showed you. So in the code tool, you will only be able to see the screens and stuff, the components and stuff. But when you download the code, you can find the entire code in your local ID or whatever in a way. So at this point, I don't know if you want to download it, but you can set it up with a list. CRAIG LABENZ: Maybe. I think I do because I'm a little-- this code doesn't look very familiar to me. This looks like I'm creating something. Well, first of all, what is the goal of this method? Do I return an object? POOJA BHAUMIK: No, you don't return anything. CRAIG LABENZ: You don't return. POOJA BHAUMIK: You just set the variable that's already initialized. And you just add a new value to it. So this is initializing the board with a new value. CRAIG LABENZ: So does this code here-- is this a dot call function that returns a single instance or something? POOJA BHAUMIK: Yeah, it will return a single instance. Yes. So basically, it will have a getter and setter in this app state class. And you're just basically now calling the getter and setter. CRAIG LABENZ: All right. So you talk. POOJA BHAUMIK: Oh, you can just use a list.generate. And for each grid item, you can have label as empty. Yeah. So 9. And then you have the index. Yes. And here you're going to return grid item. CRAIG LABENZ: Yeah. POOJA BHAUMIK: Grid item. CRAIG LABENZ: OK. POOJA BHAUMIK: So now, it's called struct here. Whatever is your variable name, we add the struct in your classes. So grid item struct. And it will have a label. Yeah, label could be empty. Empty string. CRAIG LABENZ: Oh, yeah. We could do null or empty string. Yeah. POOJA BHAUMIK: Let's not do null. Yes. CRAIG LABENZ: All right, so we're going to do that. And then this will say this is now items. POOJA BHAUMIK: Yeah. OK. CRAIG LABENZ: OK. POOJA BHAUMIK: OK. now items could be set to board. Yep. All right. CRAIG LABENZ: OK. This-- POOJA BHAUMIK: Please do not forget to save it, though. Do not forget to save it. Yeah, it will go away. CRAIG LABENZ: Because we fought hard to get to write this. Now, this line of code here-- line 15-- this seems unusual, right? I don't write code like this, which isn't to say it's bad, but it does make me want to see at least this class definition. I think that would really be helpful. And you're saying for that, we'd have to download all the code? POOJA BHAUMIK: Yeah, you have to download the code. The entire project, the Flutter project-- you cannot access it on the UI, the entire thing. This particular class will not be in that. Yeah. So yeah, you saved it. Thank you. I thought you were going to forget saving and move because you have to write that again. Awesome. CRAIG LABENZ: So when you said this particular class won't be in there, it's the FFAppState class. And that won't be where? POOJA BHAUMIK: So if you open that code thing at the top right, I'll show you what are the classes that is there. So if you see view code, if you see, it's only the pages-- the pubspec, the Firestore indexes. And if you write any components, it will only be shown in the UI here, but when you download the project, the entire Flutter project will be available for you to check any of the generated code and stuff like that. CRAIG LABENZ: OK. Can we do that real quick? POOJA BHAUMIK: You can download it and then access it. Yeah. It will take to a minute or something. So yeah, it's the same developer menu. And there you see your download code-- the third one. Yeah. CRAIG LABENZ: Great. This is going to be super interesting. And it's already almost done. And it's in my Downloads folder. OK. So tic-tac-flutterflow. I'm going to drag this into VS Code. Here we are. OK. POOJA BHAUMIK: So if you search in your-- if you just open that initialize board, you can just backtrack to the-- yeah. CRAIG LABENZ: OK. I guess we have to run pubget. This is also answering another question I know people have, which is just, does FlutterFlow have vendor lock-in? Sure doesn't look like it. POOJA BHAUMIK: You can run with it and never look back. CRAIG LABENZ: Yeah. Not having vendor lock-in be part of a company's strategy is always nice. POOJA BHAUMIK: So you're in the latest version. Can you just go back to pubspec and make this to 0.18.1? CRAIG LABENZ: What is it? Localizations? POOJA BHAUMIK: AIN. Yeah. CRAIG LABENZ: OK, so what is the deal here? POOJA BHAUMIK: You have to set the internationalization package, INTL. CRAIG LABENZ: Ah, there we go. I see. POOJA BHAUMIK: 2, 1. Yeah. CRAIG LABENZ: Oh, and it's running Flutter pubget already. So now, I'm just clobbering it with itself, but it worked. OK. Here we go. I'm so excited to see this class. A-ha. Yes. The inner instance. And then the call method or something? That's going to return it. OK, here's a setter that we saw. That's wonderful. Board, same thing. You love to see it. Bunch of other stuff. Oh, so there's probably a constructor that returns the instance, I'm guessing. Oh, yeah, of course. Right here. POOJA BHAUMIK: It's right there, yeah. CRAIG LABENZ: Yeah, yeah. Ah. OK, this is what I was guessing would be the case when we wrote this code, but I just wanted to see it to feel better. POOJA BHAUMIK: Yeah, makes sense. Of course. CRAIG LABENZ: OK, minimize. POOJA BHAUMIK: All right. CRAIG LABENZ: We're back. POOJA BHAUMIK: So we have initialized nine items. Great. It saved. Let's go back to our homepage, our tree. CRAIG LABENZ: OK. Tree. POOJA BHAUMIK: The second one. CRAIG LABENZ: Homepage. Yeah. POOJA BHAUMIK: Yeah. Now, we want to maybe initialize this on init state of a homepage. So if you can just click on Homepage-- and now, we are going to do some actions. So if you see the second tab after Properties on the right-- so the mouse cursor thing. Yes. So this is the actions, which means that any actions, like navigation, opening a bottom sheet, Firestore calls, whatever it is, we call it an action-- so any user-triggered action. So now, you can open the Action Flow Editor. I want to show you the bigger picture here. So here you will imagine that when you have a lot of actions, there's going to be conditionals and loop and stuff like that. This will show it up in a flow chart format thing. So it's easier to understand what's happening. So let's create our first action, which is going to be that initialize board. So you can just search for that action. Initialize board. And is it there? CRAIG LABENZ: All right. POOJA BHAUMIK: And so we do want to setState right after this because the UI has to change. So the hack that we use is that you create another action after initialize board and just update app state. There is no setState like that, but you do update app state. CRAIG LABENZ: OK. POOJA BHAUMIK: Yeah. And just keep it blank. It's a blank setState. CRAIG LABENZ: Right, right. POOJA BHAUMIK: So if you ever have to update your app state later, then you can add your fields, but right now, we are, I think, not adding anything as such. This could have done in a different manner as well, where you could have got a return value from initialize board and then updated the app state as well, but here we did it from code directly. CRAIG LABENZ: Got it, yep, yep. POOJA BHAUMIK: So now, it's done. We can close this. CRAIG LABENZ: I'm guessing we need to go back to the grid view and actually use the real stuff. POOJA BHAUMIK: Yeah. So now, the value has to change. Yeah, the third one. CRAIG LABENZ: All right, don't tell me again. POOJA BHAUMIK: It's right there. It's right there. You're there. You're there. CRAIG LABENZ: Oh, we're on it. POOJA BHAUMIK: Yeah. So you see the value? Now, yes. It was the last opened one. So remove, no randomization. If you see the pencil icon, just click on the pencil icon. It will edit the variable. You go back to that same thing. Now, we have the-- CRAIG LABENZ: App state. POOJA BHAUMIK: Yep, it's the board. And data structure field. CRAIG LABENZ: Yeah. So I don't know what this means. POOJA BHAUMIK: It's just the fields in your-- so if your board is like a class, and they will have some fields, right? So now, you want to access this. So if you click on that-- CRAIG LABENZ: Oh, I guess I do because I want the items. POOJA BHAUMIK: Yep. So you got the items. CRAIG LABENZ: Very nice. POOJA BHAUMIK: It's done. Confirm. CRAIG LABENZ: Right. There we go. POOJA BHAUMIK: Confirm. And then don't forget to confirm. [CHUCKLING] CRAIG LABENZ: This isn't the first time you've walked someone through this. OK, so now we-- POOJA BHAUMIK: I do that a lot. That's fine. CRAIG LABENZ: I see. Because we're still not using that-- POOJA BHAUMIK: We are. CRAIG LABENZ: Are we? Oh, because-- POOJA BHAUMIK: Let's see. Just open it and see if it's pointing to the right data-- the item item thing. Just click on that. Yeah. So we need to get the data structure field. Now, yeah-- and the label. CRAIG LABENZ: Oh, there we go. Right, right, right, right. There we go. Nice. POOJA BHAUMIK: Awesome. CRAIG LABENZ: Woo-hoo. POOJA BHAUMIK: We have to instant reload again. CRAIG LABENZ: Let's check it out. POOJA BHAUMIK: And if you want to do a shortcut of instant reload, I think it's Command Shift I on-- when you're in the dashboard, you can just press Command Shift I. And it will recall this instant reload here. CRAIG LABENZ: Oh, from the other tab. POOJA BHAUMIK: Yeah, yeah, but you have to switch tabs to see it, but yeah. CRAIG LABENZ: OK. Oh, yeah, OK. That's a two-hand shortcut, but it is nice that it exists. So this is great and what we expected. POOJA BHAUMIK: All right. So we did the structure here. Now, I guess we have not a lot of time. And I want to get to the animation part and the updating to X and O part. What are you-- CRAIG LABENZ: Yeah, yeah, yeah, absolutely. Let's add quick another action, however we make this clickable, and then hook that into a function. Let's do that. And then I don't know how you animate a tic-tac-toe game, but if you have something up your sleeve, let's do that. POOJA BHAUMIK: Yeah. I guess we can do first on-tap for each grid item. Now, a lot of you might have the question that we want to write reusable code. So for that, we call something called components because this container text could be a reusable code. You can convert this into another widget class kind of a thing. So do you want to do that-- CRAIG LABENZ: I do. POOJA BHAUMIK: --it's obviously going to add a little bit more work of adding component parameters and stuff like that, but do we have the time to do all that? Or do we want to just keep it in the tree itself? CRAIG LABENZ: I think we want to-- like I said, I'm even coming into this from a position of just deep uncertainty. Can I still do best practices? Can I write reusable code? So that's what we should show. Yeah. Let's do that. POOJA BHAUMIK: OK. So if you see the container, you can just do right-click on it-- the tree. And you convert it to component. Yeah. So now, we name it to maybe grid box or whatever it is. CRAIG LABENZ: Board. POOJA BHAUMIK: It won't be board because board is the entire thing. CRAIG LABENZ: Oh, yeah. POOJA BHAUMIK: It's each item. CRAIG LABENZ: So grid item. POOJA BHAUMIK: So grid box. CRAIG LABENZ: Grid box? POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK, grid component. POOJA BHAUMIK: All right. So now, if you see, this will create another widget for you, which is your usable one. So now, it is not attached to homepage. It can be used in any of the pages. Right? CRAIG LABENZ: Nice. POOJA BHAUMIK: So we want to do an on-tap. So if you see the container, if you click on the container and then see the Actions Flow Editor that I showed you earlier, nothing there. Yeah, just click on it. And on the right, if you see the actions for-- CRAIG LABENZ: Here, right? POOJA BHAUMIK: Yes. So now, you want to do some on-tap. So add an action. What are we doing on on-tap? So maybe set it to X for now. CRAIG LABENZ: So I'm trying to click it. And it's not-- POOJA BHAUMIK: If you see the circle, it's already on-tap. So if you want to see it in a bigger screen, just click on Open. The Action Flow Editor, Open. It's better to see it in a-- CRAIG LABENZ: Yeah. What would I do here? I don't even know, other than press Escape to make this go away. What else can I do? POOJA BHAUMIK: Oh, just click on Open. Just scroll up and click on Open. CRAIG LABENZ: OK. POOJA BHAUMIK: Yeah. So now, we want to update each item to X or-- CRAIG LABENZ: Oh, I see. Up here we've got on-tap, on-double-tap, on-long-press. I didn't notice that up here. POOJA BHAUMIK: Yeah, yeah. CRAIG LABENZ: Got it. All right. POOJA BHAUMIK: Right. CRAIG LABENZ: Select action is open, but we want to write some custom code, right? POOJA BHAUMIK: For on-tap, you don't have to because here we can access the grid box. There is no custom code required for here. You just search for update app state. CRAIG LABENZ: OK. POOJA BHAUMIK: OK. Now, add field. So now, you're going to-- yes. Click on that. And the board-- select the update type so it will be a set-- I think we are going to update a field. So it's not the entire board object that we're updating. Add field. Now, items. I think you need the index. CRAIG LABENZ: We want to update, right? POOJA BHAUMIK: Yeah, update item, add index, but you need that index value because now that this is an isolated component, the index has to come from the parent. CRAIG LABENZ: Mm-hmm. Yep. POOJA BHAUMIK: So you can just right now, go back to your component. And then yeah, you can do 0 as for now. Yes, yeah. Set value to-- CRAIG LABENZ: It just keeps going. Nice. All right. For now, we're just going to set it to X, but we're also going to have to have some global toggle, like it's X's turn. It's O's turn. POOJA BHAUMIK: Yep, yep, yep. CRAIG LABENZ: So we're going to set the value to-- POOJA BHAUMIK: So you can-- if you want to hardcode it, then you have to click on that Delete button for hardcoding. Just go back. Yeah, click on that. Now, you can just hardcode it. So make it X. Cool. Done. Done. CRAIG LABENZ: Nice. POOJA BHAUMIK: So at this point-- CRAIG LABENZ: Do I have to save here, or are we good? POOJA BHAUMIK: No, it's fine. Here this is going to rebuild the current component. CRAIG LABENZ: But we need to have the parameter, like you said. POOJA BHAUMIK: Yeah, yeah. If you want to test it, you can test if the click is working or we can just fix the logic first. And CRAIG LABENZ: It was Command Shift I or O? POOJA BHAUMIK: Command Shift I. CRAIG LABENZ: OK, I pressed that. And now, I return. POOJA BHAUMIK: It didn't work, probably because you were already having the Action Flow Editor open. So yeah. Yeah. Now try that. CRAIG LABENZ: Oh, there we go. POOJA BHAUMIK: Yep. CRAIG LABENZ: Nice. And love the Flutter blue colors across the top. Very nice. POOJA BHAUMIK: Click on it. It will open. Is it because you did-- click on the first one. Hmm. CRAIG LABENZ: It's not doing anything. POOJA BHAUMIK: Let's fix the logic first. Have the index sent from the parent. CRAIG LABENZ: OK, OK. POOJA BHAUMIK: So for that, we need to create the parameters first. So click on the grid box. CRAIG LABENZ: Yeah, it's on this somewhere. Oh, right. It was up here. Parameter. Great. POOJA BHAUMIK: Yeah. So here click on the pencil icon. Pencil icon on the top. CRAIG LABENZ: This one? POOJA BHAUMIK: No. The first one. CRAIG LABENZ: Up here. POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK. POOJA BHAUMIK: We have a parameter 1. Let's just send the entire object of grid item object. We don't need to send just-- an index. We need to send the grid item object, which could be the label and everything, and the index. So index would be integer grid. CRAIG LABENZ: There we go. Add another one. POOJA BHAUMIK: Yeah. Also, can you make that required index as required because we need the index for sure? CRAIG LABENZ: Oh, we sure do. All right. So this is going to be the whole grid? POOJA BHAUMIK: Yeah, the thing. So it should be the data type. I think scroll down. Yes, right there. And the grid type would be grid item. Grid item. Yep. CRAIG LABENZ: I see what you're going to do. Do we have to send-- if we're sending just the grid item and not the whole game board, do we need the index? POOJA BHAUMIK: We need index because index is not part of grid item. The index is part of-- CRAIG LABENZ: Aren't we just going to update the label on the grid item that we are past? POOJA BHAUMIK: No. Imagine this is completely isolated. You don't know what you're updating, right? Because you're updating the app state and giving it an index, this is just the UI component. This is just the widget. But we need to update the app state, which is a variable. And for that, we need to know which index we are at. CRAIG LABENZ: OK. I'm still suspicious that we won't need to know based on the data type that we set up, or at least how I remember it, but we'll find out. We've got both of them for now. POOJA BHAUMIK: OK. We should send it from the homepage as well. CRAIG LABENZ: Yeah. POOJA BHAUMIK: You need to send it back from the homepage. So just click on that again, the tree thing. There is a UI change that you can do. In the widget tree, if you see the last second one-- yeah, click on that. Yeah. Now, it shows everything. CRAIG LABENZ: Oh. POOJA BHAUMIK: So homepage in the grid box. CRAIG LABENZ: Oh, interesting. OK. And it sees it has grid boxes here. Great. So now, we need to pass in a parameter. POOJA BHAUMIK: Yeah. So on the right, you can pass it-- on the right, if you see. CRAIG LABENZ: OK, I'm looking-- POOJA BHAUMIK: Literally on the end. Literally on the end-- in the end of the right. CRAIG LABENZ: Of this spot? Or all the way over here on the right? POOJA BHAUMIK: Yeah, yeah, yeah. Here just scroll to the end. CRAIG LABENZ: Oh, in Property. POOJA BHAUMIK: You're in the Property. Yep. CRAIG LABENZ: Yay. POOJA BHAUMIK: This is the component properties. CRAIG LABENZ: OK. POOJA BHAUMIK: And for index, you want to take it from the orange thing that you see-- the open the variable dialogue. CRAIG LABENZ: Right, right, right. POOJA BHAUMIK: And you have the item item thing. CRAIG LABENZ: This is my item. POOJA BHAUMIK: That will have the index. Yep. CRAIG LABENZ: Index [INAUDIBLE]. POOJA BHAUMIK: And confirm. Now, you have the item item itself, and no further changes. You don't have to-- it's the entire thing. CRAIG LABENZ: Got it. OK. POOJA BHAUMIK: All right. POOJA BHAUMIK: I still think I'm not going to need the index, but it's going to be so interesting to find out. POOJA BHAUMIK: Let's see. Let's see. I don't know what you're thinking or what approach you're thinking of. Maybe it could be another approach, but let's see. Let's add that index. CRAIG LABENZ: Oh, nice. OK. I see. I just noticed this here. All right. So we've wired those through. So now, I think we go back here to the grid box itself. And I'm going to try to remember how to do this. We want an action on it. Where was that? Don't answer. I'm thinking out loud. These are just the parameters. I don't want to click back to homepage. Oh, where was the click? How do I get back to that? POOJA BHAUMIK: No, no. CRAIG LABENZ: Oh it was on the container. POOJA BHAUMIK: Mm-hmm. CRAIG LABENZ: And is the container going to know about the things that we just wired through to the grid box, or do we have to wire them through again? POOJA BHAUMIK: I'm sorry. What's your question? CRAIG LABENZ: So the container is where the click event is. POOJA BHAUMIK: It is, yes. Yes. CRAIG LABENZ: Is it going to have-- are we all in the build method of the grid box here? POOJA BHAUMIK: Yes. This is the on-tap method. CRAIG LABENZ: Got it. So the parameters of the grid box are available. They're in-- POOJA BHAUMIK: Yes, it's available. Yes, yes, yes. CRAIG LABENZ: Wonderful. POOJA BHAUMIK: So what are you-- we're going to fix that logic. CRAIG LABENZ: So we were here. App state, board, update game board. POOJA BHAUMIK: You're right there. CRAIG LABENZ: Edit this. POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK. So update item at index. So this is what I'm wondering here. So we've got-- yeah, the items. So I think we can bring this back. We got past just the individual item, right? Update item at index. POOJA BHAUMIK: You've got to update an individual item. You're not passing to anyone. Are you passing anything? CRAIG LABENZ: Well, I thought we passed in the whole item. POOJA BHAUMIK: Yeah, you have the whole item because you want to set the label and stuff for your UI, but for updating the app state, that is basically generating the grid item list children. CRAIG LABENZ: Oh, I see what you mean. POOJA BHAUMIK: You got it? Yes. CRAIG LABENZ: Maybe. What POOJA BHAUMIK: Did you do? Did you change the-- CRAIG LABENZ: I didn't do anything because I think-- oh. POOJA BHAUMIK: Yeah. Change the 0 to the index. Yep. Awesome. So I just now-- CRAIG LABENZ: No, they still should have updated the 0 index before, but it didn't. POOJA BHAUMIK: Yeah, yeah. That's why I want to see what the logic in the X thing-- is it right or not? I want to see that. Can you open that update game board thing? Yeah, click on that. Update game board. Update grid item. CRAIG LABENZ: Yeah. So we're setting the value of the label to X. POOJA BHAUMIK: It should work. CRAIG LABENZ: And this is-- and we do have the empty setState wired up, right? POOJA BHAUMIK: Yeah. When you're doing app state-- OK. Maybe do rebuild. Rebuild the homepage. So instead of the update type, instead of rebuild current, no. Just keep it. Go back. Click, uh-huh, closer. The last thing update type-- just rebuild containing page. CRAIG LABENZ: OK. POOJA BHAUMIK: And let's try it. There is an error on the top. What is that? CRAIG LABENZ: No test mode to reload. Why did it say that to me? POOJA BHAUMIK: Because it's a 30-minute test mode. So there's an error on top. What's that? String property not set. Yeah. So you need to set the parameter 1 thing. I think that should be label-- CRAIG LABENZ: Oh, OK. All right. And that's in the-- POOJA BHAUMIK: Grid box. CRAIG LABENZ: --grid box. So OK. Now, I want to figure this out. Container, grid box, local component state variables? Do I do something here? POOJA BHAUMIK: Whatever you want to update, if it's on the UI, just click on the UI. What do you want to update? CRAIG LABENZ: Oh. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Oh, nice. OK. So then this. OK, wait. Yeah, yeah. OK, that opened the same thing. So now, component parameters. POOJA BHAUMIK: Yep. It's not index. It's the grid. CRAIG LABENZ: Grid. And then here it's a field. And the field is the label. And confirm. Default value is required. Can I just do an empty string? POOJA BHAUMIK: Yeah. CRAIG LABENZ: How do I type an empty string? POOJA BHAUMIK: You did. CRAIG LABENZ: Well, I typed a Space bar. [LAUGHTER] POOJA BHAUMIK: That's what I'm doing. CRAIG LABENZ: One space. Yeah, for our purposes, it is, but it is different than an empty string. POOJA BHAUMIK: I know, I know. Yes. CRAIG LABENZ: OK. POOJA BHAUMIK: OK. So you need to re-open that test mode because test modes are usually 30 minutes. So yeah, let it open. CRAIG LABENZ: Let's look at some questions here. POOJA BHAUMIK: All right. CRAIG LABENZ: There were some other ones. So we got that. We've gotten that one now. We've also asked this one about vendor lock-in. So can custom widgets have children? I think we're seeing here a strong yes. So we've made this custom grid box, and it has children. POOJA BHAUMIK: Custom widgets-- technically, you cannot pass a widget right now. So basically, your custom widget is a separate component, but you're saying that the stateless widget has a parameter as a widget. No, that is not possible right now. If you want to have a parameter as a widget and pass it, that's not possible, but if you want to structure it in your UI tree, that's possible. CRAIG LABENZ: So a totally dynamic child parameter where that parameter is another custom widget-- that isn't a thing yet? POOJA BHAUMIK: Not yet, yeah. CRAIG LABENZ: Got it. OK. Let me see what other questions we've got. Aditya says, amazing. FlutterFlow is really making it easy to ship apps. And you are seeing me in the steepest part of the learning curve. I have never clicked around these menus before. POOJA BHAUMIK: I think that's where the most of the hard work is there-- to understand the tool because it's like switching from Android Studio to VS Code. It's completely different UI and completely different shortcuts. So yeah, it takes time in the first day or three. CRAIG LABENZ: Mm-hmm, mm-hmm. Someone's excited. It looks like a lot of progress has been made since the last time they were here. This is an interesting one. Obviously, we're not going to have time on this stream. Can you design a simple login page? Just thinking about this, there are a lot in the template, but are there video resources for this kind of task? POOJA BHAUMIK: Why is it not working? Why is it not working? [CHUCKLING] CRAIG LABENZ: Wait. POOJA BHAUMIK: Whoa. CRAIG LABENZ: Whoa. Whoa. POOJA BHAUMIK: Whoa. Wait. Why is it-- is it a lag that you see here? CRAIG LABENZ: Maybe. POOJA BHAUMIK: So it's working with a lag? CRAIG LABENZ: I don't know why we just got so many. POOJA BHAUMIK: You clicked on all these boxes, but it only updated-- I feel like if you just do another instant reload in case the-- sometimes, the generated code might not be exactly-- hmm. CRAIG LABENZ: I think it's just there's some kind of update lag, for sure. POOJA BHAUMIK: Huh. Just click on the outside-- outside the grid. Outside the grid, but inside the screen. CRAIG LABENZ: Oh, in the UI. Yeah. POOJA BHAUMIK: Yeah. See, some state is getting updated, but late. CRAIG LABENZ: Interesting. Yeah. That is when it happens. All right. We're going to quickly test that it's exactly that. Bottom middle, tap, bottom of the UI, tap. 100%. That's what it is. OK, Pooja, the FlutterFlow debugging experience-- POOJA BHAUMIK: Yeah. CRAIG LABENZ: Well, I did it wrong again. I clicked the wrong thing. Yeah. It would be nice to be able to go back to that session from here more easily. Wait. Will it if I just press Back? Answer-- no. POOJA BHAUMIK: No. It's gone. It's not gone, but you click on that thing again and you can-- yeah. CRAIG LABENZ: Pretty gone. [LAUGHS] OK. So how are we going to troubleshoot this? POOJA BHAUMIK: OK. So what if-- ah. OK. So what if the label that you're passing to the component parameter is not getting updated or something? Because we are passing the label from home page to the child-- so if you go to your homepage-- CRAIG LABENZ: Yeah, yeah. Also, why clicking elsewhere would have a global setState is another question. POOJA BHAUMIK: I have to see the code for that-- what's happening in the-- yeah. CRAIG LABENZ: Yeah, same. It feels like one of the early-- if you're early in FlutterFlow, I think it would be a common operation to just download all the code and see what the heck what you've built is doing. And then hopefully, I imagine really experienced FlutterFlow developers have done that enough times that the mystery is gone at this point. And you just immediately know where in the UI to go. And once you get to that point, FlutterFlow really speeds you up. Right now, obviously, it doesn't feel like we're being sped up. POOJA BHAUMIK: Yeah. In a way, I usually connect it to GitHub, sometimes, and just pull the code. If I want to debug something, then I pull the code in my ID from the GitHub account, from the GitHub project. And yeah, but that's if you want to keep on doing it kind of a thing. So I want to see what's-- so if you have the grid item here, just click on the item item thing on the UI. Click on the item item thing. CRAIG LABENZ: OK, yep. Trying to. POOJA BHAUMIK: So that's a grid thing. So in the component properties, if you scroll down, we are sending the item completely. CRAIG LABENZ: Mm-hmm. POOJA BHAUMIK: Mm-hmm. And update page on changes. Can you do that? The component properties-- update page on changes. So if there isn't any change on the grid-- yeah. Maybe that's the problem. Yes. So basically, if there is any change in the data type-- CRAIG LABENZ: It feels very promising. Boolean is wondering if we should send a key. And it's a good question. I think we'll be OK. Oh, no. It still didn't do it. Interesting. And when I clicked-- POOJA BHAUMIK: Oh. Did you do the Command Shift I, or did you just do an instant reload? Because it might not have the completely new generated code right in the next second. CRAIG LABENZ: Got it. Got it. I don't know what I did. I think I clicked the lightning again. And then that did something. And something happened. And then there was something. Now, here we are. As you can see, I have a very precise understanding of what happened. Oh. And the bottom click isn't good. All right. The debugging continues. That was a good guess. I was quite optimistic it would work. Didn't. POOJA BHAUMIK: OK. I want to check-- CRAIG LABENZ: I love Fré's spirit here. Add a fake button under the grid. Make it a feature. [LAUGHTER] Nice. That's good. POOJA BHAUMIK: OK. Let's see. Just to check, in grid box, on initialization, if you go back to the grid box properties-- CRAIG LABENZ: Properties. OK. POOJA BHAUMIK: On the left, on the tree, you will click on grid box on the pages thing. CRAIG LABENZ: Oh, that's right. Yep. POOJA BHAUMIK: Yeah. And then click on the grid box instead of the container. CRAIG LABENZ: Grid box. POOJA BHAUMIK: Yeah. So yeah. So I want to see if, on initialization, which is click on the mouse thing, cursor thing-- yeah, the actions. Basically, the actions on the right. CRAIG LABENZ: Oh, right here. Yeah. POOJA BHAUMIK: Yeah. So if you open this, this will basically do everything on initialization of that component. So if I add that action here instead or update app state-- CRAIG LABENZ: Which I type in here-- update app state. POOJA BHAUMIK: Oh. I feel like, OK, this is maybe something that I've done in Flutter as well. If I update just the field, it doesn't update. I'm guessing the problem is that if I update the item itself with new value, instead of just updating the internal field, the label thing-- so just go back. Maybe just go back. Let me try this. I think this is something that I've done in Flutter code also. And sometimes, it doesn't update. So go back to the Actions. CRAIG LABENZ: One thing I do want to do real quick just for our debugging purposes-- I don't know if we're going to get to animations. I'm going to download this code. POOJA BHAUMIK: You can view the code. This part will come in the view code as well. CRAIG LABENZ: Oh. POOJA BHAUMIK: The on-tap. CRAIG LABENZ: OK. Then maybe what I was going to do wasn't needed. Yeah. This is what we want. So we've got coming in the grid item and the index. Is there a way to make these not nullable, by the way? POOJA BHAUMIK: No, this is coming from our own generated code. So this is how we-- so basically, when you do required, so if you just do toggle between required or not required, I think that, you can change. CRAIG LABENZ: Got it. OK. So I just made the things not that well. So that's fine. POOJA BHAUMIK: On-tap. CRAIG LABENZ: On-tap. Here we go. So model, update page. POOJA BHAUMIK: Yeah. That dot dot label dot X-- dot dot label equal to x-- I think that's not updating. What if we just update the entire E widget index? CRAIG LABENZ: Widget index. Wait. POOJA BHAUMIK: Yeah, line number 67. CRAIG LABENZ: Yeah, yeah. How do you want this to change? POOJA BHAUMIK: So basically, the widget index that you have, the current object that you're trying to update-- instead of updating the field inside it updating the entire thing with a new data type, which is grid item struct and label equal to X-- CRAIG LABENZ: Yeah. And so this is why I think we didn't need the index is because shouldn't we just be updating grid dot-- oh, wait. No. We passed the grid. POOJA BHAUMIK: We don't-- CRAIG LABENZ: I thought we passed the grid.items, that thing. OK, you're totally right. We always needed the index. POOJA BHAUMIK: Yes. CRAIG LABENZ: Now, where do we display this? A widget.grid.label. That feels correct. That's what I would expect to see there. And yeah. This feels like this should work unless there's something funny happening in this structural code. POOJA BHAUMIK: I think let's remove the label thing. I'm right now 99% sure that that is the problem, that the internal label updation is not actually doing a UI update. Because when we say update page on changes, I don't know if it's getting to know that an internal field-- that label has changed. So let's try it. Let's see if that works because I am right now 99% sure. CRAIG LABENZ: OK. So you want me to type what? POOJA BHAUMIK: You can't type anything here. You have to change it in the UI. CRAIG LABENZ: OK. So we're going to-- POOJA BHAUMIK: Yeah, go to on-tap. On-tap. Go to update game board thing. CRAIG LABENZ: OK, edit. POOJA BHAUMIK: Yeah. CRAIG LABENZ: All right. POOJA BHAUMIK: Instead of the update field, set value. Update field in the last one that you see after index, not this one. CRAIG LABENZ: Oh, set value. POOJA BHAUMIK: No, no, no, no, no, no, no, no, no. You're setting the entire game board. You're setting the entire game board that way. You're just setting-- update item at index. That's fine. Yes. Give the index again. CRAIG LABENZ: Which I get from this. OK. POOJA BHAUMIK: Yes. And here set value instead of update field. Set value. CRAIG LABENZ: Oh. POOJA BHAUMIK: All right. So now, just create data type, the first thing that you see. CRAIG LABENZ: OK. POOJA BHAUMIK: Create data type. Grid item. Add field. Label and X. CRAIG LABENZ: OK. POOJA BHAUMIK: OK. CRAIG LABENZ: Now I want to view the code again. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Let's see what this does. OK. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Man, this feels like it's going to-- this should be the same. POOJA BHAUMIK: No. I actually remember this kind of an error, where the internal field being changed does not notify that there is a change in the data. CRAIG LABENZ: Got it. POOJA BHAUMIK: I have had this issue in traditional Flutter code. So instant, reload, yeah. All right. Can you reload again? Can you reload again? CRAIG LABENZ: I can, and I will. POOJA BHAUMIK: All right. This should work. Why? What is happening here? CRAIG LABENZ: Yeah, I feel like the change we just made-- if we look at the code-- view code. This feels like it should do the same thing. Before, we were setting the label field on whatever was at this index. And now, we're just setting a new object at the index with the label field set. This doesn't feel like it should have changed anything. It seems like this update page should be doing it. And I think that's where the issue is. POOJA BHAUMIK: Yeah. Update page just saying that it's not updating it? The page? CRAIG LABENZ: Yeah. Oh, this is also interesting. Look at this. POOJA BHAUMIK: Yeah, it's watching for the apps. Yeah, this is where we're using the provider for watching for the app states. CRAIG LABENZ: So this is what should be triggering the rebuild, right? POOJA BHAUMIK: Yeah, it should be. CRAIG LABENZ: So update page-- this actually isn't calling setState or anything. What does update page do? POOJA BHAUMIK: For that you have to check the entire code. Yeah. CRAIG LABENZ: Which I probably still have downloaded, but we didn't have any of this stuff in there. Oh, I did just download it. Let me make this change. I'll be like Michael Jackson. Make that change. OK. So yeah, yeah, yeah, yeah, nice. Pubget. Flutter pubget. I should really alias pub to Flutter pub. Does anyone else do that? Is that a thing that people do? POOJA BHAUMIK: What? CRAIG LABENZ: Alias pub to be Flutter pub. POOJA BHAUMIK: Hmm. CRAIG LABENZ: Why isn't this working? Fré says, I think it still renders the parameter it was passed in, which might still be the same. It certainly feels like it's the same. The update happens in the component, so the parameters don't update. First of all, this is the exact direction that we need to-- POOJA BHAUMIK: We are doing an update page, not an update component-- not just an update component. We are doing an update page, which should update the components as well. CRAIG LABENZ: All right, let's see. What does this do? So it calls the callback. And then we update callback. And that says-- so it creates a lambda on update change. First of all, is this nothing or empty string? OK. So update on change is false. So are on-update is nothing. And it isn't-- oh, wait. This is also nothing. We are definitely not escaping the do nothing state with this code. Right? POOJA BHAUMIK: This is update callback, right? So if update on change is true, then we do update callback. I think that's getting passed, right? CRAIG LABENZ: So on update is update on change or update callback, but I just feel like everything is an empty lambda. There's no place where we-- POOJA BHAUMIK: Do you want to try a break point here? And if we have that, we can do that. CRAIG LABENZ: Yeah. I could just run Flutter run here. POOJA BHAUMIK: Yes, you can. CRAIG LABENZ: So I will do that. What are all these problems? Just import things. That's fine. So yeah, let's add one here, I guess. Right? Oh, no. I see. Oh, I see, I see. I'm sorry. OK. I was actually slightly misunderstanding the code here. For some reason, I was thinking this was all in one function. POOJA BHAUMIK: No. CRAIG LABENZ: But this is actually just a function that returns a function. No, it doesn't return a function. It calls a function. That's interesting. Update change. Then we call the function. I see. This could also just be null, but it's just defining a thing. OK, that's fine. So now, I want to go back. I've lost the plot. So in update page, that's here. So we call the callback. The callback that we gave it is update board struct. POOJA BHAUMIK: Yeah. CRAIG LABENZ: And update item. So I presume these things are going to update this app state correctly. Then we call this update callback method. And update callback-- this is where I was confused. I was thinking all this stuff was together, but no. Update callback is, in fact, nothing. Right? And then there's this set on update method. So if set on update has-- oh, update callback equals on update. Update callback. So this can be set. So the question is actually, have we called set on update? POOJA BHAUMIK: Can you see the caller-- what the grid box is calling? Can you see? Can you go back to grid box widget? So it's calling-- CRAIG LABENZ: No, I do see somewhere a set on update. So I want to check this real quick. It's the only other place it's called in the FlutterFlow model. So what is this code? Wrap with model. POOJA BHAUMIK: So I feel that we did a current page update instead of the current component update. If it were a current component update, it would have done a setState here on top, right? But if your current page update-- I don't think the page has any changes happening. The changes are happening on the component, right? CRAIG LABENZ: OK. POOJA BHAUMIK: So maybe-- CRAIG LABENZ: I agree we have not told it to update correctly. POOJA BHAUMIK: So maybe we do correct the component, rebuild current component. CRAIG LABENZ: But I think that's what it was before. POOJA BHAUMIK: But we did not change the list view thing. As I said, the list-- if it's an internal field, it does not notify that there is a change in the list. CRAIG LABENZ: OK. Oh. Before-- I think I'm following you, finally. POOJA BHAUMIK: Oh, my God. CRAIG LABENZ: Not here. POOJA BHAUMIK: Yeah. CRAIG LABENZ: So this is old code. Update item. POOJA BHAUMIK: This is old code. Oh, this is old code. CRAIG LABENZ: This is old code. The new code is-- POOJA BHAUMIK: Now, that should have a setState now. If you just go back to view code. So scroll down. CRAIG LABENZ: Where is that? POOJA BHAUMIK: On top. Right there. Yeah. So if you see setState, now I think that should fix it because now, it's updating the current component. CRAIG LABENZ: SetState. Yeah. OK. Interesting. Then that wasn't there before, right? If we go back here-- yeah. There is no setState. And what made the setState appear? Just having it be current component? POOJA BHAUMIK: Yeah, yeah. CRAIG LABENZ: Interesting. All right. Instant reload. Here we go. POOJA BHAUMIK: All right. I'm scared now. CRAIG LABENZ: Big money, big money, big money, big money, big money. POOJA BHAUMIK: What? CRAIG LABENZ: Oh, that's a thing gamblers say before they're about to roll dice and either lose their grocery money or win next week's grocery money. POOJA BHAUMIK: Oh. CRAIG LABENZ: Big money, big money, big money. And then they roll. And oh, no. Bad news, Pooja. Also, the bottom click only works once. POOJA BHAUMIK: That's probably calling some-- that's unlikely. Oh, wait a second. OK. OK. Let's try one more thing. Go back to your on-tap again. CRAIG LABENZ: So also, setState, right? This isn't actually model state, right? The setState won't do anything because we're pulling this in from the grid. Or sorry, we're pulling this in from widget dot. We're not pulling it in from a parameter, like an attribute on-- POOJA BHAUMIK: Yeah, but you have the update page on changes. If there is any changes, it will update all the elements of the home page. CRAIG LABENZ: Right. Yeah. I agree something like that should work. I'm just realizing right now, this setState actually won't do anything because what we're rendering isn't from the state object, right? SetState doesn't-- POOJA BHAUMIK: Yeah, got it, got it. I get it. CRAIG LABENZ: --to the top of the app and drill back down. Yeah. All right. Anyway, so-- POOJA BHAUMIK: Yes. CRAIG LABENZ: --what are you thinking we do next? POOJA BHAUMIK: OK. Go back to on-tap. CRAIG LABENZ: Back to on-tap. POOJA BHAUMIK: Yeah, just click on that. Yes. Open the on-tap for that thing. CRAIG LABENZ: Open. OK, I'm trying to remember how to do that. Are we in grid box or container? POOJA BHAUMIK: Container. CRAIG LABENZ: Container. POOJA BHAUMIK: So in select update type, select update type, update field that you think that you saw the-- no, no. CRAIG LABENZ: This one? POOJA BHAUMIK: Where you see update fields-- yeah. Click on that drop-down. CRAIG LABENZ: Mm-hmm. POOJA BHAUMIK: Should this be-- no, it should not be. Should it be set? No, it should not be set value. It should be a update field itself. CRAIG LABENZ: So the difference here-- POOJA BHAUMIK: Yeah. CRAIG LABENZ: So set value was when we did this. It generated this code. When we had-- no, that's update fields. Set value-- no. Maybe not because we have update fields right now. And what it does is-- POOJA BHAUMIK: I think I know what the problem is. It's probably because the update notification only comes when your object has a change. And in this case, we have a board object. Inside there is a list of grid items. And then inside of it, we are changing-- board did not change. CRAIG LABENZ: Board didn't change. POOJA BHAUMIK: Board didn't change. CRAIG LABENZ: Right. POOJA BHAUMIK: Maybe we don't need-- but yeah, we will have to change the data structure in a way that if we just have a list of grid item as our app state, then any changes that happen in each item is going to give a notification to provider. So what we right now have is that inside items, we have a change. So it's not giving a notification. Do you want to update the app state? CRAIG LABENZ: I do want to make it work. So I think my answer is yes. POOJA BHAUMIK: Yeah. So let's go to app state. CRAIG LABENZ: App state. All right. POOJA BHAUMIK: Yes. In this case, delete the board. Or don't delete it. Just keep the app state, a new app state. And we'll just release the-- CRAIG LABENZ: New app state variable? POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK. POOJA BHAUMIK: Yeah, yeah. CRAIG LABENZ: What are you imagining-- POOJA BHAUMIK: Rename board 2? I don't know. CRAIG LABENZ: Nice. OK. POOJA BHAUMIK: And this should just be a list of data. This should just be a list of data type. CRAIG LABENZ: OK. Isn't this is what we did before? POOJA BHAUMIK: No. We had internal items. We had a data object of a game board, which had a list of items inside. And that had a list of grid item. If I'm confusing you, I'm sorry. CRAIG LABENZ: Remembering correctly. OK. POOJA BHAUMIK: Yeah, this is fine. CRAIG LABENZ: Oh, I see. I think I see. POOJA BHAUMIK: Do you see what the problem could have been? CRAIG LABENZ: I do see what the problem could have been. POOJA BHAUMIK: Yeah. CRAIG LABENZ: So now, we're going to go back to the widget tree. POOJA BHAUMIK: Yeah. Now, one thing is that now you have the-- CRAIG LABENZ: Go to the initial, right? POOJA BHAUMIK: Yeah. We have to find out where the errors are. So if we delete the board, it will automatically throw you errors. And we can just keep on replacing them-- what you want to do. CRAIG LABENZ: I want to do that. POOJA BHAUMIK: So delete the board and let the errors come up. CRAIG LABENZ: Goodbye. POOJA BHAUMIK: Bye. Sorry. So now, we have to-- CRAIG LABENZ: See ya. Wouldn't wanna be ya. All right. POOJA BHAUMIK: And awesome. CRAIG LABENZ: That's right here. So remove-- POOJA BHAUMIK: Remove-- CRAIG LABENZ: Yeah. POOJA BHAUMIK: Remove that. Add field. Board 2. Select update type. Now, we have update item at index. This is what I want. This is where it will give the notification that there is an update on the list. Index. CRAIG LABENZ: Got the index. We're going to update field. POOJA BHAUMIK: Set the value. I think just set the value and create the label with the new data type. Update data type. View grid item. Label. That's fine. X. CRAIG LABENZ: OK. POOJA BHAUMIK: Cool. Now, let's see the next error. Update type should be current component, or I don't know what it should be. It's currently [INAUDIBLE] component. Let's keep it at that for now. CRAIG LABENZ: Yeah. OK. POOJA BHAUMIK: Next error. CRAIG LABENZ: Generate children. POOJA BHAUMIK: OK. So app state-- just pencil icon. Board 2. And that's it. CRAIG LABENZ: OK. POOJA BHAUMIK: Confirm. Nice. OK. Now, the custom code-- the error might not come here. CRAIG LABENZ: Yeah, OK. Got it. I was going to say, I'm sure we have to change that. So this is initialization. So now, this is just board 2. And it's not items. It's just this, right? POOJA BHAUMIK: Yeah. CRAIG LABENZ: OK, cool. POOJA BHAUMIK: Save, save, save. CRAIG LABENZ: Brief red squiggles. POOJA BHAUMIK: Yeah. CRAIG LABENZ: Has this died? It has not, but it expires soon. Ah, we're on the clock. OK. Command Shift I. Oh, ran out of time. We've got to put in two more quarters. OK. POOJA BHAUMIK: Yeah. So that's a very common error that I've found even in any Flutter code as well is that the notification of list changes will not happen if it's an internal field change or something of that sort. So I have debugged this. I've had these kind of sessions a lot. Oh, thank you. CRAIG LABENZ: Yeah. We're doing our best dude over here. POOJA BHAUMIK: We are. I know a simple thing took a long time in this one because we were debugging it. So it was one of the days. CRAIG LABENZ: That happens. OK. It should take two to three minutes. Does anyone have a joke that they want to tell? Oh, this is a good one. Maybe folks have heard it before. Oh, wow. This is a really American-centric joke, I'm now realizing. POOJA BHAUMIK: OK. CRAIG LABENZ: Folks outside the United States, if you get this, then just I'm unbelievably impressed. If you don't get it, totally understandable. And now, here we go. What do you call a widget that lives in Washington, D.C.? To have any chance, there are a few things that you need to know about the construction of the United States. POOJA BHAUMIK: I have no idea, of course. CRAIG LABENZ: So there's 50 states in the United States. And Washington, D.C. is not one of them. POOJA BHAUMIK: Oh. Yes. CRAIG LABENZ: Same, same. Very close, Fré. A stateless widget because it's not in a state, but you're totally on the correct track there. Same error, Pooja. POOJA BHAUMIK: Do the instant reload again. CRAIG LABENZ: Instant reload. (SINGING) Here we go. Here we go again. POOJA BHAUMIK: That's so weird. CRAIG LABENZ: Well, we're doing something wrong. We also are nearly at time. So maybe we can end with-- I don't know what the answer to this question is, but where might one go if they were working solo in this exact spot, stuck in this way? Is there state update documentation? Are there videos that walk through this kind of thing? How would I self-serve and figure this out? POOJA BHAUMIK: I would just run it locally and breakpoints. If the changes are happening, and if the build method is getting called or not-- so I would just do a normal set of project debugging mode kind of a thing. So yeah, just to see what the flow of the code is. CRAIG LABENZ: Yeah. I think that makes sense. I wish we'd done this with a few more minutes because that would be a fun exercise. We've already deviated from this a little bit. I think we probably don't have time to dive into that here. Plus then we'd have to figure out-- you have to reverse those changes. Let's say we make the changes in the code ourselves. Then we'd have to come back and figure out-- what do we click here to replicate those changes in the generated code? But Pooja, we explored a lot. One thing that I think is-- so I'm just going to offer some of my thoughts here. You can either throw these in the garbage or take them back to the product theme or whatever. You mentioned that you've run into this problem before. So it does feel like maybe a little better visibility for developers into that update state, update the UI flow because right now, it does feel like we're in the dark. There's a control panel in front of us. And none of the buttons are labeled. Obviously, there's tons of labels here, but we weren't able to figure it out. So I think maybe some kind of more straightforward visibility into that inner loop would be quite helpful. The UI overall-- extremely strong. I think it's very clear that in terms of building a UI and then with the breakpoints and quickly testing on different screens-- this is awesome. This is very, very cool. Actually, I don't know why this is still-- I don't know why there's three here. I thought these containers were sized to be 100 pixels. Anyway, whatever. That's an aside. So in terms of UI building here, I feel like FlutterFlow has just such a strong offering. I think for especially experienced developers-- there were folks in the chat who were thinking, oh, man. The wall between me and the state management code feels really thick, feels very opaque. And obviously, maximum learning curve here, but we did bump into that a little bit. So that feels probably like maybe the highest priority area right now to lure in more code-first Flutter developers, but I love the idea of building UIs in the UI builder in this page. Yeah, making these reusable components. That feels very natural. So that part, I think, is really quite good. Any thoughts, Pooja, on what we got through? Maybe other things that you've built? Just anything top of mind for you before we sign off? POOJA BHAUMIK: Yeah. We did run into a problem, which took the time. I wanted to show more of the features and more of what we could have done in a way, but we are out of time. So I completely agree with the fact that sometimes, the debugging part in the visual format might not work that seamlessly because as I said, even for debugging, I think we still recommend doing how Flutter projects are being debugged. Of course, when you're running it on web, if there is some kind of console messages, they can still be shown on the test mode. So whatever console messages that you're probably looking for, that can be seen in the test mode itself. But yeah, those breakpoints stuff-- I think I still do that as what a traditional Flutter project would have done. So yes. At this point, visually, to debug that, it's not something that we are even focusing on at this point because we do love how the Flutter DevTools are used for debugging. Maybe we could make it easier someday to use DevTools directly from FlutterFlow, but yeah. Today, we just used the same traditional method. CRAIG LABENZ: Yeah, yeah. Yeah, it makes sense. The engineering task of making the full state management flow and the debugging around it as seamless as you've made the UI construction would be pretty enormous. But anyway, troubleshooting difficulties and all, I had a great time learning FlutterFlow, having this UI. Gosh, there's just so many things on the screen right now. I appreciated the hand-holding, the walkthrough from you, Pooja, to learn where some of these buttons are, and what do they mean? And what would I click on? And just those kinds of things-- I had a ton of fun kicking the tires in that regard. So thank you for coming on "Observable Flutter." I think I'm going to probably continue to play with FlutterFlow in the future and build some UIs with this. It seems pretty great. Folks, I think we are at time. This might be the longest "Observable Flutter" episode yet. Pooja, setting records everywhere you go. POOJA BHAUMIK: [LAUGHS] Oops. CRAIG LABENZ: Well, yeah, folks. Thanks for tuning in. Thanks for offering your debugging tips and your thoughts and everything. I appreciate that, as always. Gosh, I think we can call this one. Have a great week, everyone. And I will see you all next week. POOJA BHAUMIK: All right. See you, everyone.
Info
Channel: Flutter
Views: 18,194
Rating: undefined out of 5
Keywords:
Id: RF8ungZH0A4
Channel Id: undefined
Length: 118min 3sec (7083 seconds)
Published: Thu Oct 12 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.