Slivers Explained - Making Dynamic Layouts (The Boring Flutter Development Show, Ep. 12)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC PLAYING] FILIP: Hello, everyone. I'm Filip. This is the Flutter Boring Show, and with me today is Ian. IAN: Hello. FILIP: Hi, Ian. So Ian, what do you do in Flutter? IAN: I am the TL. FILIP: All right, what does it mean? IAN: That means I'm the tech lead. That means all the mistakes we've made are my fault. FILIP: Right, yes. On GitHub, Ian is Hixie. So if you were following the project, you've probably seen Hixie a lot. So Ian, today we're going to talk about Slivers, which is a scary topic for many maybe. IAN: It's not the simplest of our APIs. FILIP: On the other hand, many people don't really need to know Slivers, right. Many things are done for them, but, like, this is an advanced class. What is a Sliver? IAN: A Sliver-- maybe just to take a step back, before we talk about Slivers, we should talk about how Flutter does the layout in general. So in Flutter, we have render objects, and render objects are-- most of the render objects you'll see are render boxes. And so for example, container and size boxes, they're all render boxes. Render boxes have Cartesian corners. They have a width and a height. And the way they lay out is you give them a minimum and maximum width and a minimum and maximum height, and then they look at their children. Then they lay out the children that do whatever they want with them. And then they decide I'm going to beat this width and this height, and it has to be within the constraints they were given. That works great for boxes. It doesn't work great for scrolling, particularly when you do things like app bars and other weird scrolling effects. And so, to do scrolling, we have a different protocol that we call the Sliver protocol-- Sliver, as in a slice of something, so a slice of something that scrolls. And in the Sliver protocol, there are many different axes. If we look at the documentation for RenderSliver, you'll find the protocol that it uses. FILIP: So, yes. IAN: So we, instead of using the Min, Max, Height, and Width, we have Sliver constraints. And if you click on Sliver constraints, you'll see there are many axes here. There's literally the axis. That's the direction in which it's scrolling-- the access direction, whether it's going backwards and forwards. We have the cross axis direction, which is the other direction. If you have a grid, for example, it's scrolling up and down, say, but it's then going left and right in the cross axes. There are many other aspects. That's how much we've scrolled, how much overlap there is with the previous Sliver, and so on. And so, all of these are the incoming constraints for a Sliver. And if we go to SliverGeometry, you'll see the equivalent of size on the render box side. On the RenderSliver side is how big you are. That's the paint extent. Where the next Sliver should be, that's the layout extent-- and so forth. And again, there's a whole number of values here that you can set. FILIP: Right. So, OK. So to summarize, for a box, I only take, like, a offset and size, and I give you-- or I'm sorry, constraints. And then I give you offset and size. IAN: You give it the size, yeah. The offset officially is technically part of the painting. You can think of it as part of the layout. That's often how we do it. FILIP: But for Slivers, you get all these things, and then you can decide whatever. IAN: That's right. And the reason we do that is, for example, to do one of the app bars that stretches and grows, as you scroll-- well, the AppBar needs to know how far you've scrolled, so it knows how big it should be. It needs to know where the next Sliver, for example-- a Sliver list-- where that should be. We also use it to do things like lazy loading of content in lists, so when you have a ListView, you say, we don't actually instantiate all of the boxes-- you know, all the million boxes of your list. We only instantiate the ones on the screen, and a little before, and a little after for caching purposes and accessibility. And we do that by using this protocol, where we look at how far we've scrolled. We're like, OK, we'll start building boxes here. How far is left on the page? That's the remaining pane extent on the Sliver constraints. And then we'll stop rendering boxes, and so we only render the ones that are actually necessary. FILIP: So, OK. So now that we kind of know what Sliver is, and what's the-- sorry, the Sliver protocol is-- I thought we might want to try one of the more advanced things that are using Slivers. And that is the SliverAppBar. But, like, to be clear, you know, as you said, even ListView is using Slivers, right. IAN: Yep. Pretty much everything that scrolls in Flutter is using Slivers. There's one exception. We have the-- I forget what it's called-- the One Box Viewport or something. That is a viewport that lets you scroll a single item around. That's the exception, and that one you don't usually use, because it's not lazy. It's only useful if you have, say, the contents of a dialog box, which are almost always on the screen. But you're worried that if the screen is too small, or if the keyboard comes up and shrinks the dialog, then it might not quite fit, and so you'll want to scroll inside that. So there, it doesn't matter if you're lazy, because you're going to have everything instantiated anyway normally. But anytime you have a list, where you don't know how many things are in the list-- you use ListView, you use GridView, CustomScrollView, all of those that you think Sliver's under the hood. FILIP: Right. OK, so what we're going to do is, first of all, use the SliverAppBar, right. So we have a small app here, and it's just a list of randomly generated startup names like massPin or floodWolf, and we want this thing to be kind of a little more interesting than just, like, you know, being there. Right now, it's like a box, right. So here is this AppBar. It's a very simple API. What happens if we use the SliverAppBar first of all? IAN: It's not going to be happy at all. FILIP: Yeah. So what do I do? Oh no. OK. IAN: Yeah, the SliverAppBar, it turns out, is not an AppBar for the purposes of the scaffold. So the way to do a SliverAppBar-- the way that I remember how to do it-- is to go look up SliverAppBar in the documentation. And if I remember correctly, I put there an answer for how to actually do this. FILIP: OK. IAN: In fact, it'll be the CustomScrollView, I believe is where the actual example will be. FILIP: OK, so we remove the scaffold? IAN: So, yeah. So we don't need-- well, we can still have a scaffold, because if you have like a floating action button or a draw, you'll still use that. But instead of a ListView, you'll use a CustomScrollView. You can also find this-- if you look at the ListView documentation, there's an example there for how to go from ListView to CustomScrollView. A ListView is really just a wrapping around CustomScrollView that forces you to use a SliverList, but CustomScrollView is the real workhorse for viewports and then Slivers. FILIP: So yeah, Slivers get into the CustomScrollView. So OK, let's go through this. IAN: So a CustomScrollView takes many children. Normally, we use the name children when we have any children. Because this is a Sliver parent, we use the word Slivers instead of children. That way, you know that the children are going to be Slivers, and they're not going to be random boxes. And I believe we have successfully named all of our Silver Widgets with the word Sliver at the start. So you can start typing Sliver in your dropdown, and it'll tell you all the ones that we can use. FILIP: Right. Wow, that's a lot. Good. So we can use SliverAppBar then. IAN: Yep, that's right. FILIP: And so-- IAN: And so, SliverAppBar works exactly the same as AppBar in term of its arguments and so on. FILIP: So that would be-- IAN: And the reason we're putting this in the CustomScrollView instead of the scaffold, it needs to scroll. And so, it needs to be inside the view. If it was inside the scaffold, the scaffold isn't scrolling, and so the scaffold would have no idea what was going on there. FILIP: Right. IAN: Good. FILIP: So we already have this, which I like. IAN: There you go. FILIP: Oh, it also even removes the text. That's nice. And now what do I do if I want to have my list here? IAN: So that's where you use a SliverList. So again, this is a list of Slivers, so you can put as many things in here as you want. One of them could be a SliverList. And SliverList has basically the same arguments as ListView, because ListView is literally just a wrapper around CustomScrollView plus SliverList. So you can just pass in your children that you were putting before, and that should just work like before. FILIP: It just needs delegate. So right there-- IAN: You want you want one of the different constructors in the SliverList. We have many different constructors. FILIP: Oh, OK. I have a new Mac book, so I can't write. SliverList what? SliverList? I think it would be. I think maybe-- do we want to-- IAN: Oh yes, we probably have to provide that. So this is why I'll go to the document. So if you go to the ListView documentation again, you'll find that there is a step-by-step instructions for how to do this. FILIP: Right, but this is interactive, so we need to do this the hard way. SliverList delegate, I think-- so there are two, right? One of them is a builder like ListViewBuilder, and this one is the List, where we actually have the List. IAN: That's right. FILIP: And here we have children, which I'll just steal from here. IAN: So as a general rule, I would always encourage people to use the Builder variant rather than the List variant, because the List variant-- both of them are lazy in that they both don't render the render-- they don't instantiate the render boxes in advance, but the List version does instantiate the Widgets, because you create the whole list of the Widgets. When you have-- so in this case, for example, you have your AllNamesList. You can actually use the BuilderDelegate to just build just the Widgets that it needs of that particular time. FILIP: Let's do that. So BuilderDelegate, and of course we need the Builder. Oh, it actually-- so this would probably be context and index. OK, we need to go to [INAUDIBLE] for this. Yeah, IndexWidgetBuilder, which is it gets context and index. And then I'm just going to-- IAN: So you can just call your build tile for the specific name that is that index and its list. FILIP: So I'll call Names, index, and then-- but I need to tell it to not do things that are out of bounds, right? IAN: So you can give it the specific number of children. That's one of the other arguments to build a Delegate. You actually don't necessarily need to give that number. In this case, it's the right thing to do. In general, you don't need to give that number. If your builder returns null, we'll assume that's the last child. The reason to give the count is that will allow you to use the scroll bars. It'll be able to predict the length of the list better and so forth. FILIP: OK, cool. And, yes. So let's create-- or let's play around with the SliverAppBar just for a bit. So it has different options? IAN: It has lots of options. For example, you can set pinned. That will make it stay at the top of the list always. FILIP: Kind of like-- IAN: There is the shadow appears when stuff goes on it. FILIP: Nice. IAN: That took me days to implement. FILIP: What else is there? IAN: Floating is another effect that people like, so that one will cover-- so I don't know if you can be pinned and floating. FILIP: Oh, OK. IAN: But if you remove the pinned, then it'll scroll away. And then when you come back, it will scroll back. FILIP: Oh, it's-- my computer just for some reason stopped. OK, I'm sorry. I need to rebuild. So again, new computer things are weird. Come on. OK. IAN: Yeah, so it goes away, but now if you pull back, it goes back straight away. FILIP: Yes, so we go to here and then [INAUDIBLE] device. IAN: It was interesting when you had pinned and floating, it didn't complain, but then it made the text disappear, which is an interesting effect on the children. What's up with the-- FILIP: Yeah, it also maybe died at that point. IAN: It may be a bug. FILIP: Right. I think it just died. IAN: Yes. FILIP: So [INAUDIBLE]. IAN: There are other effects. You can add a-- what's it called? FILIP: A flexibleSpace? IAN: flexibleSpace, yeah. But I think you need to set the expandedHeight, as well. FILIP: OK. IAN: flexibleSpace is what-- so just at the expandedHeight, I think that will do bigger AppBar that then shrinks. The flexibleSpace is then what will appear behind the-- so if you go the other way, it should shrink. There you go. FILIP: Right. I love it. IAN: And if you put something in the flexibleSpace-- I put a placeholder in the flexibleSpace-- it will show you where-- that [INAUDIBLE] the X there. So that's where the placeholder-- the way the flexibleSpace goes, so typically you put some sort of image there or something like that. FILIP: Awesome. IAN: There's also-- we probably shouldn't do this in this case, but there's also a way you can put a bottom in the AppBar. And you put a tab strip there or something like that. There's a bunch of other features on there. FILIP: Yeah, this is great. So, SliverAppBar. And if you look at the implementation, let me see if you can get to build, right. IAN: So SliverAppBar uses a Sliver persistent header, which is the underlying render object to do this, except that's actually-- there's a little bit of complications around AppBars, because there's three different types of AppBars. There's the pinned, the floating, and the pinned floating, or, well, the normal or something. There's a bunch of different AppBars, and each one of those has a different render object under the hood. But we wanted to, all, expose that as one Widget, so you could just set the flags on it. Because of this, it gets a little tricky between the Widget and the render object at the bottom. In most cases, if you were to make your own RenderSliver with your own Widget to go with it, you wouldn't have that level of complexity. FILIP: Right. So this is great. So we have something that's using Slivers. We can play around with it. We can, you know, add things to it. We can see the code. IAN: One thing that would be good to show-- so, so far we've done two Slivers-- the AppBar one and the SliverList one. Both of those are Slivers on the outside, but on the inside they take boxes. Placeholder's a box, text is a box, all of these, ListTiles are boxes. So there are also Slivers that take Slivers on the outside and Slivers on the inside. The main example is SliverPadding. So for example, you could wrap the ListView or the AppBar in a SliverPadding, and that works the same-- FILIP: Like the SliverList? IAN: Yeah, you could wrap the SliverList in the SliverPadding, for example. And it works similarly to a Padding Widget in the box world. Again, because this is a Sliver, we call this a Sliver instead of a child, just to remind you that it takes a Sliver as a child. FILIP: And that padding-- IAN: And then you set the Padding, and that works the same as a regular Padding. So just an EdgeInsets. And then when you reload, you'll find that your list is indented. FILIP: Oh, nice. IAN: And the tricky thing is you can do the same to the AppBar. FILIP: OK, let's do that. IAN: It's not clear, to me, what's going to happen when you do that, because AppBars do not expect to be padded. FILIP: SliverPadding, let's break this. So Sliver and then Padding, let's do 24. IAN: Ooh, there it is. The padding doesn't want it to be floating, so we're still trying to make a SliverAppBar. The Padding doesn't know how to-- so the SliverPadding doesn't know how to float, and so it's probably adjusting the-- remember how I said we're passing constraints down and then geometry back up. The constraints get passed to the Padding. The Padding then passes the constraints to the AppBar. The AppBar is returning constraints-- the geometry that imply that it should float. The patterns are like, oh man, I don't have to do that. And so it, like, rewraps the geometry into a different sort of geometry that indents and everything and then passes that up. And that's why, here, that the AppBar is not floating. FILIP: Right. That's fine. What happens when I put something above the SliverAppBar? IAN: You try it. Put a SliverList, or you can put a SliverToBoxAdapter. That's the simplest Sliver, a SliverToBoxAdapter. It is basically just a container that is a Sliver on the outside and a box on the inside. FILIP: So let's do this. Sliver what? IAN: SliverToBoxAdapter. There you go. And then you can put whatever child you want in there. This expects a box inside of it, so you can put a Flutter logo, or a text, or whatever. FILIP: Yeah, let's do the Flutter logo. IAN: I wonder what size it will be. And then there it is. FILIP: OK, and now it's actually-- isn't it? Oh, yeah. It is trying to-- you know, the AppBar is actually trying to float over things? IAN: Yeah, the AppBar is getting really confused, because the floating AppBar doesn't expect anyone to be above it. If you remove the floating, you'll see that it's doing-- floating AppBars do not like to-- FILIP: Yeah, of course. No, I just wanted to break it in, and I didn't. So, OK. Good. IAN: The other you can do which is particularly fancy-- so if you copy your entire list above your AppBar, you can then-- I forget how this works, but you can set a center to your CustomScrollView. And then half of your list will be above the center, and half of it will be below the center. So right now, you just have a list followed by the AppBar, followed by the list. But if we look at CustomScrollView's documentation, there's a way to set, like, the center. I forget how this works. Oh, maybe we never exposed this on the Widget. Maybe this is only exposed in the render object. Never mind, false alarm. The underlying logic knows how to have double-ended lists that are infinite in both directions and have a center in the middle, like the AppBar, for example. But apparently, we never exposed it. It will be a feature for next year. FILIP: OK, so all right, I think we covered SliverAppBar and, like, the crazy things that you can do with Slivers. Let's create our own Sliver. IAN: Ooh. FILIP: Yes. Where is the-- do you want, like-- like, for example, here, do you think between these two-- between the AppBar and the List, can we put, like, a weird Sliver? IAN: Sure, we can do whatever we want anywhere we want. That's the beauty of this composition approach is you can mix it up. FILIP: I just mean like in terms of showing stuff off? IAN: Yeah, it's OK. FILIP: Yeah. OK, so would I do Sliver? IAN: So we have to create our own render object first if you want to create your own Sliver, so we need to create a new render object class. FILIP: What's it going to be called? IAN: Sliver Filip. I'm not sure whatever you want to call it. And it's going to extend Sliver-- sorry, you're going to extend RenderSliver. Extend the RenderSliver. Now I have to admit, at this point, I very quickly-- it should just be called RenderSliver. Oh, you might need to import the Rendering Library. Now at this point, I rapidly forget how any of this works, and every time I make my own render object, I always look it up. FILIP: We can read documentation. It's OK. IAN: So the first thing to do-- we need to look at the RenderSliver docs, and those are pretty extensive. And they were written specifically, so that I would remember how to write more RenderSlivers later. So the first part of this talks about how the protocol works-- [INTERPOSING VOICES] FILIP: We saw this before, yes. IAN: And then there's a subsection. The subsection is specifically about writing a RenderSliver sub class, so there's several kinds of Slivers you can do. We've talked about them so far. FILIP: Just making [INAUDIBLE] sorry. IAN: There are Sliver to Slivers, right. So you have a Sliver on the outside, a Sliver on the inside like SliverPadding. There is Sliver to Box, where you have a Sliver on the outside, but a box, like-- a Sliver to a Box Adapter, for example, or the AppBar. There's Sliver to many Slivers, like SliverList or SliverGrid. And I suppose, in principle, you could do something that isn't a Sliver on the outside, and there's a Sliver on the inside. That's basically what a Viewport is. A custom ScrollView, under the hood, uses a Viewport to actually do all the Sliver logic. And it has to adapt from a box, because on the outside we're using boxes, to Slivers on the inside. And that's what a Viewport is. So the easiest one to do is a SliverToBox Sliver. FILIP: Is it something that I can render, SliverToBox? IAN: Yeah, we could just copy and paste, render the SliverToBoxAdapter and then adjust that one, if you want. That's what I would normally do. FILIP: OK, let's do this. What is it? IAN: You want SliverToBoxAdapter. I think there is a link just there. [INAUDIBLE] FILIP: OK, and I just do this? [INTERPOSING VOICES] IAN: So yeah, and the performLayout is how-- basically, all render objects have a Layout method, and then they have a Paint method. And in the case of Slivers, there's a few other methods you have to implement, but we'll talk about those later. So first, we have this-- sorry, I just need to-- FILIP: --structure. IAN: And in this case, you want it to take a single box child, so you'll want to also create a-- you'll want to change the declaration, so it's mixing in one of the other classes. If you look at what the other one is, you'll notice it's mixing in RenderSliverSingleBoxAdaptor. And that is a nice mix in. FILIP: It's actually extending it. IAN: Oh, is it extending it? Oh, even better. So that's a render object that knows how to deal with having a single child. It does things like adding and removing a child. FILIP: So mostly, you would want that, or like-- well, in our case, we would just want that. IAN: It really depends on the effect you're trying to get. FILIP: Right, right. IAN: It's hard to predict what effect. FILIP: And then I just Copy, Paste. That's my favorite way to implement anything. So if I can-- IAN: So in this case, this class actually doesn't have a paint method, because the superclass automatically paints the child at 0, 0, which is the easiest way to do the Sliver work. FILIP: So do we care? Let's-- OK, yeah. IAN: This is perfectly reasonable. So if you use this one now, you'll find-- we still have to make a Widget to wrap the-- we really should call this RenderSliverFilip, because this is our render object. And then we'll create a-- we'll create a separate class called SliverFilip, which is our-- FILIP: Stateless? IAN: Nope, it's actually a RenderObjectWidget. FILIP: Right. All right. IAN: So we can-- you know, class SliverFilip extends-- I believe it will extend a single child. No, none of those. Let's look it up again. Look up the children of a RenderObjectWidget-- the subclasses of RenderObjectWidget. If you look up RenderObjectWidget in the docs, you'll find the list of all the subclasses. So up here where it says Implementers, you'll see all the different Implementers. And the one that we care about is SingleChildRenderObjectWidget. There you go. FILIP: So that's what we-- yeah. IAN: This again, much like the other superclass we're using, knows how to handle having a child. It doesn't really care exactly how you deal with it. It just knows that-- yeah. And then this has to return a RenderSliverFilip in the Constructor. There we go. And then you don't have any arguments except the child, and the child is dealt with by the SingleChildRenderObjectWidget, so we don't have to worry about that. We do have to add a Constructor to take our child. FILIP: No? OK. IAN: And we normally will also take a key. So normally we'll have two named arguments, a Key of type key and a Child of type Widget. FILIP: OK, so Widget child and Key key. IAN: And I think both of those can be passed to the superclass. Although, it can-- this is where I look it up every time. FILIP: Yeah. IAN: Excellent. FILIP: Wait, is it? Yes. OK. IAN: And then normally, we would also have a UpdateRender object, but we don't have anything to update because we don't have any arguments right now. So there's nothing to do there. So now, if we try adding that to our list, in theory, it should do the same as we did before with the Render-- with the SliverToBoxAdapter. FILIP: So we should put it here, and we can give it the child of Text hi, or something. IAN: Yeah, let's try it. This will probably fail, because writing code never passes the first time. FILIP: Yeah, and also I don't know what's going on with my computer, but it's just froze again. So, sorry about that. IAN: If this happens to you, please file the bug. FILIP: Yeah, I will, but not now. Oh, hi. IAN: There you go. FILIP: OK, good. Good. This is great. So crazy things that we can do with this? IAN: So let's put something bigger in there to make it bigger. Like, we can add a container or something with some big height. And then making it big will make it much more obvious that we're doing crazy stuff when we start doing crazy stuff. FILIP: Right. So let's do some colors like red, of course, and then in it there should be what? Text. IAN: And then probably, also, you'd give it a height. FILIP: Right. And height of 50? IAN: Sure. How big is that? Let's make it 150. OK. So that will give us more space to play with it. All right. So then if we look at the RenderObject-- if we look at the RenderObject, now you'll notice somewhere we're returning a geometry. So when we don't have a child, we return zero. And SliverGeometry.zero is a constant instance of SliverGeometry that just has all the numbers that are zero, because there's nothing to show. FILIP: So like if-- OK, so if I do this, this will always just not exist? IAN: Yeah, there you go. FILIP: I mean, it's like one-- OK. IAN: And as you see, it doesn't show anything. FILIP: Yes. IAN: But we have a child, so we don't want to do that. The next thing we do is we lay out the child. Now the Sliver constraints object has an asBoxConstraints getter method. You can see it there. And we use that to very quickly turn the Sliver constraints into the sort of equivalent box constraints. It's usually the box constraints you want in this kind of single child case. And what it does is it looks at what the cross extent is, the cross axis extent, and that's going to be the width. And it figures out how much room you have, which typically will be infinite in the height direction. It will make that the max extent in the height, but it knows about the axis direction. It knows about the growth direction. It knows about all the different things, and so it picks the right ones. So if you actually had a horizontal list, it would use the height as the cross axis and the width as the normal axis itself. It's a relatively simple method, but it's so convenient when you're doing this kind of stuff. So then next, we have to figure out what we want to do. So we don't have the equivalent for going the other way to find out what the child extent-- that's the height of the child if you are a vertical list, or the width of a child if you are horizontal list. And so, we have this switch here that does that for us. It figures out the child extent, based on which axis the list is in. FILIP: So in our case, it's going to be the height. IAN: Yeah, but you could easily switch your CustomScrollView to be a horizontal CustomScrollView. We should try that. What happens if you do that? Yeah, it had an axis argument here that says-- is it axis direction or something along those lines? scrollDirection. FILIP: Oh, scrollDirection. Yes. IAN: And it takes and axis direction, and I think there's like four different-- oh, it takes an axis. Yeah, there you go. FILIP: Right, yeah. This is cool. We can have, like, AppBar on the left. IAN: But notice how now our container is-- FILIP: --is correct. IAN: It's correct. I mean, like, it's all worked. And that's because of that switch statement, where we're looking at the width instead of the height. The Sliver-- the code here, that we are setting the height to 150, that's being ignored. Because the height is forced right at the height of the viewport. And we didn't set a width, so we just shrink. So that's what that switch statement is doing. After that, we're then trying to figure out, OK, what is the size of the child, essentially, in terms of the Sliver geometry? So this is where you start calling these convenience methods. FILIP: Where are they? Is that the Sliver? OK. IAN: Yeah, the Sliver. I think it might be on the RenderSliver helper class. There's a class that has a bunch of these methods that help you do these computations, but you don't have to use these. So we can delete those entirely, and we'll see what happens when we put in our own numbers. And we can remove the asserts, as well, because don't want to try and explain why each of these things has to be true, necessarily, right now. So then here's the geometry, and we can place, into these numbers, whatever we want. So the first one that really matters is the paintExtent. I believe that's the only one you actually have to get. We should have a look at the SliverGeometry constructor and see which ones it really wants you to give. In fact, it has defaults for pretty much everything, so you don't really have to-- FILIP: Yeah, it looks like it. IAN: A lot of them default to paintExtent, so like layoutExtent, I believe will automatically use the paintExtent, if you gave it. layoutExtent is how far down the next child will be. paintExtent is how much you're going to paint. There's also testExtent. hitTestExtent, which is how big you are for taps. So you can make yourself bigger than you're going to paint, so that you'll tap in an area where you're not painting, and so on, and so forth. FILIP: What's cacheExtent? IAN: So cacheExtent. So the lists will render-- I mean, we can go to the whiteboard. FILIP: Yeah. IAN: So the lists will render what's on the screen, right. I mentioned earlier that they're lazy. So we have this is what's on the screen, and we have a bunch of items on the screen. But the user scrolls. And sometimes, like say these things have an image like an avatar or something. If the user scrolls, we're going to very quickly need to have this image and this image ready. And so, instead what we do, instead of just rendering it just at the last minute, is we actually have a few pixels-- I forget how many. Like, 200 or something like that-- we have 200 pixels or something like that of content below the list and also above the list, that we have pre-rendered, and we're ready to go. We also use this for accessibility. So on iOS, in particular, if this is selected with the accessibility tools, and you say go to the next one, we need to know where it is, right. On Android, the API, you can just say, oh, I don't have it yet. Come tell me and scroll, then I'll tell you what it is. But on iOS, you need to have it already. And so, when we scroll to the next one, we have it already, because we pre-computed where it would be. And that's what the cache is. And so, in the API here, you'll see on the constraints, and you'll see on the geometry, that there's mentions of the cache. Now you don't actually have to implement any of this, and if you just leave it off you'll find it defaults to reasonable values. FILIP: Right. IAN: In our case, it really doesn't matter, because we have a single child. We're always going to lay it out, because otherwise what would we do? And so, we can just ignore all that cache there. FILIP: But like, what would you put there? Is it like something's bigger than-- like a bigger number or-- IAN: Yeah, so the cache extent tells the system how much have you cached. So for example, say your list is actually like this short, right. So you've reached the end of your list. Your list goes up to here, but then you reach the end of your list. There's nothing else to pre-cache, because you don't have any more content. And so, your cacheExtent wouldn't extend past here. Whereas if you do have content, and you have a bunch of content, you can say, oh, I've cached everything up to here. And that's what the cacheExtent is about, if I remember correctly. FILIP: Right. So it looks like we don't need this. We might not need this, because it's a child that we're just-- IAN: paintExtent, I think is the one that we do need to give. FILIP: OK. IAN: Because otherwise, it'll be zero, and if your paintExtent is zero, then we're not showing anything. FILIP: So I can just do, like, OK, 75. IAN: Yeah. FILIP: And then this one, hitTestExtent? IAN: We can get rid of that one. maxPaintExtent is how much-- what is the maximum we could ever paint? FILIP: Right. OK. And then-- IAN: And I believe that also defaults to paintExtent. FILIP: Let's see. I just want to-- IAN: No, maybe not. Yeah, the ones that default are layoutExtent, hitTestExtent, and cacheExtent. So you do need to give a maxPaintExtent. FILIP: And then is hasVisualOverflow. IAN: Yeah, that one, you don't have to worry about. That's about making certain optimizations work later. FILIP: Oh, cool. Wait. Did it happen? What if I do 50? Oh. IAN: So here you can see one of the first weird effects that we're getting. So notice how, when we got to the top of the screen, it didn't go off straight away? FILIP: Yeah. IAN: Or the next list didn't go straight away. That's because the paintExtent is being fixed here, so we're always painting 50 regardless of where we are. FILIP: Also, it covers the-- oh, because-- IAN: So we're always taking as much-- we're always scrolling as much as the child, so we're always taking up as much of the scroll, if you like, as the child. But we're always painting 50. FILIP: So, OK. So what we can do is like this, and then-- that bug again. See? IAN: Yeah, that's weird. FILIP: OK, so we'll-- what can we do? Like, can we, for example-- something that really makes it clear that this is done on basically every frame for us, and we can do whatever-- can we, like-- you know, if the scrollOffset-- which we get, right? If the scrollOffset is even, then we-- IAN: Yeah, we could totally do that. So when you look up the-- you're given the constraints as input here-- the area of constraints with axis-- so you can use anything on the Constraints object to determine what the geometry will be. FILIP: OK. So, constraints. IAN: And then you'll see we have a number of features. So scrollOffset is one of them. scrollOffset is the distance from the top of your Sliver-- not the viewport, the top of your Sliver-- to the top of the first thing on the screen that is visible. So right now, it's zero, right, because the text is the first thing inside the Widget that's visible, and it also happens to be at the top of your Widget. But if you scroll your Widget halfway off the screen, then the scrollOffset will be the distance from where the box would be scrolled off to the top of the screen. FILIP: How do I-- so that's like-- now that would be changing. IAN: Yeah, so now it's changing [INAUDIBLE].. FILIP: OK. Do I ever know, like, where I am right now in the, you know-- I just want-- I just want to make changes even now. IAN: Oh, I see. So we actually just, like, yesterday, added this feature. I don't know if the version you have has it. FILIP: No. If it's not there yet, that's fine. I was just-- IAN: We literally just added this feature. It may not have landed yet. But yes, we were looking at adding that. It's useful for things. Like on iOS, there are some weird-- what the special specific effect we were trying to do? I forget what it was we needed it for, but yeah, we've just added this feature. FILIP: All right, so so I think I can just do-- so what doubles do I need to-- can I-- IAN: So scrollOffset is a good one to base things on. FILIP: And then I can do-- oh, yeah. toInt isOdd. This will be weird. IAN: It'll be very weird. FILIP: And then, what can we do? IAN: So you could, for example, change how much has scrolled, based on whether it's odd. FILIP: So like-- yeah, so if it's odd, then 50, and otherwise 100? IAN: That's going to be very strange, but let's try it. [INAUDIBLE] try it. It just had the next child is jumping. So the reason the next child is jumping is, when you're at an odd scrollOffset, your next child is 50 pixels farther down, but you still have-- you've consumed 100 pixels, basically, of the scrollOffset. And so, the next child has already scrolled 50 more pixels. Whereas when you're even, you've consumed 100 pixels of the scrollOffset even though you're only painting 50 pixels. FILIP: So what happens here if I say maxPaintExtent? Again, a weird bug that I haven't seen before, so I don't know what's going on. If I use this, this should just not change the ScrollExtent, but change the actual-- IAN: I'm actually not sure what this will do. [INAUDIBLE] Yeah. So if we look at the documentation for a SliverGeometry, we can look at-- see exactly what-- FILIP: Let's go to the actual docs. IAN: We can see exactly what maxPaintExtent does. So a lot of the things on SliverGeometry-- SliverGeometry-- a lot of the things on SliverGeometry are used for very specific cases, and so, often, you won't see a direct effect. But if we look at the maxPaintExtent and then look at the full documentation, the estimated total paint extent. Yes, so this is actually only used for shrink wrapping. That's right. So the CustomScrollView has a flag, that you can set on it, that will shrink wrap. FILIP: What's shrink wrap? IAN: Shrink wrap means it will only be as high as its contents. This is very expensive, because it means that you have to measure everything in the ScrollView. So it can-- and it can only do it in the scroll axis directions, so if we're vertically scrolling one, that means in the height. And the reason for that is we know to stop once we reach the height of the parent, so we don't need to actually measure everything. If we were to do shrink wrapping in the cross axis, we would literally have to measure everything in the list to figure out what the widest thing was. And that would be really expensive. In fact, it might be impossible, because the list can be infinite, so you might have to measure infinite [INAUDIBLE] just to figure it out. So this one's not interesting in our case. The ones that are interesting are often the layoutExtent and paintExtent. PaintOrigin is also a fun one. Try changing the paintOrigin and all the paintExtent. paintOrigin will decide where the child is going to start painting, relative to where it should start painting. You can remove the maxPaintExtent entirely. I think it just defaults to the paintExtent, or not. FILIP: Yeah, [INAUDIBLE] before today [INAUDIBLE] So, paintOrigin. IAN: Yeah, let's try [INAUDIBLE] paintOrigin. So you could do something really crazy. Pass in just the constraints.scrollOffset as the paintOrigin. FILIP: OK. It just-- IAN: So now it's always going to stay at the top, because as you scroll it farther off the screen it's getting pushed down more and more. That's essentially how floating AppBars work. I'm sorry, the pinned AppBars, not floating AppBars. FILIP: Can I do a negative value? IAN: Yeah, you should get really weird effects with that. FILIP: Yeah. IAN: Because now it's going twice as fast. Now try doing, like, times two instead of negative. FILIP: Oh, yeah. That's going to be interesting. I have no idea what's going on. OK, so we have times two now. I love it. OK, I think that this is [INAUDIBLE] enough for me. IAN: And so, as you fiddle with these different arguments on SliverGeometry-- particularly if you make them relative to some of the values on scrollOffset-- you know, multiplying them, adding them, stuff like that-- you can get some very odd effects. This was particular fun when I was trying to implement AppBar, and I would-- you know, I was just coming up with these things, so I didn't know exactly how they worked or anything. And I would, like, put them in. I still don't really know exactly how they work. I would put them in. Like, I think I've got the right math. And I would put in the math, and I would scroll. And then, like, the thing would go up. The AppBar would go down, and then the AppBar would shrink. Like, what is happening? So you can definitely get some very strange effects. FILIP: All right. Oh, I think that's where we end unless you have any other crazy examples, but I like that. So we've covered today-- what we've got today is many widgets that you, use like GridView, ListView, AppBars, all somehow use Slivers inside them. You don't need to understand Slivers to use them, but then if you want to do something crazy you can. And to do that, you would typically extend, somehow, this RenderSliver-- either this thing or something else. IAN: My recommendation would be literally just go to the RenderSliver docs, and the RenderSliver docs will walk you through how to build these? What parts are important? And then as you want to figure out, like, what the various constraints and geometry attributes are, go to the render-- go to the SliverConstraints and SliverGeometry docs and look at those. Like you for each one, we've attempted to describe what they actually do. If there any that don't make any sense, please file a bug, and we'll try to improve it. FILIP: All right. Well, thanks for watching. If you have any questions, use comments after the video, or I think we have a hashtag #BoringShow. And yeah, see you next time. [MUSIC PLAYING]
Info
Channel: Google Developers
Views: 75,481
Rating: undefined out of 5
Keywords: Flutter slivers, flutter sliver tutorial, Flutter sliver layouts, flutter dynamic layouts, flutter sliver scrolling, scrolling widgets, sliver scrolling widgets, Flutter samples, flutter code examples, flutter app examples, flutter demonstrations, how to use flutter, flutter tutorials, flutter tutorial, google flutter, flutter development, flutter mobile apps, mobile development flutter, developer tutorials, flutter tips, how to flutter, flutter code tips, GDS: Yes;
Id: Mz3kHQxBjGg
Channel Id: undefined
Length: 49min 0sec (2940 seconds)
Published: Sun Dec 16 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.