[MUSIC PLAYING] CRAIG LABENZ: Hey, everybody. Welcome back to
"The Boring Show." It's a low 60s episode. I think the episode title
will probably clarify that. But I'm here today
with Renuka Kelkar. Renuka, thank you for joining. RENUKA KELKAR: Thanks
for having me, Craig. CRAIG LABENZ: Yeah, absolutely. Renuka is both a Flutter
GDE and an organizer of the London Flutter GDG,
which is Google Developer Group, the first one
Google Developer Expert. And she's a freelancer and a
prolific mentor as well, right? RENUKA KELKAR: So
actually, I run the community called
Tech Power Girls, where we mentor the women who are
away from the job from five to 10 years, and we help them
to come back into the tech again with the help of Flutter. CRAIG LABENZ: Yeah. So often, I think you've said
it's moms who maybe they've been raising their
kids for a bit and now they're
ready to work again. That is just so amazing. That's so awesome. RENUKA KELKAR: Yeah. Thanks. Thanks. But it's a really
good initiative. We are getting
really good response. And now we are just expanding
this to the students from the rural
part of the India. So we are helping
them to learn Flutter with the local language. CRAIG LABENZ: Yes. RENUKA KELKAR: So that is now-- CRAIG LABENZ: Yeah, I-- it's such a thing. Learning programming is tricky. Learning it in a
second language is just an enormously bigger task. That's amazing. Tech Power Girls, if
you know anyone who would benefit from membership. Renuka, techpowergirls.com? RENUKA KELKAR: Dot net. CRAIG LABENZ: Dot net? RENUKA KELKAR: Yeah. CRAIG LABENZ: OK, dot net. All right, but today in "The
Boring Show," we're here-- you wanted to talk
about some web navigation and routing things? RENUKA KELKAR: Yeah. Basically, when we think about
the Flutter Web, so many-- if you have seen that
many people are talking on the Twitter that
oh, Navigator 2 we want to use for
the Flutter Web. So everyone is much
scared in that sense. And what is the
solution for that? How to be an easy way to do
the navigation in Flutter way? So that's how I started
exploring this, basically. CRAIG LABENZ: And you're
using GoRouter, right? RENUKA KELKAR: Yes. Yeah. CRAIG LABENZ: Yeah. So you mentioned
that a lot of folks find Navigator 2 a
little intimidating. The learning curve is
a little bit involved. RENUKA KELKAR: Yeah. CRAIG LABENZ: I
concur with that. And that's kind of
where GoRouter comes in because it allows
you to write a much simpler declaration of your
routing rules and whatnot. And then it expands
that and translates it into that arcane, a
little difficult Router 2 API, which exists,
basically, because routing is a complicated thing and you
have to have a lot of controls and dials if you want
to empower developers to realize all the different
routing scenarios in Flutter apps. But that's just-- if you all you
want to do is walk next door, you don't want or need an
entire plane's cockpit. And that's kind of how
the Router 2 feels. So GoRouter is that bridge. But you've even run into
a couple of limitations with Go Router you
were telling me. RENUKA KELKAR: Yes. Basically, if you go to
the Go Router package, you can see it's a favorite
package of the Flutter team. CRAIG LABENZ: It is. RENUKA KELKAR: Yeah. So I have just-- I thought that I can
try this and everything is going perfectly
fine when I was just doing the proper [INAUDIBLE]
navigation, first level kind of navigation. And then for the
nested navigation, I am getting some
problems, basically. So that is what I
was thinking to-- what is the solution
for that, basically. CRAIG LABENZ: Right. OK. And I think you said
you have a demo. And you've got the actual
Go Router example running. RENUKA KELKAR: Yeah. CRAIG LABENZ: Yeah. Do you want to pull that up? RENUKA KELKAR: Yeah. Just a minute. I will just go in. So this is the-- CRAIG LABENZ: Yeah, this is-- RENUKA KELKAR: The sample, yeah. CRAIG LABENZ: Right, what
comes with the Go-- or it's bundled into the
GoRouter package, right? RENUKA KELKAR: Yeah. CRAIG LABENZ: And
now can you walk us through a, how it works, and
then b, how you wish it worked. RENUKA KELKAR: So
right now, if you see that these are the tabs,
and if you click on the tabs, you can see there are the
sub-tabs inside that page, you can see here. And you can see the whatever
tab you are clicking, you can get it on the URL. Like this is a
family one, so F1. And if you click on
the Chris, and then you can see the people one
from that first family. And that's how it is working. But if I don't want
to use any tabs, I just want to use the pages,
how I can develop this? And if I want to have-- I want this top navigation
will be there all the time, how I can do this with the
pages, not with the taps tabs? Like a normal scenario,
when you do the Flutter way, we want something
like a sidebar, which is persistent all the time. CRAIG LABENZ: Right. RENUKA KELKAR: Yeah, then how
we can develop this, basically. CRAIG LABENZ: Yeah. RENUKA KELKAR:
[INAUDIBLE] scenario. CRAIG LABENZ: So that's
a great question, and luckily, I have done
precisely this in the past. RENUKA KELKAR: Oh, wow. CRAIG LABENZ: Yeah,
there was a sample app that I made with a few other
folks for a couple I/O talks. RENUKA KELKAR: Oh, wow. CRAIG LABENZ: And we had some
persistent-- it's in the Boring To Beautiful Codelab that was
released back in I/O in May. That has a persistent
sidebar, which I believe is what you're talking about. RENUKA KELKAR: Yep. CRAIG LABENZ: And
we used GoRouter. So I don't remember it
off the top of my head, or even much of it, but
I know it can be done. And I think we can
figure it out again. So that's the good news. Now to make sure I'm totally
understanding what you're saying, I think we
can just pretend that the tabs in this sample
are a persistent sidebar. RENUKA KELKAR: Yeah. CRAIG LABENZ: Obviously,
they're on the top, but we'll just pretend
it's a sidebar. And we want-- right now,
you're on the Sells tab. And when you click
the Addams tab-- no, that's not actually it. It's when you click a person-- RENUKA KELKAR: Yeah, if
you click on the person-- CRAIG LABENZ: You want
the tabs to persist? RENUKA KELKAR: Yeah, we want
to have a persistent tab. CRAIG LABENZ: And
never mind the fact that kind of wouldn't
make sense for this app because we're pretending
that it's sidebar navigation. RENUKA KELKAR: Sidebar
navigation, yeah. CRAIG LABENZ: OK. So we can make
this work, I think. So let's switch back
over to the code and look at what has created
the current behavior. Oh, nice. You've already got the
Go Router docks up. RENUKA KELKAR: Yeah. So this is the sample code. CRAIG LABENZ: OK, so
let's look at this. This is in main.dart
and we have-- is that-- what was it,
stateful or stateless? Stateless. And in the build method-- RENUKA KELKAR: We have
wrapped with this material app with docked router. CRAIG LABENZ: Yeah. And that's pretty much straight
out of the Go Router docs. So somewhere else,
there's this Go Router-- oh, that somewhere else is
right below it on line 20. So the Go Router
definition, you want to walk us through what's
in this definition? RENUKA KELKAR: So
here is a GoRouter. And we kept mentioning
routes here. So these are the routes,
like a first level of navigation kind of thing. You can give the path. In this example,
because here we are doing the nested
navigation, so that's why you are passing
the parameter kind of thing, what you
want, basically, in the URL. But this is how we can develop
like a first level GoRoute. And if you want a
nested, then you can have a route
inside the Go Route. So that's how you can
give the more deep linking in that sense. CRAIG LABENZ: Yeah. And so what you're saying here
is that because that routes parameter on line 32 is nested
under the GoRoute defined in line 26, you can visit
/family, some ID, /person, some ID, and that's
a page, right? And that's what we see when we
click on a person in the UI, right? RENUKA KELKAR: Yeah. CRAIG LABENZ: OK. So there are two primary ways
that I am aware of to do this. And the first one
would be perfect. But for an edge case,
it's a little painful. The second one would be perfect. But for an edge case,
that's not very painful. So I think we'll do the
second one because we don't want-- we want less pain. But we should talk about
the first one maybe first, just so anyone at home
who's maybe aware of it, you might not be aware
of this edge case. I certainly wasn't
until I bumped into it. So you have the GoRouter
docs open, right? RENUKA KELKAR: Yeah. CRAIG LABENZ: You want
to bring those back up. RENUKA KELKAR: So
if you go here-- CRAIG LABENZ: Now there's
something in here, it's like a navigator builder,
or navigation builder. There's some function. And I don't even know-- RENUKA KELKAR: So
here, the navigator-- CRAIG LABENZ: That's it. RENUKA KELKAR: Yeah. CRAIG LABENZ: Yes. So are you familiar
with this function? RENUKA KELKAR: Not-- CRAIG LABENZ: Not familiar? RENUKA KELKAR: Not familiar. CRAIG LABENZ: That's OK. So this function is used-- it's an optional
builder, obviously, that takes a build context
and the route state, and whatever else it takes,
and returns a widget. And you can use that to wrap
your entire app in something. And the Go-- I think it actually returns
the GoRouter itself. If we look Navigator Builder. Oh, yeah, that child
parameter that it takes there, that child is-- I believe it's your GoRouter. It's like your whole
app, essentially. And you can wrap your
entire app in something. And so an initial thought
for what you might do is have your entire
app sit inside there and wrap it with maybe a row. And the first cell is that
navigation bar, that sidebar. RENUKA KELKAR: Yeah. OK. CRAIG LABENZ: And
if you do that, it will mostly work,
except there's one problem. And that is that the Go Router
class, in its build method, adds a Navigator class. And a Navigator
class, or the widget, adds a Navigator--
or what is it? A focus traversal scope widget. So this is starting to
get a little arcane, but a focus traversal
scope widget is an old widget in
Flutter that was designed with the assumption that it
controlled your whole page, your whole screen. And the point is maybe if
you have a full screen modal and you're kind
of tabbing around, you're shifting focus,
which is a desktop and a web consideration, you're tabbing
around in that screen. You don't want
your tab traversal to leave the modal
on top and drop into the page behind
it that's covered. Because that would just,
obviously, be bad behavior. So the focus
traversal scope widget thinks that it's
the whole screen. So when you're tab
traversing inside of it, you cannot escape. You are locked in that
focus traversal scope. But because the GoRouter
widget adds a Navigator widget, and the Navigator widget
adds a focus traversal scope widget, that means if
we go with this scenario and you start tabbing
around and you get focus inside the
main part of your app, you will never be
able to tab back out to your side navigation. RENUKA KELKAR: OK. CRAIG LABENZ: Big problem. Need to be able to do that. So this will cosmetically work. And if you need this
behavior on mobile only, which is actually a
scenario I can't fully-- I'm not sure that
even makes sense. But if you did need this
on mobile only where there is no tab traversal
because there's no keyboard on phones
that is used for focus, then that would be OK. That would work. But GoRouter is built
with web in mind. The deep linking is so good. So it's very much-- it's a great option,
especially when you are targeting web and/or desktop. So that's a bit of a gotcha for
using this method in that way. So that was option one. It had a bit of pain. And we will not-- we will not go with option one. RENUKA KELKAR: So what I was
thinking, Craig, that let's say if you are
building any website and if you want to have a
navigation in a website, so you have a UL
tag, like a list tag, and inside that you are
creating another list tag. And you just don't need
to care about anything. It will automatically
show on the web URL. When you click on that
page, it will directly come. So is there not any way to
simply anyone can come and use the GoRouter and just-- the work is done? CRAIG LABENZ: I
see we're saying. So that is-- yeah. GoRouter-- and I
share your opinion that this should be
more straightforward. RENUKA KELKAR: Yeah. CRAIG LABENZ: Now
there is good news, which is that
internally right now, I know of some folks
who are basically running a big research
campaign to figure out what tweaks to the
API of GoRouter would really meaningfully
expand its capabilities, and while still
concealing the complexity. And so what's probably
going to happen in the next six months is-- right now, there's
the GoRoute class. There's going to be a
couple of those classes. And they're going to
behave slightly differently and you can combine
them in ways to get what you need even in
more disparate more edge case situations. Although in desktop, that
stuff's not edge case. That's core. RENUKA KELKAR: Yeah. CRAIG LABENZ: You just
have to be able to do that. So that's coming. But for now, I know a
way that we can do this. RENUKA KELKAR: Yeah, OK. CRAIG LABENZ: And should
we dive in and get started? RENUKA KELKAR: Yeah, definitely. CRAIG LABENZ: OK. So the core concept
for this is each page-- there's two components to it. Each page that we
show should use some kind of base page
widget, or root page widget, or whatever. And it'll take a
child and that'll be the main part
of the view, but it will render that
shared component, whether it's the sidebar nav,
or the tabs, or whatever. And then the second half that
really ties this together for web and desktop-- that mobile page
transition where the page slides in and slides out. RENUKA KELKAR: So I think
that covers really nicely in the GoRouter. So you can have a
transition and you can see what kind of transition
you want in Flutter way. So normally because
what transition you can see on the mobile, it
doesn't look the same way when you have the web app, right? CRAIG LABENZ: Exactly, yes. RENUKA KELKAR: So they
have given that chance. Like here, you can see there
is no transition if you don't want any transition. So you can see that, basically. But you can say, none, and you
will not get any transition with the pages, basically. CRAIG LABENZ: Right, yes. And that's exactly
the second part. Because if you get
rid of the transition, then that widget, that
SideBar nav, or the tabs, or whatever visually, it
just doesn't look like it went anywhere. The rest of the page kind
of snaps to the new UI. But the sidebar nav
never flickered, it never did anything. And so it looks like
it's what you want. It might be a new widget. It might be a new build
element, and render, but we don't care about that. We just want-- we want it
to visually be the same. Now I said there was a
small bit of pain on this, and that actually is that
it can be a new widget. So let's say in
your sidebar nav, you have some kind of
cute ambient animation. Maybe you have some background
thing floating around. It will be tricky to not
have that animation reset. RENUKA KELKAR: Oh, OK. CRAIG LABENZ: But I think
with sufficient cleverness, you could probably
handle that, too. But be aware of that. That's the much smaller edge
case potential issue with what we're about to wire up. RENUKA KELKAR: OK. CRAIG LABENZ: Shall we start? RENUKA KELKAR: Yeah. CRAIG LABENZ: OK. So we've walked through how this
looks and we see that there's-- let's see. Let's confirm that the-- nice. So that's working. And now if you-- so we can set it up
so that wherever I go, your screen will follow. RENUKA KELKAR: Yeah, OK. CRAIG LABENZ: So if
you click on the-- I don't know where it is. Oh, maybe it's the arrow,
down here at the bottom one. RENUKA KELKAR: Which one? CRAIG LABENZ: This
sidebar option. RENUKA KELKAR: Yeah. CRAIG LABENZ: Yeah. And then you click on my name. RENUKA KELKAR: Yep. CRAIG LABENZ: Great. Now I think it will follow
me, but let's confirm. Yes, so I'm scrolling
and you're scrolling. Awesome. So we have two pages
here, the FamilyTabsScreen and then the PersonScreen. So the FamilyTabsScreen
contains the tabs. So let's look at that page. Oh, it's just down
in the same file. Very handy. So the FamilyTabsScreen takes
some parameters, good stuff. It's stateful. Makes sense because
it has the tabs. And then down here it
makes a TabController, exactly what we would expect. Disposes again, just good stuff. Oh, let's-- we'll have to
think about this later-- RENUKA KELKAR: Yes. CRAIG LABENZ: Controller.index. It says, did update widget. RENUKA KELKAR: Yeah. CRAIG LABENZ: Oh, I think I see. Yeah, so this is-- RENUKA KELKAR: Like
building again the widget-- CRAIG LABENZ: Yeah. So this is called whenever a-- actually, I don't know if
it's conditionally called. It may be called every build. But this is a way-- we actually-- we used this in
the last "Boring Show" episode, which you all will have
seen by this time maybe. Actually, I'm not sure
what the order will be. In an episode to
come out at a time. We focused on that
a lot as we made an implicitly animated widget
ourselves from scratch. But it-- this must be
when some other state management redirects us. Or maybe it's even deep linking. That could be where
this gets triggered. I'm not 100% sure,
but this is going to ensure that our controller
and then our navigation UI stays in sync with everything. So that's what's
going on here, though, what exactly triggers
it, I'm not 100% sure off the top of my head. Then we get into the page. And we have all of our
stuff, the app bar, tab bar, and then eventually
TabBarView is-- oh, these are, I
guess, all of the-- oh right, yeah, the children. Then it just shows one
and that's the whole point of the controller. It's been a minute since I've
actually used the TabBarView. And then this is kind of
the magic of GoRouter. Look how easy this
part is on this tab. So we need to basically make
all of that functionality shared and get rid of the
page transition exactly like you mentioned. And then I think it will
look pretty darn good. So let's do it. RENUKA KELKAR: OK. CRAIG LABENZ: We'll
need a new widget. And I'm going to keep
this one because this has all the stateful stuff
and the tab controller. And that's going to be
the only thing that's interesting in our new widget. So instead, I'm going
to look at the-- the family view is-- oh, this is also
really quite good. FamilyViewState-- oh,
what's going on with this? I didn't even notice at first. Yeah, this is also
stateful, right, right. Use the
AutomaticKeepAliveClientMixin to keep the state
scroll position. OK, sure. We'll do that. That is good. And then we scroll down and-- oh, this. So just simple widgets. All right, this lists all
the people in the family. And then there's
probably a person screen. And now this, this
is really the issue because the person screen
doesn't know anything about that bar. It's just a fresh scaffold. RENUKA KELKAR: Scaffold, yeah. CRAIG LABENZ: Yeah. OK, so first thing's
first, let's rename the FamilyTabsScreen to be-- what should we call it? Maybe RootScreen? RENUKA KELKAR: Yeah. CRAIG LABENZ: RootScreen? OK. Let's do it. So this is going to
be called RootScreen. And I think-- I'm not renaming it in
the GoRouter spot yet, I'm just renaming
it everywhere else. So this will be
called RootScreen. And this isn't going to
take a family anymore because this doesn't
know about families. It's going to maybe
know about an index. So as I-- let's collapse
this and look down. And RootScreen's saying
it will collapse that. So our family view,
this does take a family. That's good. And it shows the interesting
parts in the middle. So I don't even think we
have to change much here yet. So now we're going
to uncollapse this. But I think we can get rid of
this parameter, the family. And then this index, I'm
going to save this code because I definitely
anticipate needing to reference it again later. Oh, you know what? Let's look at our-- let's look at our pubspec. Probably not worth updating
this to the newest Flutter. Yeah, so we can't use
super parameters yet. RENUKA KELKAR: OK. CRAIG LABENZ: But--
oh, I accidentally closed the live share. I tried to just close the
tab, but I closed the page. It's a pro move. Let me go back. Maybe my browser bar will
remember the live share. Oh, it does. I may be about to rejoin. Sign in. You'd think I would stop doing
this by now, but here we are. So I'm back in, and closing
pubspec and opening main.dart. You may need to-- yeah, so you
may need to follow me again. RENUKA KELKAR: Yeah, OK. CRAIG LABENZ: Disconnect, no
I don't want to disconnect. Oh, there we are. So I'm going to go
into lib main.dart, and you're following. Awesome. So back down into
the RootScreen. So yeah, we got rid
of this parameter and we aren't able to use the
super stuff yet, but that's OK. And then we don't need
this assert anymore. So this can all
become just this. And we'll format it
ourselves even though we seem to be in a transitory state. We don't have an index
parameter anymore. Wait, no. We will have an index parameter. What am I talking about? RENUKA KELKAR: Yeah. CRAIG LABENZ: We have to
have an index parameter. But we won't-- oh, I've lost
my old history because I closed the tab. Well, it sure is a good
thing I just put this here. So we are going to
take this.index. Let's think about it. But we aren't going to figure-- we're not going to look at
which family's ID or anything. I'm not sure. Well, I'll have to see. But maybe we will still
take required this.index. Now what are you upset about? Index isn't final? It is. Wait, why does it say
index isn't final? Oh, it must be initialized. Well, it is initialized. I just said it right here. Does this make any sense to you? Oh, I wonder-- you aren't
getting that linter error. It must be stale on my end. For me, I'm getting RootScreen
as underlined, but you're not. RENUKA KELKAR: I can't see that. CRAIG LABENZ: OK. So we'll call that a
false negative, hopefully. Now down in the RootStateScreen. So we are going to keep this
tab controller and the length. This is interesting. So this is going to need to
know about how many tabs to show and what to print. Is there a way that we can make
this not know about families? RENUKA KELKAR:
But if you don't-- CRAIG LABENZ: I'm thinking not. All right, so I may have
overshot on some of these. I may have gotten a little
overzealous on-- oh, that's a little too far. I wonder if we will need to
still do all this family stuff. RENUKA KELKAR: Yeah. CRAIG LABENZ: Renuka,
I'm getting the sense that you knew I was going
astray the whole time. RENUKA KELKAR: No. CRAIG LABENZ: Is that correct? You've thought about
this more than I have. Is this where this was? RENUKA KELKAR: No, we want to
add a family on the RootScreen as well here. I think we missed that part. CRAIG LABENZ: Oh,
the selectedFamily? And that was a family
selectedFamily like this. And then that was
probably required. RENUKA KELKAR: Yep. CRAIG LABENZ: Have I
now kind of restored it? RENUKA KELKAR: No, it's not. CRAIG LABENZ: Oh, this
will need a semicolon. RENUKA KELKAR: And the index,
it is showing the error to the index. CRAIG LABENZ: Oh,
because it's this dot, so it would
have to be like this. RENUKA KELKAR: Yep. But still, it is
showing the error. CRAIG LABENZ: Oh, now
you're getting the same one that I was getting. RENUKA KELKAR: Can we go
back and do the controls here and then-- CRAIG LABENZ: Oh,
oh, oh, oh, oh. OK, I think actually
I see what it is now. This needs to be like this. But does the super
go at the end? Is it like this? No, no semicolon. We may need to just
spam Command-Z. RENUKA KELKAR: Yeah, it's done. CRAIG LABENZ: I've
made a royal mess. Oh, no. Yours likes it. Mine still doesn't. Can you format? Because mine won't
format either. RENUKA KELKAR: No, it's not-- CRAIG LABENZ: All right,
we're manually formatting. Well, that kind of worked. All right, we'll
leave that for now. So I guess, actually,
all we're going to do is use the RootLayout
up here, RootLayout, and then also use
the root layout here. But then the point
is we're going to pass a child because
the RootLayout is going to stop knowing what it shows. So the RootLayout is going
to also take a child. I guess that's the change. We'll say required this.child. And then-- RENUKA KELKAR: So we
want a RootScreen, right? So-- CRAIG LABENZ: Yeah, we're
going to use that everywhere. RENUKA KELKAR: OK. CRAIG LABENZ:
Final Widget child. And then it's the render
method in the state there that will start to do
something different. And if we scroll back up-- interrupt at any point. You looked like you were
about to say something. RENUKA KELKAR: So yeah. Here, the root layout, we
have created a RootScreen. CRAIG LABENZ: Oh, did I just
call it the wrong thing? RENUKA KELKAR: Yeah. CRAIG LABENZ: Well,
thanks for that. RootScreen. I get incorrect or
non-existing linting on my end. So it's very handy for
you to point that out. Yes, RootScreen. And yeah, RootScreen
still thinks it's broken on my end,
which is really helpful. So that widget parameter
that was missing up top, that is now going to be a
lot of stuff that appears in-- oh, it's FamilyView, right? And the FamilyView-- so
I think we grab this. And we have to figure out
the activeFamily all the way up top. So it's the FamilyView like
this we're going to pass in, but we have to determine
the activeFamily. And so then we'll basically
just grab this code right here and come back up. And this might not
be a very elegant-- I don't know what the
name of that syntax was where you just have
the arrow return statement, but I think we outgrew it. So we'll store our
activeFamily family final, and that will go
here, activeFamily. Is that making sense? RENUKA KELKAR: Yep. CRAIG LABENZ: Now I'm also
thinking that this activeFamily should be able-- I think our route
screen will maybe be able to stop
knowing about families. It'll just know their names. But we'll get to
that in a second. So the FamilyView here--
and by the way, at any point, if you see where
I'm going with something and you want to drive, hop in. I see on your screen there's
red stuff everywhere. RENUKA KELKAR: Yeah,
there is red stuff. Yeah. CRAIG LABENZ: And I
can't see any of that. So selectedFamily. That came from-- where
did that come from? It was passed in and we passed
it in-- oh, it's just this. This is selectedFamily. RENUKA KELKAR: Yeah,
that is selectedFamily. CRAIG LABENZ: So
we'll cut this maybe and we can say final
selectedFamily equals here we go. So now that works. What else is broken? So the RootScreen and then-- RENUKA KELKAR: The activeFamily. CRAIG LABENZ: Yeah, this
goes back to selectedFamily. Oh, this could just be
families.data where index. This is totally redundant. These are going to
point to the same thing. RENUKA KELKAR: Yeah. CRAIG LABENZ: I think. Data where-- What does
this constructor do? Family data, family ID. Yes, those definitely
do the same thing. So selectedFamily
and activeFamily-- RENUKA KELKAR: Yeah, the-- CRAIG LABENZ: --are
literally the same thing. RENUKA KELKAR:
--same thing, yeah. CRAIG LABENZ: So thank you. And-- RENUKA KELKAR: We have to
assign the selectedFamily. CRAIG LABENZ: --now
this becomes this. Now we're still-- it's
upset about something. RENUKA KELKAR: Still is, yeah. CRAIG LABENZ: What
error are you seeing? RENUKA KELKAR: So the name
parameter index is required. CRAIG LABENZ: Oh. Yeah, that was true. How did we get
that index before? Let's Command-Z. RENUKA KELKAR: We don't have
the index.time, I think. We just added a new one, right? CRAIG LABENZ: Oh, index. It wasn't ever a parameter. That was the whole point
of this index where. Oh, I've really made
a giant mess of this. So now index isn't a
required parameter, but we deduce the index. And I think this is starting
to make a lot more sense. We'll scroll back up-- RENUKA KELKAR: Yeah,
the [INAUDIBLE].. CRAIG LABENZ: And this
is closer to happy, but we need a semicolon. RENUKA KELKAR: Yeah,
there is no real line. CRAIG LABENZ: All right. OK, so we have this idea where
the root screen, all it knows is it's going to
do some tabs stuff. And then it's going to
have a view underneath it. And it's completely up to that
view to figure out what to do. So you have no
errors at all, right? RENUKA KELKAR: Yeah, no. CRAIG LABENZ: Can
you relaunch the app? RENUKA KELKAR: Yep. CRAIG LABENZ: Or reload,
or whatever we need to do, the green circle arrow. RENUKA KELKAR: Yeah. CRAIG LABENZ: And then
let's go to your running app and see if it still works. RENUKA KELKAR: Yes. So now it's-- OK. CRAIG LABENZ: I'm nervous
that it didn't restart because you were still-- oh, it was just a deep link. That's how we were on that page. So it does work. We didn't break it. RENUKA KELKAR: Should
I run it from scratch? CRAIG LABENZ: I think
we're OK, but we could. RENUKA KELKAR: OK. CRAIG LABENZ: So in
terms of what we do next, we basically want to
introduce RootScreen around the PersonScreen. So if this starts to
return RootScreen, and ultimately the child
will be the PersonScreen-- oh, it just rebuilt on your end. So we can verify
that it does work. RENUKA KELKAR:
Yes, it's working. CRAIG LABENZ: OK so our-- that a little bit of surgery
that we performed did still compile and run. OK, cool. Want to switch back to the code? RENUKA KELKAR: Yeah. CRAIG LABENZ: All right. So we've got the
RootScreen and we're going to wrap the PersonScreen. But what are the other
parameters that we need? It's the selectedFamily, which
is required, and that's it. Looks like it. So we need to get the
same selectedFamily thing, and that's just family. That's right here, right? So it was selectedFamily
as family. And now, I still have false
positive linting errors, but it looks clean on your end. RENUKA KELKAR: Yeah. Yeah. CRAIG LABENZ: So why
don't we rerun it again and see what we get. RENUKA KELKAR:
Let me just rerun. CRAIG LABENZ: You're a big
believer in the full reboot. RENUKA KELKAR: Sometimes-- CRAIG LABENZ: It's the
only way to be sure. RENUKA KELKAR: Yeah. CRAIG LABENZ: I think we
could have done a hot restart. RENUKA KELKAR: OK. CRAIG LABENZ: Obviously,
Flutter for Web doesn't have hot reload
because JavaScript is different than Dart. So this is good. And let's click. Now we just need to get rid
of that page transition. And do you want to
drive for that part? RENUKA KELKAR: Yeah. CRAIG LABENZ: All right. So let's see. So if we go on the the-- Yes, shamelessly steal it
from the documentation. Never anything different. RENUKA KELKAR: Yeah. So we have a Page Builder
and we just need to add that. So here-- CRAIG LABENZ: Now was it-- one thing I didn't notice,
was the parameter different? It was Page Builder
instead of Builder? RENUKA KELKAR: I
think they have a-- the new version, they
are using a builder. I think that is what
I have read about. CRAIG LABENZ: Well,
you just had it up. I just didn't read it
fast enough because I'm a really slow reader. So whatever you saw
I'm sure is correct. RENUKA KELKAR: So-- CRAIG LABENZ: But yeah, I'm
just pulling it up as well. RENUKA KELKAR: If we-- do we have that kind
of thing here itself? CRAIG LABENZ: Oh,
it is Page Builder. Look. RENUKA KELKAR: Yeah. CRAIG LABENZ: So it's
a little different. RENUKA KELKAR: It's
a Page Builder, yeah. CRAIG LABENZ: Yes, so we'll
need to convert our Builders into Page Builders. Sorry, keep going. RENUKA KELKAR: So if we use-- let's say if I just do
this, comment the code, just [INAUDIBLE] Page Builder. CRAIG LABENZ: Yeah,
that's perfect. Yeah. RENUKA KELKAR: Yeah. And we just need to use the-- CRAIG LABENZ: And steal
back where we want. Well, I guess it's
kind of all of it. Well, can we-- Oh, yeah. Keep going. Yeah, there's just a couple
of things we need to change. Yeah, basically all
of those things. RENUKA KELKAR: And then
we need to use a child. CRAIG LABENZ: So the child
will be the RootScreen-- RENUKA KELKAR: Yeah. CRAIG LABENZ: --I think. RootScreen, nice. RENUKA KELKAR: And the kind? CRAIG LABENZ: I have no
idea what that means. RENUKA KELKAR: No,
it's not like that. CRAIG LABENZ: Where
did kind come from? RENUKA KELKAR: The kind is the
parameter of the Page Builder, I think. CRAIG LABENZ: Oh, look. It was passed to the
example transition screen. Oh, I think that is just-- we don't care about that. That was to show the transition
that had just happened in the transition sample. I think we're OK. RENUKA KELKAR: But how
we can remove this? A child should be like a family,
what we have done here, though? CRAIG LABENZ: No, I think
the whole RootScreen. So on line 30-- RENUKA KELKAR: Yeah. CRAIG LABENZ: --where
we have-- that's where we're going to
return the RootScreen and all of the stuff. RENUKA KELKAR: From here itself,
like from the RootScreen? CRAIG LABENZ: Yeah, down-- yep. Yep. This is going to be delightfully
confused about what's commented and what's not. RENUKA KELKAR: So I will
just remove the comment. CRAIG LABENZ: Perfect. RENUKA KELKAR: Yeah. And I think-- CRAIG LABENZ: Yeah,
don't think any of it-- that can be constipated. RENUKA KELKAR: Yeah. Then we need to like this one. CRAIG LABENZ: And we'll need
to convert the whole function again. Yeah, that's-- I was thinking
to put it there, too, for a second. But we have to
convert the function. On line 28, we can't use
the arrow notation anymore. If you remember, I
had to do that below. Oh, so not quite. On line 28, see where you've got
the equals sign and the arrow for the quick return function? RENUKA KELKAR: OK, like a
flat arrow kind of thing? CRAIG LABENZ: Yeah. We just need actual curlies
for our function body. RENUKA KELKAR: Can
you show me that? CRAIG LABENZ: Oh, sorry. Yeah, I must be-- that's not what I want. Where's the tab? Where's the live share? Are you the live share? Are you my mommy? So yeah. So I think what we need to
do is go into this mode. RENUKA KELKAR: OK, return and-- OK. CRAIG LABENZ: Return, yeah. And then you'll be able
to paste whatever-- I want closing curlies. And then this has to
become a semicolon. These are all footguns that I
was tripping over a second ago. And now I think what you have
in your clipboard, hopefully still, can go right here. RENUKA KELKAR: OK. CRAIG LABENZ: Oh, it put your
cursor in an awkward spot. So yeah, up one row. RENUKA KELKAR: So-- CRAIG LABENZ: Yeah, paste. Nice. And then we apparently have
one too many semicolons. This has to go to this. RENUKA KELKAR: Yeah. CRAIG LABENZ: All right. Yeah, so you want to
convert the other one. RENUKA KELKAR: But we have
already added this, right? So here is the transition. Should we add here as well? CRAIG LABENZ: We'll have
to do it here as well. RENUKA KELKAR: OK. CRAIG LABENZ: Yep. RENUKA KELKAR: So-- CRAIG LABENZ: In fact,
that first one probably doesn't even matter. RENUKA KELKAR: Yeah. CRAIG LABENZ: Because
it's the root page. So it's like we transitioned
to there from what-- RENUKA KELKAR: Yeah, that's to-- CRAIG LABENZ: --the
splash screen? Whoa. But this one's really important. RENUKA KELKAR: Yeah. So we need a Page
Builder from here. CRAIG LABENZ: Oh yeah. And we'll also
need a comma there. Yeah, nice. Yeah, just grab
the little thing. RENUKA KELKAR: Yeah. And then we can have here. And we just need to pass the-- CRAIG LABENZ: Yeah,
grab the RootScreen. RENUKA KELKAR: Where
is the RootScreen here? CRAIG LABENZ: Oh, I think
RootScreen will go-- RENUKA KELKAR: Up here? It's-- CRAIG LABENZ: Oh, no. Sorry, this is just the-- RENUKA KELKAR: So
RootScreen is here. I think we are
messing up something. CRAIG LABENZ: Yeah, so this-- no. So you're good
for the most part. RootScreen-- so this
becomes PersonScreen. RENUKA KELKAR: Yeah. Here a child is a
PersonScreen, yeah. CRAIG LABENZ: Yeah. So do you want to keep driving,
or you want me to drive on it? RENUKA KELKAR: Yeah,
you can answer it. CRAIG LABENZ: OK. So I think we grab
these two lines. We can cut those. And we'll put them here. And it's similar to what we had. The auto formatting situation
right now is atrocious. RENUKA KELKAR: Yeah. I know. CRAIG LABENZ: The
live share is like, what do you want me to do? I have no idea. So then we'll put this up
here and get rid of all this. RENUKA KELKAR: Here, we
have to say a family, the selectedFamily is a family. Line number 57. CRAIG LABENZ: 57. Select-- oh, good call. I don't have that linting. Thank you. RENUKA KELKAR: Yeah. CRAIG LABENZ: And-- RENUKA KELKAR: And
I think it's done. CRAIG LABENZ: Oh, it is? Holy smokes. Going to save. Yeah, see if you
can get it to format because we are just wading
through the muck right now. RENUKA KELKAR: So
if we go here-- CRAIG LABENZ: I
guess this will work. Whoops. OK, I think I formatted it
the old fashioned style. RENUKA KELKAR: No,
something is not-- CRAIG LABENZ: So yeah,
what's happening? Can you go back and then
click into one again? I totally missed it. I didn't see anything. RENUKA KELKAR: So
first family one. If we click on the
John, yeah, it's coming. CRAIG LABENZ: We did it? RENUKA KELKAR:
Yeah, it's coming. So if you click on this, so
a person and family, too. Yeah, person, too. CRAIG LABENZ: Oh, but look. Something's off because it
still just shows the names. It should say the
person in their age. RENUKA KELKAR: Yeah,
yeah, yeah, yeah yeah. We missed that, yeah. CRAIG LABENZ: OK. How did we screw that up? RENUKA KELKAR: So there is
some detail screen or something maybe? CRAIG LABENZ: Stage. RENUKA KELKAR: It is changing
on the URL, but not the page. CRAIG LABENZ: Oh,
I think I know. And it's keys. Hop back to the code. This is super interesting. RENUKA KELKAR: OK. We have to pass the key. CRAIG LABENZ: We passed the
key in one too many places. RENUKA KELKAR: OK, that's-- CRAIG LABENZ: So before,
the key had to go here. This was the important spot. But when you use
the Page Builder, then the key goes on the page. But because we just
copied and pasted the key survived, and the problem
is that the pageKey-- I think it basically just-- the pageKey is telling Flutter,
there's nothing to see here. You don't need to
rebuild any of this. And so it never bothers
to make the page screen. So I have a sneaking suspicion
that if we get rid of this, and if we scroll up and
we get rid of this-- I don't know if it'll work, but
something will be different. Let's find out. All right, so yeah. We'll love it. So there we go. We can click into one. Oh, just nothing. So still not close to right. OK. State pageKey. I'm trying to think here. This still feels like some kind
of key-based problem because we very clearly are not-- RENUKA KELKAR: Seeing the-- CRAIG LABENZ: --using the
FamilyView both times. RENUKA KELKAR: Yeah. CRAIG LABENZ: So
we know that we're getting into this
Builder, and we're even returning the correct
widget, but something is preventing Flutter from
showing the correct widget. But actually, just to
confirm that, let's-- just to make sure we're not
completely losing our minds, let's do this. And then up here, we
can say, selectedFamily. And let's rule out that
we're totally bonkers. See what this gives us. RENUKA KELKAR: Let's see. This is the-- CRAIG LABENZ: Yeah,
so I can see it. So it says family in the logs. And then if you
click on a person-- RENUKA KELKAR: Yeah,
it's not clickable. CRAIG LABENZ: Well,
now it's just crashed. RENUKA KELKAR: Yeah. CRAIG LABENZ: Oh,
what did they say? What happened? You want to pop back
over to VS Code? I'm not getting the stack trace. We really made it upset. I'm going to click
on-- oh, maybe I could. I'm just on the right tab. Oh, here we go. Failing to evaluate didComplete. Internal error. No frame with index 14. Oh, that old thing. What? So I'm seeing this
in the terminal if you want to click right here. Yep, that icon. Exactly. So this brings up the terminal. Oh, you haven't had
the terminal up. I'm sorry, you've been
playing on hard mode. RENUKA KELKAR: Yeah. CRAIG LABENZ: So if you
click on the triple dots. RENUKA KELKAR: OK. CRAIG LABENZ: Which editor
do you use again normally? RENUKA KELKAR: Normally
Android Studio. CRAIG LABENZ: She's
an Android Studio gal. RENUKA KELKAR: So I am
completely new with the VS Code today. CRAIG LABENZ: Yeah, I yanked
her into a new environment and then I just
let her flounder. So yeah, triple dots
and terminal-- or no, debug console, that's going on. RENUKA KELKAR: OK CRAIG LABENZ: Debug console. RENUKA KELKAR: OK. CRAIG LABENZ: Hey, look. Isn't that handy? So anyway, who
knows what that is? Let's pretend it's fake
and just rebuild the app. And then if it isn't
fake, then we'll have to figure out what
it is if we see it again. RENUKA KELKAR: So let me-- CRAIG LABENZ: A
Chrome proxy service failed to evaluate
expression didComplete. RENUKA KELKAR: So how
I can run this now? Great. CRAIG LABENZ: Oh, so you seem
to be in full screen mode, which is a little tricky. Yeah. Exit full screen. RENUKA KELKAR: Yeah. CRAIG LABENZ: Yeah. And then maybe if you just
drag the window to be big. But Apple's fullscreen mode
is like, I don't want that. I never want that. What's wrong with you? Oh, I did it again. Do you like this mode? RENUKA KELKAR: No. CRAIG LABENZ: Right, who could? Because it's hiding the thing. Maybe move the cursor all
the way to the top and then the dock will appear. There we go. So run right there
to the right, yeah, and then with start debugging. RENUKA KELKAR: OK. CRAIG LABENZ: That one
will work, too, I think. I actually have never noticed
a difference between start with debugging and
start without because it feels like all the debugging
things still happen. RENUKA KELKAR: Yeah. CRAIG LABENZ: Maybe
I'm just dense, but-- RENUKA KELKAR: I have to
learn the VS Code now. You have shown me the way. CRAIG LABENZ: Sorry
I've done this to you. I do like VS Code a lot, though. We're using it right now
specifically for the live show, but I also just like it. OK, so let's click on something. So it's doing
stuff, that's fine. Now click on a person. I'm getting all the right logs. And it says in the log-- so
if you switch back to VS Code, you'll see it as well. RENUKA KELKAR: Instance, yeah. CRAIG LABENZ: Yeah. And I generally leave
this up and then just stay on the debug console. So instance of person. And yet, it doesn't show. So I still think we
have a key problem. And I'm just wondering about
this state, this page key. But this should be different
from route to route. So I wouldn't expect
that to be an issue. What happens in
the PersonScreen? Let's just verify
that TapHandler. So we go-- this is in the nav. Oh, you're not
following me anymore. So you want to click
on my name here. RENUKA KELKAR: Yes. CRAIG LABENZ: OK. So I am-- I'm just scrolling down. I'm in the-- it's
now the root layout. So that's actually not
what we care about yet. It's not going to be
down-- yeah, here we go. So this is what's going to
route to the next thing. And we even know that's
working because we're getting the correct log
statement that says instance of family, instance of person. So there must be
something with the keys. And I'm not going to
lie, I'm not an expert. The person-- the RootScreen. So this takes the key,
passes it forward. I don't think any
of these widgets are going to do the
wrong thing with keys. This is correct. And then the PersonScreen
that's going to also be correct. Oh, but those keys-- no, they're not
even going to that-- they're not even going
to the widgets anymore. That's what we just deleted. RENUKA KELKAR: Yeah. CRAIG LABENZ: The
key isn't going here. That's the only
way what I was just looking at could have
possibly mattered. So that's not it. Let's get rid of this
comment to go to. Oh, I have a false positive
on the linter again. So that's fun. So I guess we need to look at
the documentation on GoRouter for this page key. Maybe we're doing
something wrong. Except if you switch-- if you go to the
transitions page, boy, are we doing what it says. Right? We can see-- the reason we're
using that line of code is we got it from-- it's right in the middle
there of your screen. We got key colon state.pageKey
from the documentation. Keys and StatefulWidgets
is literally the next part. If you find yourself
implementing a page builder function that returns
a screen widget that extends StatefulWidget, you have
to be careful with your keys. RENUKA KELKAR: I think we
haven't read the next part. That's way. CRAIG LABENZ: Well, I'll be-- for example, the
example multiple routes navigate to
the same screen. So this actually
doesn't sound exact. The multiple routes
navigate to the same screen. OK, kind of. Yeah. Notice that in addition to using
the custom page transition, the authors in setting
routes both use the same key. This is important because
the BookstoreScaffold screen is stateful and switches
between tabs based on the selectedTab property. If the keys were
different, the user would see a jarring transition
when they switch between tabs. I fearfully actually
don't think this is our problem, which stinks. Because it was fun to think
we had found the issue, or found the answer. Yeah, that issue is
this scaffold key. That is showing us
hey, use the same-- RENUKA KELKAR: Same scaffolding? CRAIG LABENZ: Exactly,
across different pages. Exactly. So that's not doing
it for us just yet. Well, let's just try
some stuff and see. I keep going to the wrong thing. What happens, do you think-- what if we just get rid of
these keys all together? Also, let's print
the keys because I want to know what those do. So print state.page key. Let's start with this. But I'm really curious
about what will happen-- you want to rerun? I'm really curious what will
happen if we just get rid of those parameters altogether. They don't seem to be helping. RENUKA KELKAR: So
if we run this-- CRAIG LABENZ: So the keys and
then you click on a person. Now the keys are different. All the key is is basically
a string and it's the path, and they're different. So the idea that this-- it's hard to imagine
those being the problem. I'm really quite confused. PersonScreen, RootScreen. Nothing is const. RootScreen. Oh. Oh my gosh. Oh my gosh. Oh my gosh. I know the issue
and I was thinking we needed to do this
earlier and I forgot. Ready to laugh? RENUKA KELKAR: So yeah. CRAIG LABENZ: OK. What we didn't do in
the root screen is-- drum roll, please-- use
the child parameter. RENUKA KELKAR: Oh, we have
created that, right, but-- CRAIG LABENZ: We never used it. So in our TabBarView, our
children can, I think, just be-- are you following? RENUKA KELKAR: Yeah. CRAIG LABENZ: You're syncing? RENUKA KELKAR: Yeah. CRAIG LABENZ: OK, great. What if we do this? Oh, widget.child. RENUKA KELKAR: Yeah. CRAIG LABENZ: That's
funny, yours says it's bad, mine said it was good. RENUKA KELKAR: Yeah. Let's rerun this. Try it? CRAIG LABENZ: Try it. Oh, it doesn't like something. Let's rebuild the whole
thing because we just yanked the floor out from
underneath it pretty badly. RENUKA KELKAR: Just a minute. CRAIG LABENZ: Here there was-- oh, the controller's length
property has to match. Oh, that's stinky. I don't want that. So it's going to error again. So now we have to think about-- see, this is a little
awkward about tabs. If we were using
sidebar navigation, this would actually just work. But we need to be using-- RENUKA KELKAR: So that is
one more kind of thing that-- CRAIG LABENZ: For tabs. RENUKA KELKAR: Yeah. So here everywhere,
whatever example I have seen on the GoRouter,
everywhere they're using the tabs. And we don't want tabs
for every time, right? CRAIG LABENZ: No. RENUKA KELKAR: So how would you
just create the pages just like [INAUDIBLE]? CRAIG LABENZ: Should we
get rid of tabs and use-- how much time do we have? Wasted a lot of time
on not using the stuff that we're writing. We have 10 minutes. Think we can use
a NavigationRail in that amount of time? RENUKA KELKAR: Do we finish
that time in 10 minutes? CRAIG LABENZ: I don't know,
it'll be really challenging. So I also prefer
a NavigationRail. We have to decide fast. Do you want to use
NavigationRail, or do we want to make
the tab bar work? Your call. RENUKA KELKAR: You can
do that NavigationRail. I want to see that. CRAIG LABENZ: NavigationRail? Let's go. So our main thing
here, the scaffold, we're going to have
a different body. So let's comment this out. So this is going to go-- we're
going to have to be hasty here. We're going to use a row. And then we'll say it's going to
have children that are widgets. And the first widget
is going to be-- RENUKA KELKAR: Column. CRAIG LABENZ: Yeah. The responsiveness of this
is not going to be perfect. Nobody-- kids, don't
try this at home. We're going to have
maybe a flex widget here that will be the first 20%. So is the parameter flex? Or no, it's flexible. I can never remember
these things. Flexible, is that it? RENUKA KELKAR: Yeah. CRAIG LABENZ: I'm getting
odd tooltips here. What do you see? RENUKA KELKAR: So-- CRAIG LABENZ: Yeah. Flex and child. RENUKA KELKAR: [INAUDIBLE]
add the child, yeah. CRAIG LABENZ: Great. OK, so the flex
is going to be one and the child will be our nav
bar, which we haven't made yet. And we'll even
call it SideNavBar so there's no confusion. And then that's the
first element in the row. And then the next
thing will be flexible, and we'll give this the
other 80% of the screen. And it's child
will be the child. And actually, I don't
want that comma. So now our whole tab
bar-- also we won't-- oops, we won't use this. That's gone. And I'm not going to
bother getting rid of this TabController stuff. It'll just sit there dormant. So our RootScreen,
that seems pretty good. Index, that seems pretty good. Now we need to
make the nav bar-- oh, this is widget.child, right? RENUKA KELKAR: Yeah. CRAIG LABENZ: Widget.child. RENUKA KELKAR: So we have to
create the side bar for just-- CRAIG LABENZ: Right, yes. So we'll pop down. Class thingamajig extends. I don't know if it needs to
be stateful or stateless yet. We'll just make it stateless. Widget const SideNavBar. I have no-- I have no tool-- or no snippets, that's
why I'm typing this all. RENUKA KELKAR: OK. CRAIG LABENZ: So key,
key, super key, key. Override widget build,
BuildContext Context RENUKA KELKAR: Context, yeah. CRAIG LABENZ: NavigationRail. Now we're going to need
the families on this. So that'll be basically
all the families. const-- nope, final
list family families. RENUKA KELKAR: [INAUDIBLE]
required parameter. CRAIG LABENZ: Required, yep. Good call. NavigationRail. I'm going to pull
up the documentation for the NavigationRail. You want to do it as
well so that people can see similar things
to what I'm reading. NavigationRail Flutter,
NavigationRail class. So the parameters that we need
for this are a selectedIndex. That's great. We've already been using that. On destination selected,
that's our old onTap method. We need that. And destinations,
which are widgets. So I'm going to copy the
destinations for sure. We'll grab this too, I guess. Actually, I'm going
to just grab it all and then we'll perform
some surgery on it. So if you want, you can
pop back over to VS Code. And I, folks, just
stole this out of the built in-- the embedded
DartPad in the navigation, and have copied in
all of this stuff. So I'm going to indent it. OK, selectedIndex for
us, that is a thing that we'll also need. So required this.selectedIndex. RENUKA KELKAR: Index, OK. CRAIG LABENZ: And
then that will go here final int selectedIndex. So no underscore anymore. RENUKA KELKAR: Yeah. CRAIG LABENZ: OK, setState. So this is going to need a-- RENUKA KELKAR:
StatefulWidget, yeah. CRAIG LABENZ:
Basically an onTap. Required this.onTap. And final function that
will take an integer, and that's onTap. And then all we're going
to do is call onTap. So that's how this
is going to go. Then we'll have our
navigation type, or, sorry, our label type. I have no idea what that is,
but it came out of the example. So hopefully it works. OK, not going to worry about
the families or the icons yet. But our label here, so this
is actually going to become-- RENUKA KELKAR: The-- CRAIG LABENZ: Yeah,
we're going to-- RENUKA KELKAR: --families. CRAIG LABENZ: Yeah. We'll run a map on this. So we'll say destinations
is families.map. And that's going to turn into
a navigation rail destination object each time. And then this takes a function,
which itself takes a family, and then that returns a
navigation rail destination. I know we're going to
need the two lists. We'll just get to that
now before I forget. And this is going to take
these kind of parameters. And all we really care about is
that the label is going to show the family.name or something. I'm not-- oh, there
it is, family.name. OK, love it. So we can now get rid
of all of this code. And I think we've
converted this. RENUKA KELKAR:
I'm getting some-- I think you have given. CRAIG LABENZ: Feel
free to fix it. You are right. That needs to be a comma, right? RENUKA KELKAR: Yep. CRAIG LABENZ: Nice. Well done. The auto indenting there
is really miserable. That's all better. So OK, now I know
this isn't going to be getting any
of the parameters that it needed up above. So let's do that real quick. Where you at? Where you at? I'll look at your
screen so I see the red. RENUKA KELKAR:
Yeah, there is one-- CRAIG LABENZ: Can you help? RENUKA KELKAR: What is that? I don't know. [INAUDIBLE] when you
click on that red, it will just take you to that. CRAIG LABENZ: It
does what you want. It helps you. SideBarNav. Oh, here it is. RENUKA KELKAR: OK. We have a-- CRAIG LABENZ: There it is. OK, so this needs an
index, which is that same-- where the heck are we? Oh, we're in the root thing. So does this know the index? Oh, it has a controller.index. Oh boy. This refactor is going
to be tricky to pull off. Oh, there was-- no,
that's not it either. RENUKA KELKAR: So can we not
give the index of the family? CRAIG LABENZ: Yeah,
we're going to need that. We may have bitten off a bit
to chew here with limited time. But we know that this line
of code gets the index. So boy, are we
going to steal that. Plop it here. Final. And that index can now
go here, except it was called selectedIndex, right? RENUKA KELKAR: Yeah. CRAIG LABENZ: Then we
need all of the families. And that actually
could have also been probably pulled from the
global thing, but whatever. And then what was our
other parameter we needed? Oh, onTap. RENUKA KELKAR: Yeah. CRAIG LABENZ: So onTap is
going to be a function that takes an index, int index. And then we can-- there must have been such
a function floating around before. No, there isn't. RENUKA KELKAR: So did
it update that function? CRAIG LABENZ: Oh, awkward. What did it do before? Oh, I guess the controller
just handled everything. RENUKA KELKAR: Yeah. CRAIG LABENZ: So this
is-- well, this is already a StatefulWidget, that's fine. So this will just
start to hold a index. So this will be
int index equals 0. And then-- now we can
still do that setState. That's what will happen here. setState. RENUKA KELKAR: But we need
to have a StatefulWidget. CRAIG LABENZ: We're
in a StatefulWidget. RENUKA KELKAR: OK. CRAIG LABENZ: We lucked out. RENUKA KELKAR: Yeah, OK. CRAIG LABENZ: We finally
got one to go our way. RENUKA KELKAR: OK. So there is still some errors. CRAIG LABENZ: Yeah,
what else are we seeing? RENUKA KELKAR: OK,
selectedFamily. CRAIG LABENZ: Oh,
selectedFamily. We don't know what the
selectedFamily is yet, so we'll have to steal a
line of code that does that. And selectedFamily, we know
that lived up in the root here. OK, here's the selectedFamily,
although we actually called the correct parameter up here. So we'll grab this,
bring this down. Glad you at least have a linter. selectedFamily,
what else is wrong? Oh, state. RENUKA KELKAR: State now. CRAIG LABENZ: Oh, boy. We can't buy a win. Oh, but selectedFamily
was passed to the widget to RootScreen. Woo, undo. This just becomes
widget.selectedFamily. RENUKA KELKAR: But still,
it is showing the error. CRAIG LABENZ: Isn't defined
for the type of RootScreen. RENUKA KELKAR:
Import the library that defines the selectedFamily. CRAIG LABENZ: What's
it talking about? Yes. It is. Oh, we accepted the parameter,
but we didn't actually save it. Final selected-- or
family selectedFamily. And then this will
just become this dot. That make sense? RENUKA KELKAR: So
there is one more-- CRAIG LABENZ: All
right, we got less red. RENUKA KELKAR: So build-- CRAIG LABENZ: We have
to return the scaffold. RENUKA KELKAR: Yeah. CRAIG LABENZ: Very helpful. Indeed. RENUKA KELKAR: Now there
is no red line of-- CRAIG LABENZ: The odds that
this works are one in a million. This is like Luke exploding the
Death Star level improbable. Oh, my gosh. RENUKA KELKAR: The
name is coming. CRAIG LABENZ: So things
are mostly not working. But-- so we've got a
nested nav bar here, or an app bar, but that's
just because that widget still says to do it. We'd delete that. RENUKA KELKAR: But we can-- CRAIG LABENZ: We
aren't correctly passing in the activeIndex. That's also a little stinky. SideBarNav selectedIndex. Why didn't that update? It feels like that actually
should have updated. Oh, that's probably
because it's in the router. We didn't-- RENUKA KELKAR: So we
don't get another thing. CRAIG LABENZ: Yeah,
that's not working either. Well, we're a little
out of time, I think. RENUKA KELKAR: Yeah. CRAIG LABENZ: But
though we tried to perform a last second
emergency surgery, we first ran into a little
issue because the way we were setting this up was
incompatible with the tab bar. RENUKA KELKAR: Yeah. CRAIG LABENZ: And
then we said, well, let's do a navigation rail. But we only had 10 minutes. RENUKA KELKAR: Yeah. So this is the
way like when we-- any beginner can
just dive into the develop the Flutter
Web navigation. So they are getting such
kind of problems, yeah? And you are saying that this
is the favorite package. So that is also-- that is also then creating
some problem, right? CRAIG LABENZ: Right, right. No, it is. RENUKA KELKAR: Yeah. CRAIG LABENZ: It is a
problem that the official-- RENUKA KELKAR: People
are-- just a thought that, OK, Flutter
team is telling that this is a favorite,
then we should go with that and just play with this. But we need some help. CRAIG LABENZ: I love that
you phrased it that way. Yes, folks. If you're out there
struggling with the GoRouter, make some noise,
raise questions. But also know that there
is a lot of internal work going on right now to figure
out how the API can evolve just a little bit to
hopefully vastly expand the power of GoRouter. And especially around things
like nested navigation and the similarly sounding, but
different nested navigators. But yeah, this kind of
pattern is definitely-- it will get you very far at a
minimum for now, and probably forever. Though forever, you might
not need to do this forever. But if you have a
root layout widget and it just builds whatever
your permanent navigation is, and it takes a child,
and then plops the child into the right spot. And of course, don't try
to refactor something in 10 minutes, and
you'll probably get it working pretty easily. But did that all kind
of make sense to you? RENUKA KELKAR: Yeah, yeah. Definitely, definitely. Yeah. CRAIG LABENZ: OK, well
I would love to hear, because you've got another
project that you've been bumping into this
exact problem with. RENUKA KELKAR: Yeah. Yeah, same. CRAIG LABENZ: I
can't wait to hear how it goes making this change. RENUKA KELKAR: Yeah, definitely. I would love to, yeah. CRAIG LABENZ: Awesome. And folks, also another
rock-solid code sample for this in The
Boring To Beautiful-- did I already talk about that? Did I? RENUKA KELKAR: No. CRAIG LABENZ: I didn't? In the Boring To Beautiful
Codelab that came out in I/O, this really feels like
I thing that I said, it shows how to do this. So don't really pay much
attention to the chicken scratches that we just did. But that one works. And you can find that in
the Flutter Codelab's repo on GitHub called
Boring To Beautiful. Renuka, thank you so
much for coming in. RENUKA KELKAR: Thank
you so much, Craig, for having me here and just
experimenting with this code author, and giving
me this experience to lie on "The Boring Show." CRAIG LABENZ: Yeah. I mean, hopefully we didn't bore
you or anyone else too badly. I had a lot of fun,
even though it always feels a little stinky when we
don't get to the finish line. But such is life. You can't get everything
done in an hour. RENUKA KELKAR: Yeah. CRAIG LABENZ: Well, folks,
this has been great, and until next time. Have a good one. [MUSIC PLAYING]