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.