Jaime Blasco

Getting started with Slivers

Transcript

Jaime

Hello, everyone. Thank you for welcoming so nice. I'm so happy to be here, especially because I'm based in Madrid and it has been the rainiest March in history in Spain. So I think thank you for welcoming with such a nice weather. I'm Jaime Blasco. I'm a senior Flutter developer working on a startup called Hyperbolt that is based in London. We do AV chargers, so I work on the app side that connects to the charger. I'm a world developer expert in this technology and also an open-source maintainer of several packages. We are here today to talk about scrolling experiences. I hope you're already familiar to build scroll views in Flutter, maybe using a single tile scroll view. Also, like single tile scroll view, if you want to use like single slide columns to stack one item below the other. Or also list views if you want multiple items that want to improve performance, no? But today we are going to focus more about the custom scroll view. It's a widget that allows to make it more advanced scrolling experiences, but also some crazy ones, as we also usually like to do developers. The custom scroll view is composed of sliver widgets. There is a type of widget that we explain after, and what it does is modularize our scrolling view to make it more performant. So, let's compare with normal widgets. When we have a normal widget, this widget is usually defined to layout, is usually defined by his height and width. And how it works very briefly, the layout process is his parent widget tells you have these constraints. You can be this minimum or maximum width, this minimum maximum height, and then you decide and the item itself, the widget decides I want to be this width and height. For the slivers is a bit more complicated because it has an extra variable there. So it tells how much you want to be displayed in the screen and how much is hidden below the screen. So very similar we are going to have our width that's going to be cross axis extent because as we know scrolling can be vertical and horizontal. Right now we are going to always focus in this talk about vertical experiences. So it's going to be like this similarity is going to be width, it's going to be our cross axis extent, and the height is going to be always the main axis extent. But we can extrapolate it to be horizontal as well. So for the height itself, it's going to be the cross-scroll offset, how much this widget can be scrolling the screen. And then we have other properties that define our sliver, that for example, how much is painted on the screen, the paint extent, and also for example, the paint origin, where this paint starts, you know? The layout process is also very similar. We have our custom scroll view, that is our parent, and then it's going to give some constraints to the sliver. This constraint is the direction, how are you going to be scrolling, vertically or horizontally. Then our width is going to be our cross axis extent, you have to be this width, it's only one option. And then the viewport main axis extent is going to be how much you can paint on the screen at the same time. Even if it's infinite scrolling, you only can paint this viewport extent. Other properties that we have is the scrolling offset, how much of this widget is already scrolled, we cannot confuse it for the other scroll offset, that's always very confusing. And then the preceding scroll extent, that is going to be how much this scroll already happened for other slivers, how much already the scroll view has scrolled already. With this, we do the calculations and we have the geometry that will be used. We can interpolate it to be our size. So it's going to be what we're already familiar, we are going to have our scroll offset, our cross axis extent, and our paint extent, and other properties that are also very valuable, but we are not going to mention here. This seems very overwhelming, but we can simplify this as use all of our tooling that already the framework have to use simpler widgets to create our slivers. So we are going to use slivers that they already built. Let's start for the first one and the most basic one. The sliver-to-box adapter. So this sliver, what it does is translate every complexity and simplifies to use any width that we want. So we can use, for example, a container, we wrap it into a sliver-to-box adapter, and we can just build our custom scroll views with containers that are wrapped in sliver-to-box adapters. So very easy, we can just do everything we want there. And then the only thing that the user probably has currently experienced is performance. We don't want to build 10,000 Slipper-to-Box adapter because we have to build them, layout them, and paint them. We might want to also optimize it. For that, we're probably already familiar using Slipper List or Slipper Grid. And just to highlight our sliver list, I just want to say like try to use the builder method if you are already building a lot of items, like let's imagine 10,000 items. With this builder, we can just build the ones that we need for layout and appear on the screen. So from 10,000, we only maybe just build six of them and we are telling them it's a list of 10,000. If we want to optimize it even further, we have more specific sliver lists that we can use. We have the sliver fixed extent list that what it does is if we already know the size of each item, like let's for example say all of them is 64 height, we can tell him to the widget itself and then to calculate everything will be way more optimized. Let's say, for example, the sliver needs to calculate how much of the sliver is, like 10,000, 100,000. It only needs to do a multiplication of how much is item size plus the item count, and then it has the full size of the sliver itself. If we know the size of all of the widgets, but they vary depending on the index, we can use this sliver var_extent_list, but instead of a fixed number, we have a method that based on the index of the position of the item, we can tell him which size is going to be. If we don't know the size of the item itself, but we know all of them are the same, we can use the sliver prototype extent list. What it does is just uses one item, one widget that's the prototype item. Then what it does is calculate the size for all of our items using that single item. So imagine we have 100,000 items. We can just with one calculate the layout of all of them. This is very useful for real cases scenarios. Let's say for example, we have an amount of items that are list styles. Usually the list style will vary its height depending on the text size. And we all know for accessibility reasons, all of the users should be able to change the text size on settings. So with this, you can calculate and very optimistically create list styles for your users. For custom scroll views. For if we want to similar to sliver list, but we want it in a grid, we can use sliver grid. That I don't want to go very into deep, but let's say the basics, same we will be using an iter builder to just specify the items that we want to build on the screen. And then to this are the size of our items, how many columns do we want? We have two delegates, two options to the, we have to provide a delegate and there is two options, not very easy to pronounce like sliver grid delegate with fixed cross axis count. That you define the columns, the cross axis count is like, I want to four columns. and then you define the height. The height can be defined by the main axis extent, or you can define a ratio, like I want it to be square. No, I want to be the same height to be the width. So with these two options, you can define the size of your items. And then the other option is the max cross axis extent. That is like, I want to be 200 maximum each item, and then he calculates how much can it fit. So if I have 800 width, it can only fit four items. It may look very similar, but as soon as we resize the view to make it smaller, we can see the difference here. The first one still keeps the four items, but the second one is only able to fit three of them. So now that we know a bit more about how can we optimize for a large number of items, let's make it to the fun part. How can we make it look nice? How can we decorate all of this? Let's maybe drink a bit of water first. But, as you know, for Flutter we already have a lot of options to decorate our widgets as usual, but for Slivers it's a bit more complicated. Lucky for us, the Flutter team already implemented a lot of Slivers that they look very similar to our normal widgets, but hides all this complexity to make it work the same, but with a different implementation. So we have the sliver padding to add a padding, the decorated sliver, sliver visibility, opacity for transitions, for animations, and things like that. Let's focus in the decorated sliver. So, for example, for this one, we want an infinite sliver list that we want to add a background around, add some corners. Or maybe we want to add a background that is a gradient. As soon as you start scrolling, it will change the color until it ends with a different color than when it started. So for that one, we can grab our sliver list into a decorated sliver, and then we provide a decoration. It can be a shape decoration, a box decoration. We add a box decoration here, and then we add a color and a border radius. The very interesting thing about this is, even though I think we already know that we can paint decorations, like for example with a container, but we can also decide until the decorated sliver, also the container, that we want to paint this decoration on top of the sliver itself. So I think one real use case scenario here is when we want to change the color of the sliver or something like that. For example, I use it for disabled slivers. If I want to display a part that is disabled, I will use a decorated sliver that paints in the foreground, defines a color that is gray, and then use the color blend from the decoration to make it, the color blend will be saturation, and it saturates all to be black and white, the sliver. Then you grab that, for example, with a sliver, ignore pointer that disables being able to tap it, and then you have this new sliver disable that you can enable and disable for your widgets if you want. Let's continue. For a custom scroll view experience, it's very common to be able to want some toolbar or an app bar on top that want to be fixed when the user starts scrolling. The easiest way to do it will be with the sliver app bar or the Cupertino sliver app bar that are already material Cupertino versions that are already defined on how they behave. So we have an when the initials when the user hasn't started scrolling and then we have the collapse height when the user starts scrolling it will resize the app bar to be this height and then we can define if we want a pin so it stays on top as soon as the user starts scrolling or floating there is a behavior that hides when it's scrolling but when you scroll back a bit it will appear again. We have also this flexible space that it works, it resides with the expanded area, so you can define a lot of items there. You can have a background that has a parallel effects, we can have a gradient, things like that. If you wanted a bit more customized or more simple, and not like... fix some material guidelines, we can use different widgets. We have the pin sliver header that we just pass whatever widget we want and behaves like a pin bar. And then for the other behavior, we have the sliver floating header that same like it scrolls, it hides. When it scrolls back a bit, it appears. And then we have this other option, a bit more complex, a bit more customizable, that is the sliver resizing header. The sliver resizing header is the base for the sliver app bar. And what it does is the same. Like you have a maximum height when it's not scrolled. And then when it starts scrolling, it collapses to a minimum height. And then you can decide if you want it pinned, if you want it floated, or not at all. Like imagine you just want to, as soon as the view starts scrolling, you want it resized, but then disappear. We can see an example later of this. So with these three options, you can do something more simpler and more customized. Let's see, let's take it a bit farther. Like in my aim, we want to have a more complex layout that has rows and columns. Let's say for example, we want to add like an analytics dashboard that has a lot of cards, or we want this forum that has an app bar, a scrollable view, and a bar on the side, like a section on the side, or even like a checkout page that has also the checkout regime on the side, things like that, more complex behaviors. So recently for that, the Flutter team integrated this new widget that is the sliver main access group and the sliver cross access group that work very similar to column and rows. So the main axis group, let's say it's like a column, no? You can just define some items one below the other. And the nice thing about this is it allows you to modularize more your custom scroll view, wrapping several slivers together into your own stateless widgets so you can test it separately instead of having to test the custom scroll view altogether and things like that, but also allow us to use this in other different places. Like, imagine you want to add a padding to a lot of slivers, you can grab them in sliver main axis group and add the sliver padding or you can even add the decorated sliver that we mentioned before for this sliver cross axis group we will have also this this option but instead here we are going to have to provide kind of the width that we want. For that, we use very similar to roll. We want to use expanded here. You have to provide the flex. You have a flex one, flex two, and then what it does is it decides the width relative to the total flex. Like for example, here, the left one will be double the size because is also a half the number. We can also define a fixed width, like it will be using this constraint cross-axis sliver. That way you define like the max extent that we can have. It can be smaller, but you define the maximum and it will try to expand to that. So with this, combining with all of this, you can have different way multiple scenarios. You can even have a main axis group inside a cross-axis group and things like that. Then we have more layouts that are more independent, we can do more stuff about that. One that I would like to mention is the sliver field remaining, that may not be very useful but I have a trick that I'll use it a lot for that one. So the sliver field remaining is like you have a custom scroll view, but imagine like this new user only has four items, so there's some missing space below. you can use this sliver field remaining, so it will calculate how much space is left, and it will fill that space with a sliver. So that you can add an image there, some methods for the user, or something like that. I use it very often for this kind of trick. So let's imagine we have this login page, okay? We have this login page that is very nice, very small, that will have the login for the email, the password, the login button, or maybe our logo. But what happens, like we don't need scrolling here, but what happens when the phone rotates that we start needing some scrolling, even like when the person taps the email field, now that that view collapsed a bit, it's made smaller and maybe you start needing some scrolling there. So to center that view, keep that view center, but then make it scrollable, I use that sliver field remaining. What I do is like, I tell him like, inside the field scrolling, I don't have a scroll body, I don't have anything, and then I will probably overflow if it's bigger. So with these two properties, you can, in the first case, it will fill as much as possible and then you add something that is center and then in the other cases it will overflow that remaining space and will be able to be is scrollable. And the last one I want to mention here is the layout builder. So the layout builder is very similar to the layout builder for a normal widget, but this case is lever layout builder. And what it does is tell us the constraints that this widget can have. Like very similar for the normal widget will be max and min width. But for for this specific widget, we have way more properties we can play with. Like, let's for example say we want to have this animation where when you start scrolling, the view changes from opacity 0, it animates and starts appearing as opacity 1, like kind of what Apple does with their amazing UI in the websites. So for that we can use our sliver layout builder and use one of the properties of the constraints. One of the constraints is reminding paint extent and what it tells this paint extent is reminding paint extent is telling you how much space is left to paint your widget. So in case the views below, it will tell you there is no space to paint your widget. The remaining paint extent is zero. If you can paint all of your widget, the remaining paint extent will be 600. If the height is 600, it will tell you the remaining paint extent is 600. So with that, as soon as you start scrolling, the remaining paint extent will increase because you have more extent to paint your view. the using a sliver opacity, you can change that. You can send that progress to animate itself. So, a bit to recap what we can build with all of this. We have a custom scroll view that we can add slivers into it. We have the Sliver-to-Box adapter that allows us to add any widget that we want. We can optimize it for multiple items like thousands of items, infinite items, using SliverList and SliverGrid. We can also decorate all these complexity slivers using DecoratedSlivers, SliverPadding, SliverOpacity, and others that we have on the framework itself. We can also add some app bar that stays on top using the sliver app bar. The sliver resides in header, all of the ones we mentioned. We can also define more cross-axis and main-axis like columns and rows in our items and group them all together using the sliver main-axis group and cross-axis group. And then we can make it even more customizable using the sliver layout builder to make our own slivers, using other data the community provides as a sliver table view, the Cupertino pull to refresh, and things that increase the value of what we can build with it. Let's say, let's check for an example that we can do here. Like, we have this view, let's say it's a web view, that we have an app bar, then we have a section, and you can start scrolling. And then with this cross-axis group, we add a pin header on the right one, For example, here we want to resize with the resizing header when we add a table view, and then it's also a cross-axis group with two items. And then the last one, it will be the one that we mentioned that as soon as you start scrolling, it will fade in. And as you can see, we scroll back to the top with no performance problems. So, with all this, I hope you can build whatever Scrolling experience that you want. You have here the code for this demo and the link for the slides if you want. And I hope it was useful for all of you. Thank you.

Vadym

So we are opening our slider and I already see upvoted messages or questions and I'm happy to ask that. But first and first, I'm really happy you shared this topic because to be honest interviewing people nowadays I'm asking like how to build scroll in scroll in scroll and they're like oh maybe list. I was like, hmm, Slivers, no? Samson Ticks? I think the main problem here is the naming. Like, Sliver is dangerous, but custom scroll view is already super dangerous. Like, how it's like Sliver redelegated with MaxAxeStreets account, you know? It's like, how crazy. Just by reading that, it feels dangerous to use. And I think making maybe, I already suggested making it flameless names. Like, instead of custom scroll view, just call it scroll view, you know? Like, that would make it even more friendly to use. Naming is not always the best thing actually. Yeah. I know that, but people shouldn't be afraid of using tools. Anyway, we are going to the most upvoted. It's anonymous. And we are saying, why would you use sliver padding instead of padding wrapped with a sliver to box? Yes, really nice question.

Jaime

So, for example, I think the problem with padding, you cannot grab padding if you want to use a sliver list. If you want a list that has padding on the sides, you need to use sliver padding for that because otherwise... I mean, it's really bad if you use a sliver-to-box adapter with a padding and there's a list view. You should not be using list view inside a custom scroll view. So in that case, it's for when you already have a sliver and then you want to add a padding. I use it a lot for websites. If you want to dynamically adapt your view, like you want the content to be centered and add some padding, there's like maybe 12 pixels on the mobile, but then or on a computer you want to be 100 hits, it adapts the view. You want to add this sliver padding so the experience is still scrollable. The scroll bar is on the right side where it should be, but then the view is performed with your custom scroll view.

Vadym

That was pretty short answer. Yeah. Thank you. Upvoted. The most upvoted question was. Now, another one. Does Sliver come with the gain of performance?

Jaime

The main difference here is you know how much of that widget is painted, and that brings a lot of performances here. Like if you use a single-style scroll view with 1,000 items, that means to calculate what it does, Flutter builds first the 1,000 items, it layouts all of them, and it paints all of them. So it tries to paint everything. When we are talking about Slipper itself, what it tries to optimize is how many items are on the screen? How many items do I need? I need the three first ones. I have 1,000 list items. I only have to build the three of them, lay down the three of them, and paint the three of them. It's a bit more complex because imagine we can add infinite lists. It's like how flat the size for the scrollbar, which one is the end. There's so many optimizations, like for example, for a sliver list, that if you have an infinite scroll view, it will try to lay out a couple of them and it will have like a cache or something cache where you can you can decide how many offset and that offset the scroll offset of the sliver it will vary depending on how much you already scroll so as soon as you start scrolling there's so many hidden optimizations for hiding like if you want four items it's okay you cannot use a sliver like a single tile scroll view but as soon as you're using a list and you want something extra that's also very scrollable you might want to use sliver for that.

Vadym

I think I'm mostly on Slivers now. I'm Sliver dependent. I'm a Sliver fan, so. I'm Sliver dependent. Yep. Thank you. No, that's a good one. That's a good one. The previous was good one. And now they're top-voted. So it's like 9, 8, 7, and now it's 9 again. So Question from Frey, I hope I pronounced the name right. Sorry for that. If not, which approach would you recommend for scrolling to a specific item in a sliver?

Jaime

So that's a tricky one, I think. And what, so because all of this I mentioned before. some of the widgets that you already have, you don't know the position itself because maybe you want to scroll to the bottom, no? You don't know which is the bottom or you want to scroll to the last one and you don't know which one is the last, no? So you have, of course, I don't know for you, you're not familiar, you can control the scrolling position of the item using the scroll controller. And so the scroll controller, you pass it to the customer scroll view and you can tell it scroll to this position or jump to this position. So I think if it's something very easy, you can tell exactly, jump or scroll to 1000 pixels, you know? And then what you can, otherwise, like it's a bit more complex and you have to kind of optimize it. Like start scrolling with a speed, like using kind of a physics, you can tell him like, scroll to that with like scroll with this physics and then you can, you tell this physics and that physics can be like high speed until that widget appears on the screen and when you find this widget, like reduce the physics with some friction, so it stops where the position is it. But definitely it's a bit more complex if you want to like in a very complex levers to jump to that. specific to slivers. Well, I built for myself, I have something that's called sliver anchor that I try to track the position of that anchor. And then with a global key or something, I try to jump to that position itself.

Vadym

Oh, yeah, that's important. That's interesting. And that's useful for me as well. Thank you very much. And here the last question, because it's the last in the most top voted. What is the best documentation source for learning more about Slivers? And I'm done in questions here.

Jaime

I hope with this talk, you learned the basics, but if you want to go more in deep, how it really works internally, Flutter documentation is amazing, like is over, like, definitely, like, if you don't know what is sliver constraints do it, this remind painting extent, they explain very well what remind painting extent means, like, it's the space that's meaning, like, is missing to be painted, no? Like, things like that, I think we, even if we mention a lot, like, we don't mention enough how good the documentation in Flutter is, like, you can find courses how to use slivers, talks like this, but to you, to understand how it works, I think with that, like, you go to the documentation of sliver geometry, sliver constraints, custom scroll view, and the sliver render, that is called the one that takes care of all the rendering. And just by reading that, learn a lot about how everything works. Then for like real use cases, I hope this talk is more like how to use each sliver itself, but you want to read more in the internals. And I think there are a couple of sessions in Flutter Berlin last year about how to build your own custom sliver. I think that can be very useful in some cases, like for example, where I mentioned the sliver anchor, with that you can calculate the position of that item and use it for other purposes. Like in that case, you want to understand how this sliver works and how can you create your own sliver to do things like that.

Vadym

But don't say only in documentation because people won't come to the conferences. No, no, definitely. No, here we're also sharing cool things. Exactly. No, that's how like you can read documentation, but it's always nicer to make it something practical that you can understand. Like a real use case, no? Now that's cool to have interpretation from a real person who tried that, who read that, tried that and also shared all the pitfalls. So thank you very much for sharing that, Jaime. Round of applause to Jaime, please. Thank you. We can leave you back. Thank you.

Edit on GitHub