Nested Navigation with GoRouter (The Boring Flutter Development Show, Ep. 63)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[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]
Info
Channel: Flutter
Views: 33,789
Rating: undefined out of 5
Keywords: nested navigation with GoRouter, nested navigation, GoRouter, flutter navigation, web navigation, flutter tutorial, how to use flutter, the boring flutter development show, boring flutter show, flutter development, flutter developer, flutter developers, firebase developer, flutter, google developers, google, flutter latest, latest from flutter, flutter updates, Craig Labenz
Id: NuX4b6qtU2U
Channel Id: undefined
Length: 70min 42sec (4242 seconds)
Published: Tue Sep 20 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.