# Building the Cloud Next demo (again) | Observable Flutter #85

## Метаданные

- **Канал:** Flutter
- **YouTube:** https://www.youtube.com/watch?v=VS3eeSGsi3w
- **Источник:** https://ekstraktznaniy.ru/video/22911

## Транскрипт

### Segment 1 (00:00 - 05:00) []

Hello everyone and welcome to another episode of Observable Flutter. My name is Craig Levens and I'm your host. And today I'm excited to continue exactly what I was doing last week and start working on uh the demo that I've been building with Noey from the Firebase team for uh Cloud Next and it sounds like at IO as well. It'll be rebranded a little bit for that. So that's exciting. Um okay. Well, there's uh not a ton of fanfare today. We're just going to kind of pick up what I was working on yesterday when I ended my workday. Oh, wait. Actually, that's not true. I work on a different direction today. Okay. Anyway, we'll get into that as we get into that. To start, folks, do recall that this is the Flutter community. We all know what that means. All right. So, let me organize a few windows here. I don't want to share my screen while OBS is up. Ain't nobody got time for an infinite mirror. What window do I want to put it in? The old OBS. How about right there? Okay. So, uh, as you may recall, I've been working on, let me grab the Figma mocks for what we're dealing with here. I've been working on building this demo. So, we saw a bunch of these screens last week. The user comes in um picks up their you know picks up clicks past the intro screen and then gives their name uh configures their coffee a little bit then describes their happy place. Then Gemini Nano Banana of course will generate four images that match that happy place. So I think in this scenario we're to believe that they typed yes the mountains. So we've got four flavors of the mountains here. And then the user So it's going to be a little different here. The user is going to be able to click either accept or tweak. Maybe we'll go with modify. I don't know. Anyway, if they accept the image that they've selected here, then you're just good. If they select modify or tweak or whatever, then the user will jump to this screen where they'll answer some questions that Gemini has uh produced. This little genui situation and yeah, so the skip anyway, the button, it's not terribly important. Um then after you answer these questions, you get four more images that are variants of the one you selected. So unlike here where we're still just looking at the original batch of images, let's say you had this kind of uh wine, you know, olive farm, whatever this image is. So you answered some of these questions. You're going to be looking at four new olive uh orchards. I don't know what the noun is for an olive place. And uh then you can either pick one of these or you can revert and go back. And then eventually you accept your order and you go to the barista. They make you a latte. They sit it in a latte printer and the latte printer prints your image and based on how good the printer is, you know, maybe it looks good, maybe it looks silly. Uh and then you uh move on about your day. So, that is what I've been working on. But there's another flow. We're gonna Is there I guess it's not in this mock code. Oh, here it is. Time. Yeah. Thought for sure it was somewhere. Okay. So, there's another place another screen. This is actually what I'm going to work on today. We're going to have uh this little I guess mosaic is what it's called here. It's going to be on a giant 55 in touchscreen TV on the side of the demo and it's going to show the recent images that people have had printed on uh on their lattes and oh, Olive Grove, Randall says. Okay, well that's super helpful and sounds right. And so we still have the Olive Grove front and center

### Segment 2 (05:00 - 10:00) [5:00]

here. So, um, this will and you can click on one because it's a touchscreen TV and then you get this place and you, you know, take a selfie by it or whatever. Um, okay. But I want to work on this and I um I want it to be like each thing kind of moving around like bubbles. And when they hit each other, I want to have a little ripple effect as they bounce off of each other. But like literally have them, if two are kind of colliding like this, have them go and like, you know, um, yeah, ripple in that way. So that's going to be my quest for today. And I don't know how far we'll get because I have basically none of this um, in order yet. So great, I haven't written a single line of code for any of it. So, here we go. Um, Aish says, "What is this guy doing? Can anyone tell me? " Uh, yeah. I think I've just been describing it, but hopefully, um, yes, wonderfully ambitious indeed. Yeah. Uh, hopefully, Aush, you now have a sense of what this guy is doing. All right. Anti-gravity, report to the principal's office. Where are you? in the wrong window because Mac OS cannot for its life keep anything straight. All right, I definitely want a clean um starting point here. I actually could have committed all of this last night. So, I'm going to do that. And this is um wizard improvements plus um persistence persisting in progress or all right now we can Am I even on a branch here? No. That that push was nightmarish. Um get checkout Let's put the what branch do I want to name yesterday's work? Uh, saving orders we'll call it. And then I'll push this to saving orders. And then uh we can would it be an olive branch? But um nice. We love a good pun. um get checkout staging which has kind of been our main branch. Get pull in case I'm missing anything. One of my favorite tricks that I do as a developer is push a branch then forget to go back to the main branch. So I push a branch, it gets merged. I forget to go back to staging and pull and I just keep working on that branch and commit more. I'm always happy when I do that. It's really good. Okay. Wow, there's a lot of problems. What? This seems like uh a build step failed or something. Oh, well, I'm actually kind of confused by the What are you talking about? Oh, these are open files that don't exist anymore because of Git. Okay, so that was a little bit of funny business. All right, it's time to look at some of the intro code or like the uh the Bootstrap code in this project because that is what's going to be important next. So, I've made the screen the code bigger. Uh, I'm not going through Streamyard today, so I'm not able to put any chats up, but I have my uh template in front of me and I'm reading um reading what you're all saying. Does this mean you now prefer to use Google anti-gravity instead of VS Code? Sai asks. Uh, yeah, I have actually even aliased the code word. So, in my uh zsh uh profile, whatever it's called, um I have alias code equals agy because I have so many years of muscle memory typing code space, whatever to launch something in VS Code. Uh so, yeah, I've definitely been using anti I've been using uh anti-gravity. it. I don't think I would have changed as quickly if it wasn't based on the same UI, like the same Kodium core. Uh I it would have been a much harder cell for me to

### Segment 3 (10:00 - 15:00) [10:00]

get me on an IDE that did not look visually like VS Code and did not behave like VS Code. Uh but yeah, I've been using anti-gravity in my all of my both personal and professional development, which has been fun. Uh oh, someone's asking if hog dominance has updated to the latest. Oh, on most of my projects using shorthand throws lint errors. So yeah, hog, you either need to update to the latest version of Dart or you may lint packages. It would be surprising if you' done both of those and still had problems. I've been shorthanding all day every day. Okay, so let's take a peek at how this project works for better or worse so that it'll make sense why I am writing code where I write it. So users will not be really creating accounts in this. It's like a kiosk situation is the main flow. So all accounts that will ever exist in this app are pre-made by Noey and I and they have different permissions in Firebase. Um and not like records in Cloud Fire Store, which is what I wanted to do because I'm lazy and don't know the best things, but like actual Firebase off um grants or tokens. I don't know. there's some word for exactly what they have. And so anyway, Noey makes these um user accounts in advance and then once you sign in with them, the this permission token comes down and the app is able to route you specifically to where you go. So if we look at um main. dart, which I'm not able to find while I'm also narrating. So main. dart Dart just calls Bootstrap. Fairly typical. Bootstrap starts to set things up. Sets up some dependency injection. Turns on Firebase uh path URL strategy because I'm not a monster. Commented out debug code to delete stuff in Firebase. And then we run our app view. Great. So the app view has a go router which is uh wrapped by a little extra fun. And the go router. Now we'll click into that. So go router has all of its routes. And it has this whole like redirection flow. But actually I think the routes is all I care about right now. How did this work? Uh ah no. So redirection will send users. There's only a couple routes. There's where the people setting up the demo will log each account in to the correct screen. And then once you're logged in, you just get routed to your uh your groups, you know, your user types homepage. There's some like just idle readonly display UI like that's this Q. Uh recent orders is what we're going to be working on today. This is that kind of floaty bubble screen. So that means that I can go into a recent orders home screen and write code here and after I log in as that user, which I'm now remembering I don't actually know what their login is. So I'm gonna have to figure that out. Uh, so smart of me. So smart. I'm going to So, I have the app running here in the kiosk form. So, I asked it for a theme park with dinosaurs and it generated not as scary as you would have maybe expected based on the cultural zeitgeist around theme parks with dinosaurs. These are cute dinosaurs at a carnival. Um anyway, so this is the kiosk flow at that pick and pick a latte art thing. We're still testing things like you know should these be sepiaone images because they will definitely be sepiaone once they are printed on the coffee. Anyway, um so what I think I'm going to do is make a new window here and let's go into this make file and grab this. And I'm going to run this on a new port. And this will be the port that I test. Oh yeah. Okay. Go into the UI directory.

### Segment 4 (15:00 - 20:00) [15:00]

Try again. So now this will be the port that I launch the window at and log into the coffee version from. So I'll do this and go to port 8082. It's probably not ready yet because it's still loading. Yeah, those cute dinosaurs are pretty cute, aren't they? Can you please show in future videos how to develop for CarPlay using Flutter? Satoga asks. Uh, whoa. I don't know if that's a terribly supported flow. I would love for it to be. I know Toyota is going pretty hard on Flutter as their main SDK. So, I'm not I actually I've never even thought about CarPlay. That's super interesting. Um, but Toyota is going to make getting Flutter apps in your RAV 4 and whatever other cars Toyota makes uh really easy. So, that's pretty exciting. Um, okay. Very long name. I don't really know how to uh would know how to approach pronouncing it, but I'm sorry I'm a bit late. Is this Flutter? Yes, it is. Uh, okay. So, now I need I'm sorry. I'm going to look at my other screen here and see if I can recall how to log in as the uh Yeah. Okay. So, it's you. This is the email address I need. How do I copy this email address? Okay. Like this maybe. So, let me refresh this page. Are you going to take me to the login? It's a really pretty login page. You guys are not ready for how pretty it is. Great. Okay. Um, let me try to type this password. Do I remember what it is? Let's go. All right. So, I'm logged in as the recent orders user, and we can tell because that's what the page says. I'm a pretty smart guy like that. So, all right. This means I can close a bunch of nonsense and start developing. Okay. So, let's remind ourselves what this is even going to look like. So, we're building this. So, we need a white background. Uh, I'm guaranteed a portrait landscape, so I don't need to worry about any kind of responsive adaptive layout things. This will only ever run on a giant touchscreen TV, so I need big fonts. And yeah, so I the one bit of responsiveness that I definitely need to do is scale my text based on the size of the screen. I absolutely need to do that because if what looks good on my monitor will look ity on a 55 inch TV that people are meant to walk up to. Um, what are you building this time? Mr. Verden asks, I am continuing last uh last week's stream and working on the demo for Cloud Next and Google IO that I've been building for quite a while. And today I'm specifically building this screen. And these bubbles are supposed to bounce around back and forth with each other and have a little wobble effect when they hit each other. And I have a shader queued up in mind that I hope to use for that um Hanan made for me a while ago. I simply asked if he knew how to approach something and like two days later he said, "Here's the shader for it. " I was like, "Oh gosh, God bless you on. " Uh okay, here we go. Let's start building. So we need a white background and this um header situation. So that will be our first quest. So I'm using Shaden UI. No, Shaden Flutter, not Shaden UI. That's the other one. It's just as good. And so yes, so I first need now my overall app theme here has black for scaffold background color because that's what the other ones have. Also, I really just don't like themes. I think themes as a concept are bad. CSS as a concept is good. Uh, come at me. Themes are just bad.

### Segment 5 (20:00 - 25:00) [20:00]

Luckily, they can be overridden. So, background color on a scaffold. Let's make that happen. Oh, look at that. Emails uh still in my clipboard. Background color. And this will be appc colors. We'll import app colors, by the way. So, these four flavors of the app, one thing we were debating is like, should this be four different Flutter apps or should it be four different segmented user journeys within a single Flutter app? And obviously, we've gone with segmented user journeys within a single Flutter app. Um, it's paying a lot of dividends in basically everywhere except the fact that the theme only works for one of those. Okay, so that's good. And now the other thing that we need is yes this gen latte and then the footer. So I already have this footer widget ready to go. So what I think I'm going to do for the app bar is we need to know the width and I think appbar for oh it is still a preferred size widget. You know, I've not really done much with preferred size widgets before, and I'm wondering how cute I can get in that to know it's to know its width. Like, can I just put a layout builder in there, or is that going to be a problem? Uh, an interface for widgets that can return the size this widget would prefer if it were otherwise unconstrained. Okay. There are a few cases, notably app bar and tabar, where it would be undesirable for the widget to constrain its own size, but where the widget needs to expose a preferred or default size. For example, a primary scaffold sets its app bar height to the app bar's preferred height plus the height of the system status bar. Widgets that need to know the preferred size of their child can acquire that their child implement this interface. Wait, widgets. I need to know the preferred size by using this class rather than widget as the type. Okay. And then my preferred size is a getter, which is interesting because that means it does it have access to the build context. Not sure. Uh, all right. Whatever. So appbar is going to How did I do this? You know what? I don't even think I did an app bar. I think I am not going to use an app bar. I think I'm going to use a stack around everything. And that is going to be way awesomer. Okay, let's wrap all of this in a layout builder. Make it a layout builder with a Oh, actually I can use my other class layout provider. builder and I'll bring in that widget and then this gets layout info. All right, there we go. So now I can have a positioned widget which we'll calculate the position of in a second which is going to have a child you is going to say gen latte and now let me look up where I did that on the other screen. Um because I just want to reuse that. So app view gen latte. Yeah this one. Here we go. Okay. So, apparently it was this. Okay. Here we go. Um, oh yeah, H4. So, instead of flutter material, I'm importing shad CN flutter. And we'll put that at the bottom. Cool. There we go. So, this scaffold now has a child. Okay. So, this is not going to be centered. It positioned where we want it. But, uh, look at that. Gen latte. Oh, and the color needs to be black this time. Themes are a bad idea. Themes are not a good idea. Style. Text color. Nope. Wow. Text color for the style. Craig, good job. text style color app colors. bl black. I'll probably refactor this later into being an actual theme that like modifies the original theme and sits above this and then uh I don't have to pass color values everywhere. But that's not what's interesting about this for today. So that's not what I'm going to do right

### Segment 6 (25:00 - 30:00) [25:00]

now. Okay. Boom. Now we need to position it and size it. So this is where um we can oh I think what I'll do is give this first of all definitely a width of layoutinfo width and a height of like oh again we already have to start scaling things. So, I'm just going to pick arbitrary values here based on like what I'm looking at on my screen and then I'll scale uh off of these. So, this will make sense when I'm done, I hope. All right. So, I'm currently looking at 1278. Uh, wait, that's width. I'm looking. All right. So, 1200 will be my base height. let's set a const value here. Static uh base height equals 1,200. So now we can get our height scaler. Final height scaler equals layout info. ight divided by that. And then we'll get a similar one for uh width. And because this is a portrait situation, I'm going to say what's 1,200 * like 0. 6 720. So that'll be my base width. 720. Okay. So now I have these values that I can multiply sizes by. So, uh, let's give it sure a height of 100 times a height scaler. I'm wondering if I should actually just do layoutinfo. height times a certain percentage. It's interesting thought. Um, and then I think the text will the text could scale with like either of them probably. Uh, but I'm going to get rid of H4 actually, which probably means I don't need to pass black anymore. And let's just pass a font size of yeah sure some value and we'll see if it's good and you're not const anymore. Okay, that's pretty big. That's good. Let's give it a little top padding of um 50 times height scaler. Perfect. Okay, nice. And now I think if we just wrap this in a center that should work. Great. Look at that. Going to read a little chatterino here. Uh Randall just fielding all the questions as is tradition. Just absolutely incredible. Uh, Satoka says, "Is it possible to upload the design from Figma or an image on Flutter and then Flutter will generate the exact match design in code? " Um, Gemini can do a bit of that. I don't know if it would be able to do it with the Shad CN package. Like maybe it could do it with official Flutter like a material theme. Uh, it's an interesting question. And then uh more talking about oh scalar o e er oh you know one day I'll learn my own language interesting. Why is it not highlighting all of them? Hello. There we go. Okay. That's er apparently. What is what does scalar mean? Define scalar with an a. Is it even a word? It is having only magnitude, not direction. Indeed, that's not what I want. Uh, okay. So, oh yeah, did it center? It did center. Lovely. Now, we need the footer widget. So, let's go here. Positioned. This will be right below that. So, this height is going to be all right. Let's put um let's say final this is the gen latte banner height equals this value and then we'll have the gen latte banner top padding equals this top padding equals that and then final total Gen latte height equals these two oops these two together. Okay. Now that goes here and that means

### Segment 7 (30:00 - 35:00) [30:00]

that this can become that value. Great. And its own height will be Oh, you're going to need a child at some point to stop whining. So your own height can probably just be the same. Let's try to give you the same height and your width is the same. So that's pretty good. And then I believe there's just a footer widget that is ready fodder and that can be constipable. Can be constipated. All right. Well, that was not good layout logic. What? How did I get that wrong? Uh the top total genate. Oh, I passed in. No. What's going on here? We've got the top padding plus the height and those two values. I wonder if this height was not enough based on the size of the text. No. What on earth? What am I doing wrong here? Okay. Yeah, you don't need these PNS. Scale factor might even be more clear. That's true. Scale factor. But why with a top value? So this is crazy. And then let's give you like a border of color app colors blue or something. Oh yeah. That doesn't work in a stack. I guess I have to do it here. This is going to be less instructive than the other thing would have been. Oh, you probably need a center as well. And then cons. But still something's not right. Why are they overlapping? What's the thing that decide you that uh really made you decide to move to Google anti-gravity and are you that you are not getting in VS Code? Um so I've loved VS Code forever and honestly didn't think I was going to change for anything and I tried cursor and it just kind of I don't know felt not quite there. So, it's I think the it's the same stuff that cursor does well that like having Gemini right here, the autocomplete is really really strong. It reads my mind so consistently. I really wouldn't I don't have a lot of patience for it making up nonsense. It does do some of that. One of my favorite things when you have a PubSpec version open, it'll be like, "Want to change some of these version numbers? I got new numbers for you. want to try those? And you know, that's always just like anti-gravity, quit smoking crack. Um, but mostly it just reads my mind really well and is uh very, you know, it speeds me up. So that's what made me switch. Uh, Luca Silva watching from Brazil. Hello. Some right here with my the math ain't math. So the top of this is the height and the height of it then the top of the other one should be the sum of those and then it has its own height. So why are they not stacking? Why are they drawing on top of each other? Am I mad? Like this is really crazy. So to debug this, I am going to have a colored box with a color of app colors dot something. Pick a color. And then let's do the same thing for down here with this size except you get these and then your color is something else. How about blue? Sure. Are these colors not going to stack?

### Segment 8 (35:00 - 40:00) [35:00]

The top is the total gen latte height. Oh, they do both literally have the same thing. It's that I can't Oh, I can't copy and paste correctly. There we go, guys and gals. I was going to lose my mind. But like uh most times when I'm deeply confused, it was a PBCA situation. Oh, whoops. I deleted the wrong one. const center child footer. Great. Now the footer needs the pos needs to be able to have black text. So this will be a new mode. Um this font color equals appc colors. White is its default final font color. There we go. Let's Yeah, let's watch it. Autocomplete for us. And so then here we have the style. Da da. There we go. Font color. Lovely. Oh, and I have to pass in black, don't I? Google blue. No, my friends. Black. Nice. Okay. So, this could have a little more space. It's um not quite Yeah, it needs to be wider. much bigger. So, let's figure out that the footer. So, don't pass in anything right now. Yeah. So, I think I'm just going to pass in like a scaler. Yeah. this dotscale equals 1. 0. That's great. Scale the footer. Um, that aspect ratio is going to be the same. That's fine. The max size is times scale. Actually, we can just put that at the end, can't we? Which then means you I guess could be con still. Yeah, it seems pointless. Let's do this. All right. So, that's great. Yeah. So, this is our uh let's call this like UI scale so I'm not shadowing. So, I think this will just work. So, now if I say UI scale 1. 5, are you going to get bigger? It did, but it's not centered anymore. Um, why aren't you centered? Both of those windows are taking up the same thing. Also, 1. 5 is a little too big. 1. 2. Wow, that didn't make a big difference. One to 1. 2. All right, that seems about right. Now, do there's a little too much padding below these. So, I think this thing maybe should be like 80. Yeah. Okay, that's better. Let's even go 70. Okay, that's good. Now, why aren't you centered? So this Wait a minute. What? Yeah. So my whole width here is 894 pixels which means 48

### Segment 9 (40:00 - 45:00) [40:00]

447 is the middle. Okay. 447 So, Jen Latte is centered. The footer is not centered as our eyes does not deceive us. Um, use the alignment widget maybe. So, the center widget is an alignment widget. Little known fact, if you ever command click your way into the center widget, you see that it is a line and it merely implies an alignment value of alignment dot center, which I don't actually see where it does that, but anyway, it does. So, let's go back and look at the footer. Also, I got to close these rulers. They're driving me crazy. Um, okay. We Yeah, you're just a row and all of these things. Uh, wait a minute. Main axis alignment. center. That didn't change anything. Like start is obviously not what I want, but also doesn't change anything. So that's weird. What? What on earth is going on? What if I put a spacer before and after? Okay, also changes nothing. Now I'm starting to think like. Yeah, let me just get rid of the Gemini text. Okay, so I am actually changing running code. Is there a particular reason you aren't using the Flutter screen util package? Well, that's because I've never heard of it before. Um, pub. dev packages flutter screen util. Oh yeah, this is like exactly uh screen util and knit initialize and set the fit size and font scale to scale according to the systems font size accessibility option. Uh well it seems pretty good. Um probably not going to incorporate this right now because it looks like there is a fair bit fair amount of like sorting out to do. Um and but thank you for that tip. That is flutter screen util is discouraged. Randle says you want responsive instead. Wrap the footer in a container with full width. Well, something funny is going on because I think that the center takes up the full space of its parent. Like this is just this is true. If I were to give this width time 8, we should see it all switch shift to the left. So center is taking up the full width of its parent and then trying to center the child in it. Now the thing about the footer is a row. So the footer also takes up its whole parent. So then it would be up to so this center widget shouldn't do anything because it's up to the row to center everything now that we think about it. Yes. No change. So the footer. Why are you not centering things? Okay. I see there's some scaling situation that I have not accounted for. It's probably the responsive sizedbox. builder. That is it.

### Segment 10 (45:00 - 50:00) [45:00]

All right. So, this is like just a utility widget that I made that I'm currently trying to abuse without thinking about very deeply. And that is the issue. So, I'm not going to worry about this anymore because I really want to get to the floating bubbles. That's where the fun is. So, we can see that the uh row is still sized. It's trying to size itself based on its uh like previous values that were passed in. So something that I did here. I wonder if I just don't what if I didn't have a max size. No, it's still wrong. Okay, interesting. Okay, I'm going to sort this out later. That isn't what's exciting. That's at least not what I want to get into today. But I do understand the problem and I'm going to leave this border here so that I remember to come back and fix it. Okay, so it is now time to have a bunch of images floating around. We're already 47 minutes in, so I really need to get cooking. So, I'm going to go into Fire Store for this and make a new collection. Start a collection. And these are going to be recent latte images. Next, and the first document ID is going to be um whatever, don't care. And then we're going to have an image URL that I'm just going to put something in for now and we'll fix that in a second. And then we'll have description which I'll also fix. Okay. Recent latte images. You should load. This is strange. Uh all right. I see folks are talking about using the widget inspector. Uh I should. You're right. Uh the problem is that I'm currently not I'm like not attached to the debug tools. So I just need to kind of go through that step. But you're all right. That is the next debugging trick. Um okay. So in order is it in metadata? Where does this happen? In order itself, I guess. Oh, yeah. No, it is in metadata. I'll just pull them out of Where's the batches? Here we go. Okay, so this has a bunch of images. So, I'm going to grab a handful of these and go back here and set the image URL to that. And the description is going to be I'm just going to leave that asf. Okay. So, uh, I don't really want to make a large body of these. So, I'm going to cheat and multiply data in code. So, I already have a recent orders home block. So, let me drop into here. And for this, I'm going to make a new repository. So, I'll say final. This is recent images. repository. I have some packages to import data layer. Oh, this isn't actually what I want. And then I guess late final. And here I will initialize it. And then we have to register it because I don't think I have that yet. Yeah, I've just switched it to batches. So, I'll actually call this the recent latte images repository. And yep, that'll be that. And you don't exist yet, so you're justifiably upset. New file, recent latte images repository. dart.

### Segment 11 (50:00 - 55:00) [50:00]

Let's watch Gemini write this for me. Oh, and I already got it wrong. That's okay though. It's not crazy of it to assume that I'm doing normal Firebase things and not using my own random package. Yeah, it's a problem. Yeah, I know. Can you just format, please? Why aren't you formatting? Am I missing something? This is weird. Why am I having to format this myself? What is the problem? Yeah, I know. Oh, I need another closing thing. There we go. All right. Uh, tough day. Yeah, isn't it always? All right. So, I do need some of these bindings things for latte images as recent images. I didn't want to uncomment that. So, basically, this is just going to be recent and recent latte images. And you don't have an ID, so that'll just be image URL, I guess. No, won't it? Uh, no, that's not right. You're going to have to get an ID. todo. Should this be a separate model? Oh, yeah. And I got to provide a prompt and the questions. It definitely has to be a separate model because we don't have the questions anymore. So, I'm going to have to copy this and make a new model. And that's going to be a recent latte image. And you will have an ID. These are only ever generated by the server. So, we don't have to worry about it being null. We don't have questions. There we go. So, that is all good. And now I can comment this out again. Okay. So, I'm gonna have to run the generator in the data package. Oops. I don't even know what key I just pressed. I did that. What is happening? What is going on? What just closed? Why is this my life? All right. Looks like autocomplete is also destroying the flow. Uh I'm not sure. Yeah. Did autocomplete get in the way at some point? I think it was pretty helpful. I pressed the wrong keyboard shortcut at some point and that really sent me off into a tail spin. But um if you have time, could you explain your personal package? Uh personal Yeah, the repositories. Yeah. Yeah, absolutely. Okay. Um

### Segment 12 (55:00 - 60:00) [55:00]

so recent latte image, that's good. So now let's go back to dependency injection and recent Oh yeah. Okay. There's a couple exports that we have to do. So data, you're going to need to export the recent latte images repo. And then in the data package models is going to have to oh because I was in the same file. So that's fine. Okay. So now recent latte images repo. There we go. And this is now recent latte image. Nice. And then your recent latte image. And I'm going to give this a fire store source. Gemini definitely thinks it's a collection. Uh, but what it really needs is Firebase. Oh, no. Get it. I firebase. Fire store. What's your problem? Oh, it's fire. Nope. That's right. What's the issue here? Define type. Am I not importing? What's the image? What? That's right. Okay. And you probably need the bindings. Fire store source. It's like literally right here. Fire store source. Oh, right. That's not in the package yet. Um, there we go. All right. All right. So, I think that will now load the thing. So, if I go back to the recent orders block, I think what I can do. Yes. So you need How did I register that? Yeah, this could now actually just be repository recent latte image. That's better. So in the block, I'll grab that. Every now and then it does. Autocomplete just goes bananas. All right, let's load all of the things. Get items and we'll print the results. Now I'm going to have to add this to security rules. So, So here we'll say match recent latte images image ID uh allow read if oh yeah so this is actually like I'll just say if true for now but this needs to be a todo convert to if recent. And then we'll say allow write if false. Okay, I'm going to copy all of these, put them in the fire store. file. Okay, there's like the tiniest chance in the world that this is going to work. A lot of print statements. It's upset about couple lines over 80 things long. Otherwise, we're doing okay. So, here is our thing. I'm going to refresh and load the tab. Um, we have an error. Good stuff. Oh, whoa. There's a Oh, yeah. Okay. So, this rendering error, I believe, goes back to when I had a box in between the stack. Oh, no. myself in here is not registered in sign. Get it. Uh but I swear I did I distinctly recall

### Segment 13 (60:00 - 65:00) [1:00:00]

registering this inside dependency injection which literally right there. Yeah. Notice how nothing changed. Make another space. And that way you can kind of come in a pinch. Is it running before get it is initialized? That shouldn't be possible because in Bootstrap we set up our dependency injection and await it before running any app code which would get us to the page which initializes the block which runs this code. So that really should be the explanation. Nothing here is like an unawaited async techmith. No worries. Oh, so someone's asking what is the binding stuff? So, I've been working on this data layer package for like ever. And the whole idea of the bindings is just these are the things that like if it was Python or a language with introspection. I'm not saying Dart should be those languages, but you would easily just kind of you could extract this information from a class itself like either some attribute on it or you know whatnot. Um but like or if repositories could only extend classes that had a certain superass and then they could imply this information. Uh like for example getting the ID off a class. I know that my class has an ID on it, but I don't want to enforce some kind of superass that has an ID that would prevent me from using these repositories against like third party um classes or APIs that I don't control or you know, whatever. Uh so there's no guarantee that there's an ID on this class even though I know I've put it there. So instead, anytime the repository needs to get the ID off something, it passes the object to a get ID function where the dots kind of just get connected. Um, similarly to JSON from JSON, just kind of again, I know that those functions exist, but I'm not coupling these repositories to classes that like are descendants of something that guarantees that they exist. So bindings just offers functions that the repository can call when it needs to serialize or des serialize something and it delegates as needed. So that's all bindings is. It's just kind of like the glue meta code. Hopefully that made sense. Um all right. So um yeah, why is this happening? Let me put independency injection just like Well, it didn't print at all. What? I saw it print a second ago and then I cleared it and I refreshed and it didn't print the second time. I feel like I am not getting like the latest code or something. Something very funny is going up. Reading a few chats I've recently Oh yeah, Randall. Yep. Signals flutter for multiple reasons. Yeah. Uh Randall big signals fan. Uh can you show the UI please? Yeah

### Segment 14 (65:00 - 70:00) [1:05:00]

sure. The UI is not good right now, but soon it'll be flawless. That's what I'm telling myself. Oops, I switched tabs. All right, let's refresh here. Here we go. Okay, so all of that was just a bunch of nothing. Now, there's some other error that we'll figure out, and that's probably that I didn't put a prompt in the Firebase collection like I remembered needing to do. Anyway, this was all just working, but like dead code was cached somewhere in a really lovely way. Okay, you need a prompt. Got a field a super cool image. Searching far and wide for the U key while typing that. I was like, how do I type super? All right, it's loaded. There we go. We are now ready to actually start doing something. And I couldn't be, you know, uh 67 minutes later. I couldn't be happier. Uh recent orders home block. Great. So, the state that this is going to have. Oh, actually, you know what's going to be super cool? Oh, I just got super excited thinking about a thing I'm about to change. Um, yeah, recent images. There we go. Except you are in fact recent. Latte images, aren't you? And we can just call them images. This whole page is recent. So, that's already known. Okay, that's good. Default empty list. Great. So then we're going to need an event here which is um updated list updated recent I guess updated recent images. There we go. Updated recent images. And this will take a list of recent latte image classes called images. And then up in this we will have that event and you are going to be called this and that and after I run the builder this is going to like do good and stuff. All right. So, what we're going to do with the recent updated images is exactly what Gemini thought we were going to do, which is emit a copied state. So, I believe all the errors in this file will go away when the build runner is done. And I believe that's so much I'm going to leave and come back later. Recent home view. So now here I need a layout builder here. Where do I want to have the layout builder? Yeah, I guess here. Or sorry, not layout builder. Um block builder. There we go. And that is a recent I don't even know what it's called. Um, recent orders home. Wow, that's very verbose. Recent orders home block. Recent orders home state. That's great. And the block goes here. One thing I almost never do is use the block provider. There's one block at the very top that is a app like an application block and it watches basically just authentication if you have any way from the server to signal that like you're down for maintenance or you force an upgrade or something. It just listens to those ultra global things but other than that I always think of a block as a page specific thing. it lives and dies with its page and internally it uses data repositories which would persist anything that actually would survive the life of that page. So for me uh I always make a block in the state class usually in its state like if there's anything interesting I'd migrate the initialization of it there and then just pass it directly in. Okay, so we are now ready to rebuild and that means that we can like calculate centers and sizes which will be a fun method. So we can say here

### Segment 15 (70:00 - 75:00) [1:10:00]

image right so we're going kind of need like a little class that I think is going to be a center and a diameter. So this is going to be a class latte position. Primary constructors will be nice for stuff like this coming soon. Final. So this will be a um I guess like an offset. So we could say double for the uh radius and um then a either offset or vector 2. I guess we'll use an offset. That's kind of the center. Size of the latte image pos center of the latte image. That's great. Now we need a constructor. It's not a spell required, is it? Center required list radius. All right, let's look. I don't want this git view and I don't know how to make it go away. Does anyone do I stop this? Does anyone know how to make this end? How to put myself out of my misery? I mean, all I know how to do is close the file and bring it back. What a nightmare. All right, Gemini, write me a position. Yeah, there we go. Love it. Okay, so we are going to have a function that returns a list of latte positions. So that's going to happen here. Final latte positions. No, this is going to be calculate latte positions and it's going to basically just take like a number of latte images. So, this will be um what am I doing? I'm just going to pass state. im images. length. That's what I'm doing. So now in list latte position, oh, I'm in the build method still. Calculate latte positions int count. There we go. All right. Um I don't know any like scatter plot math. So wonder if I can cheat a little bit and kind of hardcode some. So let's look at how many latte images appear here. This is one, two, three, four, five, 6, 7, 8, 9, 10, 11, 12, 13. And that feels kind of like a reasonable number. So, what I might do is limit the number that I pull in to 13. And we could hardcode 13 positions scaled based on the actual size of the screen. Does that make sense? That would also mean I'll pass in the layout info as well. Like layout information. Nice. Um, I think this makes sense. Do you have a Git Lens extension? I do. Yeah, that's 100% what it is. I just almost never click on the thing. And then when I do, I don't know how to get out of it. So, I'm not actually surprised about where it came from. I'm just surprised that the UX doesn't have a clear get out of it button. All right. So, we only have one thing right now. So, I don't really I have I'm gonna have to think more deeply about this, but to start I'm going to like maybe the maybe these will ultimately be hardcoded. I think they will be hardcoded. I'm not I don't know. I'm not sure. Um so, to start, we've got a center, which is like Yeah, about the center. That's good. And a random radius. And then uh there's not two things, but I'm going to um Oh, these could be like ratios. Oh, that would be nicer. So, this could be 0. 5 and that'll just be a percentage instead of an act an absolute pixel value. And we could translate this into actual coordinates once we're Yeah. Yeah. Yeah. I like this. Okay. Okay, so this is like um 10%

### Segment 16 (75:00 - 80:00) [1:15:00]

in, 20% down. Sure. And then another Okay, whatever. Look at it. Just generating values that are kind of plausible. And then we'll shuffle these around a little bit. Oh, nope. That's too big. Um, whatever. Okay, this is fantastic. So, these are just the starting positions. Actually, as I'm thinking about this, I'm realizing that there's um there's going to be some really interesting interactions as new ones appear in the collection. They get pulled down and the UI needs to kind of cycle their like kind of replace an old one with a new one. Maybe we just kind of like pop it. Oh, yeah. could use um could use another shader to kind of make it pop like a bubble and have a different one grow in its exact place. That would be so cool. At a minimum, there's the old Thanos uh effect that's existed in Flutter for, you know, since the very beginning. Um that would be a fun effect to cycle out the oldest latte art after a new image appears. That would be really cool. All right. Um anyway, what are you mad about? Oh, yeah. You had to pass in the layout info. Except not because I'm making these relative positions. So, that's just good. And then we have to return them, of course. Oh, I was doing that down there. Okay. Uh so, we have the latte positions. And now we're going to say latte positions. m mapap. And this is going to go to a widget. Actually, we'll go specifically to a positioned widget. And you're going to get a let's do indexed. We're going to need that. So then this is going to be the index and position. What are you mad about? Oh, got to do this. There we go. Now we don't need the two list anymore. Okay, now we actually have to um return a positioned widget. Why don't you Why are you formatting already? Child. No. What are we missing syntax wise? There's no bad syntax anywhere else preventing this from formatting. Why aren't you formatting? Sometimes you manually format and then it's clear. Oh, semicolon. That'll do it. All right. So, the child here is going to be a latte image widget, which probably doesn't exist, but that is going to be that is correct once the image exists. And then the position is going to be all right. Let's see what we got here. Index and position. Oh, yeah. No, it's not index like that. It's uh because that's a tupole. All right. So, what is it saying here? It's the center. Is that might put the top left? I think we're going to have to like subtract half of the radius from each thing. So, um going to subtract the radius. So, that's index and position to radius. Yeah. You go. Okay. That was some good autocomplete. Look at that. All right. Now, we just need this latte image widget, which will go here is how much can it write. It's uh It's cooking. It's cooking, folks. Look at this. Look at that. Hey, surprised it did that one line at a time. Normally it just like writes the whole thing. Um, recent latte image. What's your problem? Oh, it's probably not being imported. Yep, fair enough. And then I don't know if we do need No, we will take that. And

### Segment 17 (80:00 - 85:00) [1:20:00]

that but that was layout information. Well, we got a spammer in the chat, folks. Yeah. Um, can I I'm on my One second. The spam is strong with this one. Um, remove. Oh, did I just remove one message? There we go. It's fixed. Okay. So, the image radius. No, you need a latte position required this latte position. Is that what I called it? Yep. Latte position. Very good. So now this is the latte position. There we are. That's so good. And you're positioned by somebody else. So nobody cares about that. That's great. Network image. Uh oh yeah. Then I got to put the clip on that. So if I go to choose an image, there's an oval clip I can copy. Yeah. Clip oval. There we go. That's good. So, now we got the latte image. And then you need the position. Now, nothing's going to move. Nothing's going to feel alive. But there is this boy is there a really really tiny chance that will be helped by me actually fixing Oh, how was I copying and pasting that around? Yeah, it's like me. Oh, I just didn't change that. That's what it was. Okay. Okay. Um, Dean White says, "Randall, I believe he is spamming because he wants translations. " Um, well, I don't know if that's why I don't know what that spamming was, but that would be an odd way to ask for translations. That's all I'll say. Um, all right. Let's go back to the UI. Great. Index error. Where did this How did this come to pass? Oh, yeah. Yeah. I was going to check something and I didn't. I remember thinking that this was a to-do and not doing it. So, that's not what I wanted. we need to bail. So we say if um index and position the index is greater than state. im images. length then we return nothing and you are no longer positioned. You would have to go back to widget. Okay, now it might work. Probably still won't. refresh. Hasn't errored yet. Also, not rendering anything. I thought it would render something. Uh I don't know. Oh, I remember. I know what the problem is because wait, do is? Yes, the problem is I'm printing the images. I am never actually calling the uh the thing. But okay, this is exciting. So what I want to do now is watch this repository. So I'm going to watch items and this is it just watch. Yeah. Okay. So watch is going to return it needs an ID. So look at these functions. There's by ids and watch list. That's what we want. Okay. So, this is just going to pull down the whole collection. So, I'll have to figure out like a limiting um thing on it later. Also, these are going to all need probably a server time stamp so I can order and guarantee that I'm pulling in the 13 most recent. So, I'm

### Segment 18 (85:00 - 90:00) [1:25:00]

just going to leave a to-do there. To-do. Um add server timestamps to ensure pulling the nuest images. And then also to do add a limit to only pull n probably 13 based on Figma most recent uh images. Great. Anyway, so this is going to return a stream and we can add a subscription to this stream subscription which is going to pull in a list and we have to import async and then you need a new variable name recent images subscription and so now when we say stream. listen Listen, we can save that to the subscription. And here we get our recent images we add those images. And then when we close this block, oh, let it cook people. Now we cancel that and we ignore. All right. Okay. Now it might work. Are you refreshing on your own? I don't know. Let's try again. Um, is that dash on my shirt? Yeah, it is. It's really tiny. Oh my gosh. There's a thing. It rendered. All right. So now, because we're watching this is very standard uh Firebase stuff. I should be able to add a new So, we need a description and image URL. I'm going to grab a different image URL first. Oops. Get out of here. So, pick a random thing. Pick a random image. So, we actually see two different things. Now, I go back to recent latte images. Add a document auto ID. Of course, image URL. It's like there's not a pipe in that. And then we need what was it? A description. Super good image. And then we need a prompt. Make me a super good image, bro. And now we go back here. Hey. And I switched back in time to see it appear, which probably was just because of network latency to download the image itself. All right, they are not moving. They're definitely not bumping into each other yet. Um, that is going to be quite fun work. And maybe I will save that for the next stream. Maybe I will pause on this particular um task and work on them moving back and forth and applying the shader that Hanan made so that when they hit each other they go. I think there's just going to be so much fun stuff in this particular part of the project that uh yeah, I'm inclined to save it for next week. I can't fully promise that I'll do that, but that is my current intention. All right. Um, pay a lot for a shirt like that. Julian says, "Uh, you know, it's funny. Flutter would like maybe theoretically sell swag in a uh in like the Google store. You know, Android sells swag there. YouTube does. Like it's not unheard of for Google brands to sell swag in like store. google. com. " Obviously, Flutter does not currently do that. I don't know if there's some minimum bar of like, you know, you have to have a billion users or something or I have no idea why when Google allows a brand to put their t-shirts in that store. Uh, that would be really cool though. Um, yeah, maybe I'll talk to people about that. I'll probably get swatted down, but I don't know. Maybe I won't. And then maybe shirts like this could be available, which would be cool. All right, folks. Thank you so much for humoring me while I did my day job on stream. Really love killing two birds with one stone, which is honestly an unnecessarily violent phrase. You know, birds have got to be really tired of hearing that. How about

### Segment 19 (90:00 - 90:00) [1:30:00]

uh housing two birds with one nest? H All right, I'm workshopping it. We'll figure this out later. Uh, but you know, maybe birds can live as well. Folks, thank you so much for joining today, for goofing off with me. I really appreciate the company. I do expect to be here next week. So, uh, same Flutter time, same Flutter signal. Uh, until then, everybody, happy Fluttering.
