[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]