[MUSIC PLAYING] EMILY FORTUNA: All right. I think we're at 1:30. [NON-ENGLISH]. And welcome to Innovations. Welcome to Innovations
Done Incorrectly. ANDREW FITZ GIBBON: Incorrectly. Emily, I already know how
to do animation incorrectly. I want to learn how
to do them correctly. EMILY FORTUNA: Well, you're
giving this talk, so. ANDREW FITZ GIBBON: Right. OK. So welcome to
Animations Done Right. I'm Fitz. EMILY FORTUNA: I'm Emily. We are both developer advocates
on the Flutter team at Google. ANDREW FITZ GIBBON: Yes. So one of the
challenges with trying to figure out how to
do animations right is there's a lot of options. So I want to do an animation. Do I choose a
rotation transition? Do I choose a scale transition? Do I to choose an
animated container? What do I choose? I don't know. EMILY FORTUNA: So we are going
to be walking through two different parts of our talk. The first part is if you know
exactly what sort of animation you want to create
and you're just not sure how to implement it using
all of those different widgets, how do you choose? The second part is walking
through the thought process of creating animations and
adding them to your app so that you can know how to-- adding some
animations can really elevate the level of
professionalism of your app and how thinking of
where to add them can really clarify that process. ANDREW FITZ GIBBON: So once you
know what the animation should look like, you're on a
team with a designer, and you know exactly what you
want, what are the next steps? EMILY FORTUNA: Well, we
have a couple of questions to work your way through that. So the first question
is, is the animation you're envisioning
more like a drawing? If the answer is yes,
then we recommend you not create your animation
purely in Flutter. ANDREW FITZ GIBBON:
Are you saying I can't do a dancing penguin in
rows, and columns, and boxes? EMILY FORTUNA: That is not,
in fact, what I'm saying. I actually can create it. And I have seen people do that. But your life will
be much more pleasant if you use tools that help or
that are right for the job. And tools that are right for
the job for a more drawing-based thing are more drawing based
tools, things like things Rive, things like Loddy. So if that's the case,
then use a plugin. But if you have an animation
that is using more Flutter core layout concepts, things like
rows and columns or text styles, or like
Flutter parameters, then a pure Flutter
code-based animation is what we'll be talking about,
such as this example that we saw on [? Marcine ?] yesterday. ANDREW FITZ GIBBON: OK. So when you're doing the pure
Flutter kind of animations, there's two different
kinds of animations that we're looking at,
the implicit animations and our explicit animations. So what's the difference? Well, our explicit
animations are the things that we give it a controller
and we can tell it when to go. We can tell it how to go. We can tell it how long to go. We control what happens
when it happens, everything. On the other side, the
implicit animations we are not controlling it as much. We are giving it an end value. And it just kind of
takes care of everything. And that's our
implicit animations. And this is on our
range of easy to hard on creating our own animations. EMILY FORTUNA: It's
implicit because when it starts and the process of
getting there is defined just by setting that end value rather
than that controller saying it's time to start. ANDREW FITZ GIBBON: So we
have more questions though, because just knowing
implicit versus explicit is kind of a hard decision. And so we want to figure out
how to make that decision. So first off, EMILY FORTUNA: You
should ask yourself, does my animation
repeat forever? Is it going for the
length of your app running or is it repeating while a
certain condition is true? If the answer is
yes, then you want to use an explicit animation. The second question
you can ask yourself, is my animation discontinuous? What I mean by
that is if you look at that middle
animated circle here, you see the circle is growing
from small to large and then small to large. It never goes small,
large, large, small. So the value of circle
size is discontinuous. It's jumping from that big size
back down to the small size again. And so if that's the case,
then again explicit animation is what you would need. And then the last
question to ask yourself is, are
there multiple widgets that I'm animating together? So on the far right,
you can see there's a bunch of squares that
are kind of animating in and out in tandem. And that is done by using
an animation controller that can orchestrate how
each of those squares goes in at different points. So again, if you have multiple
widgets that are all connected, explicit animation is for you. But if you answered no
to all those questions, then you can use an
implicit animation. ANDREW FITZ GIBBON: All right. So now we've decided
whether we're using implicit versus explicit. Next question we want to ask
is, is there a built-in widget for what you want to do? And often the answer is yes. Choose a property of a widget
that you want to animate, and there's likely
a built-in for it. So whether you're rotating
it, whether changing the size, or changing your color and
opacity, all of these things already exist. And they're nice and easy
to use for the most part. And so the general idea
there is try the easiest and the simplest thing first. We don't want to
preemptively make our lives difficult by
making a custom painter that is animating every single
pixel on every single frame. That's going to be a bit hard. So try to do the
simplest thing first. EMILY FORTUNA: So we've got
this sort of difficulty scale here with the implicit
on the far end because there's fewer
things to manage. You don't have to manage
that controller object, which is separate. You have to control constructing
it and disposing it. And then you have
Tween animation builder as the next step followed by the
explicit animations and so on. ANDREW FITZ GIBBON: OK. So we've walked through
a bunch of theory, a bunch of questions. What's the next step for
actually trying to do this? EMILY FORTUNA: Right. So let us do some coding,
and apply these questions, and see how it works. So now we're moving into the
second part of our talk of also like I have an app and I
want to make it look nicer. But I'm just not
really sure how. And we're going to show
by adding only animations, not adding/changing a layout
or anything like that. You can make it
look a lot nicer. ANDREW FITZ GIBBON:
And we're using the basis of this app
as one of the apps from "The Boring Show." And this is a very
professionally done app written mostly by a puppet. EMILY FORTUNA: Not
professionally. ANDREW FITZ GIBBON: It's really
an in progress app though. And there's some rough edges. And it also doesn't have really
any animations in it already. So we thought it would
be a good candidate for adding some life to it. EMILY FORTUNA: So if
you are not familiar with "The Boring Show,"
DashCast is a podcasting app. And as Fitz mentioned, it's in
the relatively early stages, because right now you can
only listen to one podcast. But again, we're
showing how animations can take a pretty basic looking
app and make it look nicer. ANDREW FITZ GIBBON: OK. So let's start looking
through the app first. Here on the right, we
have an initial version of the app running. And you can see it's
our basic list view with a bunch of list tiles. And in fact, our
list tile is there with the text for our title
and our subtitle, et cetera. EMILY FORTUNA: And
before we go in, I just want to
check with everyone. Is our font size large enough? Shall we up it? It's good. OK. ANDREW FITZ GIBBON: OK. Great. You'll notice a couple
things throughout here. We're using our provider for
various things related to state management in this app. In particular, we're
using it for the episode status, and the playback status,
and a couple other things. We'll see those come up again. FORTUNA: Yeah. Provider provides all the
information of the podcast itself and whether we've
downloaded it and someone. ANDREW FITZ GIBBON: Yep,
and speaking of downloads, we can click on a button,
and we get a nice snack bar at the bottom telling
us we're downloading. We can click on an
episode, go into it. We can play it. EMILY FORTUNA: And just
again, emphasizing, this is an app that is
a work in progress. ANDREW FITZ GIBBON: Yes. EMILY FORTUNA: Pretty basic. ANDREW FITZ GIBBON: And
so even the basic things like the play button going just
straight from a play to a pause and then straight from
pause to a play and back, like, that's all it's doing. EMILY FORTUNA: It is
playing the audio. We turned the audio down. ANDREW FITZ GIBBON: Yes. OK, so let's start
on this list page. It's kind of-- it's
just what it is. EMILY FORTUNA: Right. ANDREW FITZ GIBBON:
What should we do first? EMILY FORTUNA: So right
now when you download, the download button goes away
because you have downloaded it, but it would be nice to
know the download progress. And so you could do something
like adding a progress bar. I had the idea of adding-- first of all, once this
app becomes more fully featured, we want to-- you'll have multiple
podcasts, and you'll have multiple images
related with each podcast. So we could put an image next
to each list tile to tell you which podcast it's from. And when it is
downloading, we could start with it semitransparent
before it's downloaded. And once it's downloaded, you
can make it hopefully opaque. So we could have it
update showing your image fading into view. And this is a imprecise way
of showing download status, but the user doesn't need
to know the exact percentage of your download. You just need to see
that it is progressing. ANDREW FITZ GIBBON:
So we've kind of given us a little bit
of a starting point here already to work from. So we can see
we've already added the image there as the
background to the download button. And in fact, we've already
implemented the opacity there. Again, this is done
with one of our-- well, actually, let's get
to the questions first. EMILY FORTUNA: Well-- and OK. Yes, so in terms of asking
our questions for how we should implement this. First up, is it a drawing? ANDREW FITZ GIBBON:
No, it's just changing that opacity
layer on something. Not a drawing. EMILY FORTUNA: All right. Second up is does it repeat
or is it discontinuous? ANDREW FITZ GIBBON: No, it
just kind of goes from zero to fully opaque,
and then it's done. EMILY FORTUNA: Great. So we're in implicit
animation territory. And then the last question,
"Is there a built in for it?" ANDREW FITZ GIBBON: Yes, yes,
there is a built in for it, and that is simply
animated opacity. EMILY FORTUNA: And that
is why we've already coded this up because we want
to get to the interesting stuff where there aren't built ins. We'll live code that part. ANDREW FITZ GIBBON: Yes. EMILY FORTUNA: But
first the code for this. ANDREW FITZ GIBBON: First the
code for animator opacity. You can see here that
within this build method for the image, the leading
image of our podcast, we just have the
animated opacity taking the child, which is
the thing that it's changing the opacity of. EMILY FORTUNA: If you attended
Philip's performance talk yesterday, you may
recall about how you don't want to rebuild
all parts of your sub-tree if it's not changing. And in this case, the
only thing that's changing is that opacity overlay. The child, which is the image
is consistent the whole way through. So that's why we had
that child down below. And that part stays. This is only built once,
and then the opacity gets built as we get
download progress. ANDREW FITZ GIBBON: Yep. And so we're basing the opacity
on the download progress. And so we're doing
a little bit of math to make sure that we start at
something that's kind of there. EMILY FORTUNA: Like how do
we get that download process? ANDREW FITZ GIBBON: Ah, yes. Well, that's coming
from our provider. EMILY FORTUNA: Which
is here you can see we're consuming the
episode which gives us updates on the percent downloaded. ANDREW FITZ GIBBON: Right. And so you could look
at this and say, well, every time the episode
downloads, status is updated. Since we're using Provider,
this builder is gonna be called. And we could just set the
animated-- the opacity based on that. So why aren't we? EMILY FORTUNA: Well, we're
using a little bit of extra mass which you can
scroll down to show. Just because we're starting
with the image slightly visible so we have a default
opacity of 50% and then we just
make it so that when you're not at 85% capacity, it
doesn't show that it's 100%. And so I want to just review-- do a quick review. Animate opacity is a
implicit animation. Implicit animations usually
are in the form AnimatedFoo, where "Foo" is the property
that you are animating. In this case, we're
animating opacity. ANDREW FITZ GIBBON: Yep. And so animated opacity
is doing something for us nicely here as part of
the consumer builder which is giving us a smooth transition
between different opacity levels. And so we could just set
the opacity straight, but then it'd be
potentially disjointed. EMILY FORTUNA: Yeah, since
we don't know the granularity that consumer is giving
us updates on downloads, if we just use a
pure opacity, it could be quick jumps in
the opacity as Fitz said. All, right so we've got that. Let us now-- one
other thing we can add for animations is we're
looking at the user actions. We want to go to the next page
or to the page of the episode. ANDREW FITZ GIBBON: Yes there's
a hugely underrated widget that we could use here,
referencing last night-- EMILY FORTUNA: Some might
say the most underrated. ANDREW FITZ GIBBON:
The most, yes. And so we have this
transition here, and for every single episode,
it's about the same transition, right? It's just that default
page transition. And so we could override
the entire thing, right? EMILY FORTUNA: Yeah, so there
if you use Page Wrap Builder, you can overwrite it
and create a very custom in page animation. Another really easy
way is to add a hero. Because here, we've got
an image on the side here and the same podcast
image on the full page. So all you've got to do is
wrap that child in a hero. You specify a tag to
uniquely identify that image to connect it to the
image on the new page, so Flutter knows what
you're intimating. And then you set it on
the other page as well. And then you're good to go. So now the user
has an indication, as they are transitioning
to the new page, of the actual podcast, the
particular episode that they're going to be listening to. So it's nice to just give
another visual indication of what they're doing. ANDREW FITZ GIBBON: Yes. And speaking of
visual indications, I feel like we're not
quite done with this page yet, because when I was
watching that opacity come up for the download, I
feel like I missed it. I was doing other
things and trying to pay attention to
what we were saying, and I missed the download. EMILY FORTUNA: So
yeah, so really, when a download is complete is
what you're most interested in. And I had the idea of having a
little more of a indication on, for each episode, once
you've downloaded it, when it's complete. And the visual indication
would be making each list tile jump up just a little bit. Not anything huge, but
just a little bounce. ANDREW FITZ GIBBON: Yeah,
just a little jump up. EMILY FORTUNA: So
asking our questions. First of all, is it a drawing? No. ANDREW FITZ GIBBON: No. EMILY FORTUNA: Does it
repeat or go on forever? ANDREW FITZ GIBBON:
No, it kind of just ends once the animation is done. EMILY FORTUNA: Right. So we're still in
implicit animation world. And then, lastly,
is there a built-in for a wiggle jump animation? ANDREW FITZ GIBBON: Elevated
wiggle or something like that? Yeah, no, I don't think
there's a built-in for that. EMILY FORTUNA: So that
puts us in the realm of TweenAnimationBuilder. This is for when you have
an implicit animation, but you do not have a built-in
animation widget for the jump. So let's code that up. ANDREW FITZ GIBBON: OK. So again, we've
kind of started a-- given us, ourselves,
a starting point here. And we want to take
that entire list tile, and that's what we want to
have bounce a little bit. And we're going to wrap it in
our custom widget, which we're calling AlertWiggle,
in this case, just to give us a little wiggle
alert when things finish. So we'll wrap that, and we'll
go over to Alert Wiggle. Now, so far, Alert Wiggle
is just a stateless widget. We have our consumer there,
again, provider everywhere. But it's just
returning the child. So-- EMILY FORTUNA: Right,
so it's currently just returning that list tile. So we're not doing
any animations at all. And before we code
this up, let's think a little bit about
what we are coding. So what we need to
make this animation is we want both positive
and negative values-- so to move our widget
up a little bit, to move it down a little bit. And we want a continuous
function to get us that value so we're not randomly
jumping from new location to new location. Now, what mathematical
function might satisfy those two qualities? AUDIENCE: [INAUDIBLE] EMILY FORTUNA: Sine wave! I heard it. So if you're like me and
need a little refresher on trigonometry, a
sine wave is a function that goes from negative 1
to 1, and it starts at 0, goes positive, goes negative. And the rate at
which, or the distance between when that
up and down movement starts repeating itself is 2pi. We'll get to more
details as we go. So we're going to
use a sine wave to calculate our offset for
how much we should go up and how much we should go down. And it has the
nice added property of an easing up at that
top and easing up and down. So it's not just purely
linear to the position we want, giving it a little
bit of a more natural feel. So back to the code. ANDREW FITZ GIBBON:
All right, so we've got this nice to-do here
that's telling us what to do. And we're just going to start
with wrapping that child in a tween animation builder. So there's a couple
things happening here. We've got our duration,
which is our duration. Then-- EMILY FORTUNA: Yes, so we want-- we're having our duration
short because, again, we don't want to annoy the user. It's just a small
visual indication. And then, [? between ?]
is we're saying, what are the values that
we want to animate between? I currently have
this between 0 and 1, although I mentioned
this whole 2pi thing, so we're probably going to
change that in a minute. And then we've got the builder,
which is where the animation action happens. Currently, we're just
returning a child again, so we're just returning
that list tile. So instead, what
we want to use is we want to translate that
widget up a little bit and then move it back down. So we have that
transform widget, and what we're doing to
calculate that offset is we're just using sine. So we get this value from
[INAUDIBLE] animation builder, currently between 0 and 1. And we calculate an offset. And as you may recall, its
sized between negative 1 and 1. So we're also
multiplying it by 2, just so the wiggle is a little
bit bigger, a little bit more obvious. OK, so we've set
this, but we're not making this animation
happen in response to when the download is complete. That's when we want the
animation to happen. So to do that, we have
this consumer again to check our download status. And we're checking how
much is downloaded. And when the
download is complete, we set that n value
to the period of sine. Now, we're not using n value. So let's set n value to
that end in the tween. So as you can see, at the
class initialization-- scroll up just a little bit-- you see n value is
initialized at zero. And then, when the download
is complete, we set it to 2pi. So we do that wiggle. So let's see that in action. ANDREW FITZ GIBBON: All right,
hit the Download button, and then wait for it to finish. And then-- I did save, didn't I? AUDIENCE: [INAUDIBLE] EMILY FORTUNA: Oh, yeah. Main looks like
there's got a change. You didn't save main. Thank you. There it goes. So do we see it? Do we see the wiggle? ANDREW FITZ GIBBON:
It did a little bit. There it is. EMILY FORTUNA: OK, so-- ANDREW FITZ GIBBON: OK. So it's really subtle, though. I had a vision of it
kind of bouncing up. And to do that, we might
want to add a shadow. So I'm thinking
of what kind of-- there's actually a widget built
in that gives you a nice drop shadow, and that's
the material widget. EMILY FORTUNA: So let's go back
to our TweenAnimationBuilder. And inside that child, we're
checking-- the material has this notion of elevation. And so we're saying, when
our value is animating, when it's not the
start or end value, we give it an elevation
of three arbitrarily. All right, so now
let's see that. And we should see--
oh, there it went. ANDREW FITZ GIBBON:
There they all went. I guess they're all
finished downloading. So we get our nice
little elevation wiggle. There it goes. EMILY FORTUNA: There it is. Cool, so now we've got a
small little visual indication on there. I feel like we've done pretty
much everything we can do based on user actions on this page. ANDREW FITZ GIBBON: Correct. EMILY FORTUNA: So let's move
to the individual podcast page. ANDREW FITZ GIBBON: Right. So let's look at one of these. There's not a ton going
on here, but there's one animation we've
already implemented for us. And I mentioned that
Play/Pause earlier. Now, this one's pretty simple. I hit Play, and it does a
nice little animation over to the Pause button, and I
hit Pause, and it comes back. Very nice. So let's look at
what's going on there. I go into our player code. And here, we have
our player buttons with our Rewind, our Play,
and our Fast Forward. And here, we're just
using an animated icon. So as Emily mentioned
earlier, our implicit widgets are nicely named
AnimatedFoo, where Foo is the thing you're animating. AnimatedIcon is an
exception to this. And it's an exception
because you can see here, as the progress parameter,
we're passing an animation controller. This is one of our
indications that this is going to be an explicit animation. And so we're doing this
for the animated icon because it gives us
this nice ability to just tell it to
go forward and then tell to go backwards in order to
go from Play to Pause and Pause to Play and back again. So you can see that in
the onPressed, where we're listening
for the Play status and reversing or
forwarding as appropriate. OK, so-- EMILY FORTUNA: So
now it's playing. ANDREW FITZ GIBBON: Yes. EMILY FORTUNA: But it'd be
nice if we had some more visual indication
that it is playing, because if we don't have
the sound on very much, we don't know if it's
actually doing anything. ANDREW FITZ GIBBON: Right. And for some episodes,
they're really long. And so the progress
bar is moving, but it's moving very slowly. And I have no idea if
it's actually playing. EMILY FORTUNA: So
we thought it might be good to make an animation
kind of like audio, something suggesting sound movement,
recording sound waves. ANDREW FITZ GIBBON:
Yep, exactly. EMILY FORTUNA: And just want to
take a step back for a second. One thing I forgot to mention
is I'm finding sine everywhere in animations. You have all of these
animations are using sine. This is from the
Flutter Spinkit package. And a lot of these are using
it with explicit animations to control how the offset
of when different animations coordinate together. But yeah, sine is a good place
to start with the animations. ANDREW FITZ GIBBON:
And in fact, we went searching through the
internet for inspiration. And it's the internet, so
there's many things inspiration on there. And sure enough, our friend
the sine wave came back again. So it really is everywhere. And so I was looking
at this play page, and there's that
little gap of space in between the
description and the image. And so I thought it would be
nice to add a little indication of something playing with a
little sine wave animating to the right as the
play is happening. So what do we need? A couple things. We need to be able
to draw a sine wave, and we need to be able
to wiggle it to the right while it's playing. So we ask our questions. EMILY FORTUNA: Is it a drawing? ANDREW FITZ GIBBON:
Is it a drawing? No, it's just a sine wave. EMILY FORTUNA: Does
it repeat forever? ANDREW FITZ GIBBON:
Yeah, actually, while the podcast is
playing, it does repeat. EMILY FORTUNA: Yeah, so while
a certain condition is true, we are repeating forever. And that puts us in the
realm of explicit animations. So lastly, do we have a
built-in for making a sine wave animation? ANDREW FITZ GIBBON: So like the
wiggle to the right sine wave animation? No, we don't. So there's nothing built in. We need to create our own. And that puts us squarely into
our custom explicit animations, with either AnimatedWidget
or AnimatedBuilder. And we'll briefly note that the
difference between these two is that with
AnimatedWidget, it's a nice thing to use if you
have your animation mostly self-contained. And it can just be a
drop-in for some widget that you want to be animated. On the other side,
with AnimatedBuilder, if you're a little bit more
tied to things around it, then you probably want
to use AnimatedBuilder. OK, now I'm going to take
a brief detour into what are explicit animations. So I mentioned the
animated icon is a little bit of an exception
on our naming scheme, and that's because of
this animation controller. So when we look at
the documentation, the animation controller
is really just an animation object, right? So what is that? So an animation is really
just a progression of images that slightly change
on each one, right? So it's just little minuscule
changes on every single frame. And when we play them
back fast enough, we get a nice little movie. So we have this
animation object. The animation object
is an abstract object that has three things. It has a status, so
whether we're running, whether we're dismissed,
whether we're going backwards, whether it's
completed, et cetera. We have our value-- so our current state on that
0 to 2pi range, for example. And then a set of
listeners that we can notify when that value changes. So the animation
controller is really just an implementation of that
where we add some control. OK, so what is it
actually doing? Well, we get that control. We can tell it when to
start or when to end. And once the
animation controller receives that, it will
update the status-- because, again, that's
one of our three things in the animation. And then we wait
for a new frame. And we also call that a tick. We'll come back to that later. Once we receive a new
frame, we update the value, which is a function of our
duration, our lower and upper bound, how long it's been since
the last frame, et cetera. And then we notify
all of our listeners, and we update our status again
if we need to-- for example, if we were asked to stop or
we reached the final value. OK, so that's our
brief detour into what are explicit animations,
what brought us here. We just need to have an
animation that starts and stops on demand and moves our
wave slightly to the right. OK, I'm going to
drop into the code. Let's take a look at what that-- how we actually implement that. So if we scroll to
the top of this, we have our episode
image here, which is that top little thing there. And here, you can see the
other side of that Hero widget that we implemented
earlier, where it really is just that one line. We're saying that this
image has the same tag as the one from the
ListView, and it nicely does the transition for us. We also have this animated
opacity making another return. This is what lets us nicely
fade in or nicely fade out when the play is going. And we're tying that
to the play status. So like the other one where
we were tying to the download status, here we're just
saying, if we're playing, make it opaque. If we're not, then
it's transparent. EMILY FORTUNA: All right, but
I'm not seeing anything yet when I hit play. ANDREW FITZ GIBBON: Yeah,
so there's nothing really happening yet, so let's
drop into this Wave thing. So right now, Wave is
just a stateful widget. There's not a ton
going on there. We take the size and we move on. Within the actual state
object, we have our controller, where we create it in init
state and we dispose of it in dispose. And here, when we initialize
that animation controller, we set our duration to
whatever it needs to be. And since we're using
our sine wave again, this is where our
2pi comes back. By default, the upper
bound is just one, and to give you like a nice
percentage of the animation complete, we're
overriding that to 2pi. Then there's this
VSync parameter. So you notice
we're passing this, and we've done something special
with the state object, which is to pass it or mix in a single
ticker provider state mix-in. All right, I had to practice
saying that really fast because it's a
bit of a mouthful. But all it's doing is saying
that this object is waiting for new frames to come in. EMILY FORTUNA: So it is
when this particular widget is visual, it is
listening for new frames. And we're connecting
those two by passing it in to our controller. ANDREW FITZ GIBBON: So
remember, the controller, one of the things it's doing
is waiting for new frames. By passing this in, that's
what allows it to do that. OK, so let's check out
our build method here. This is why it's
not doing anything. We're just returning
an empty container. EMILY FORTUNA: Boring. ANDREW FITZ GIBBON:
So let's fix that. While Emily's
typing our code, we can see that we're consuming
the play status again. So we can know when to start
the animation versus not. And again, this is one
of the helpful things about the controller,
is that we can just tell it to repeat forever, and
it will just repeat forever. OK, so we've added our
animation builder here, and we've given
it the controller. Really, another way to
think about the controller is that it is a thing
that emits values. So we need something to
pick up those values, and the animation builder
is one of those things. OK. EMILY FORTUNA: So
inside our child, we have this blue
gradient, which is literally just a rectangle
that is a gradient and is blue. And then we are
clipping it to make it look like a different shape--
(WHISPERING) like a sine wave. ANDREW FITZ GIBBON:
(WHISPERING) Oh, my gosh. EMILY FORTUNA: And
how do we do it? Let's look in Wave Clipper. ANDREW FITZ GIBBON: Yes. So Wave Clipper is
a custom Clipper. And the critical thing about
it is it returns a path. And so ClipPath is
going to constrain the visible area of whatever it
is to whatever path we give it. You can see at the bottom,
the last three lines are creating that bottom
three edges of our box. EMILY FORTUNA: And the top one
is very, very tiny sine wave, if you look very closely. ANDREW FITZ GIBBON:
Yes, you have to. I can't even see it from here. You have to go real,
real close to look at it. And what we're
doing there is, path does not give us a Create
Sine Wave function. So we are manually setting
all of the individual points for the sine wave along
the top and adding it as a polygon along that edge. EMILY FORTUNA: So let's
look at that sine wave. ANDREW FITZ GIBBON: Yeah. EMILY FORTUNA:
All we're doing is we're getting the width
of our screen here, and for each point along the
width, we calculate sine. And then we make a
point and stick it in. So as I mentioned, sine
is negative 1 to 1. It's pretty small. We can make it taller by adding,
by multiplying that value-- so add amplitude. ANDREW FITZ GIBBON: OK, so
that's a little bit better, but I'm sure it's probably hard
to see still for most people, probably even in the
back of the room. I'm sure it's bigger
up there, but I can't-- I can barely see it here. EMILY FORTUNA: Yeah,
it's really skinny. So we want to make it wider. And to determine
what to do here, we're going to consult
our math books again. ANDREW FITZ GIBBON: We're
going to put our math professor hats on now. EMILY FORTUNA: So here's
the [INAUDIBLE] taller. What we want to do is,
we found that if you add a coefficient to
x that is less than 1, you make your sine wave wider. And if you multiply it by
a number larger than 1, you make it narrower,
the period shorter. So in our cases, we
want to make it wider. We're going to add something. Let's divide by 4. ANDREW FITZ GIBBON: All right,
OK so that's a little bit bitter. It's starting to look a
little like a sine wave, but the top got cut off. What else can we do with
our sine wave to fix that? EMILY FORTUNA: Yeah,
so again, if you just add a constant to whatever
value you're getting, you can shift that
sine wave up or down. So we will just add a
constant to that sum, conveniently named y offset. ANDREW FITZ GIBBON: All
right, so that moved it down. So we'll note here that computer
pixel math is a little bit different, and 0 0-- EMILY FORTUNA: A little
different from math math. ANDREW FITZ GIBBON: What? EMILY FORTUNA: Math
math coordinates are. ANDREW FITZ GIBBON: Yes,
to think-- computer math different than math math! And top left is going
to be our 0, 0, and then bottom right is going
to be our positive x, y. And so moving up in this
case is actually moving down. EMILY FORTUNA: All right,
so we've got a sine wave, but it's not moving. ANDREW FITZ GIBBON:
No, it's not moving. But I recall from when we did
our tween animation builder, we called sine wave with our
animation controller value. EMILY FORTUNA: Yeah, so we pass
in the animation controller value to our clipper, but we're
not doing anything with it. So first thing you might think
to do is just stick it in here. ANDREW FITZ GIBBON:
OK, so it's moving. It's definitely changing. It's animating now. EMILY FORTUNA: So this looks
pretty similar to that wiggle animation, probably. And then, if you think about
it, you're like, OK, well, what I'm doing is, for the
entire width of the screen, I am just calculating
the exact same value. I'm not taking
advantage of the fact that sine can be different
at different points. So again, let's consult our
math books for a minute. So if you add a factor to
before calculating sine, you can shift the sine
wave left or right. So here, we've added pi. We've shifted it to the
right a pi distance. And if you subtract, we're
moving it slightly to the left. So what we want to do is-- value is changing
for each frame, and we want to shift
the sine wave a slightly different amount at each point. And that will give
us the illusion of the sine wave moving. So we're going to add that
to what we had originally. So we've got x/4 plus value. ANDREW FITZ GIBBON: OK, so we're
moving nicely to the left now. EMILY FORTUNA: And
then, yeah, if we wanted to go to the right,
we just shift the sign. ANDREW FITZ GIBBON: Awesome. OK, so if you recall from
our original inspiration, it was kind of
sound wave-y, right? EMILY FORTUNA: Yeah,
this is way too perfect to look like speech. ANDREW FITZ GIBBON: Right,
and speech is not perfect. So let's-- EMILY FORTUNA:
[INAUDIBLE] random. ANDREW FITZ GIBBON: Right. EMILY FORTUNA: Semi-random. ANDREW FITZ GIBBON: Semi-random. So let's create
something semi-random. EMILY FORTUNA: So let's throw
out that whole sine thing. ANDREW FITZ GIBBON: Yeah,
just throw it all out. And so the first
thing we do is we set our baseline for where all
those points on that polygon are. So let's reset those
to something random so we have some
place to start from. EMILY FORTUNA: Oops, not that. ANDREW FITZ GIBBON: And
what we're going to do is, for each point along the
top edge of that container, we're going to set it to
some random number between 0 and approximately 80% of the
height of that container. EMILY FORTUNA: All
right, so we're just setting this with random
values within a certain range. Cool, so now we'd say-- I haven't hit Save
yet, but we've got a random initialized
set of values. But we're not going to
do anything with those. How do we update them? ANDREW FITZ GIBBON: Right. So if we look back at
our ClipPath function, we're still making a sine wave. Instead of making a sine wave,
let's take those random points and modulate them a little
randomly so that things move a little nicely
like a sound wave. So what we're doing
here is, again, going for each point along
the top edge of that container and getting a new random value,
but within a certain range so that we don't
get random noise. Let's get, actually, just
a little bit of a wiggle. EMILY FORTUNA:
Yeah, so we take-- we have these random
points, and we're shifting them just
a small amount so it's not complete chaos. I'm going to do-- and there you have it. We've got our points-- ANDREW FITZ GIBBON:
Nice little sine wave. EMILY FORTUNA:
--bouncing up and down. [APPLAUSE] And just to prove that you
don't want random noise, I'll just show you what that
would look like if I just take this NextDouble. I'll multiply it by the
height, size of height. Aargh. ANDREW FITZ GIBBON:
That's why we control how much it moves by. That noise is a little-- I don't feel calm by
that podcast playing. But this one I feel OK with. EMILY FORTUNA: All right, great. So we've added a
collection of animations. Let's review. So we talked a little bit
about the set of questions you need to ask yourself
to determine how to code up the animation that
you are envisioning, whether you're working
with a designer or you just know what you want. And then we talked about the
process of taking an app, and you're like, I want to
make it look nice, but how? So to review, the questions. Is your animation
like a drawing? If yes, probably use a plug-in. In fact, there's a talk
just, I think, this afternoon about using Rive. Determining whether--
so if you're determining you want to use
Flutter properly to code it up, then yeah, the next
step is, should I use implicit or explicit? And there's three questions. Does my animation
repeat forever? Is it discontinuous,
or are there multiple widgets that
are all animating that I need to coordinate together? And then, the last question
is, should I write my own, or is there a widget for it? And obviously, don't reinvent
the wheel if you don't have to. And at the bottom, we've got
our little difficulty scale of all your animation options. And then, for our app, we
added these five animations. ANDREW FITZ GIBBON: Yeah, and
so, truly, our inspiration for this was looking through
what actions the user is going to take
as part of our app and gently adding in
animations to them. So we added some
download progress and a nice little
animated opacity. We added some Heroes. We added an indication
of done, et cetera. And they're all
related almost exactly to what users are
going to do and want. And so that's
really our guidance for how to come up with
these ideas for animations. First, look at what
your users are doing. Second, emulate the physical
world when possible to make it feel like it's coming
up, it's coming down. You tap on something, it reacts. You have that sound wave to
indicate that sound is playing. And usually, generally,
subtle and short is good. So if you remember
from our alert wiggle that it lasted just
for a half a second or so, and then it was done. It was out of the way. We could have very nicely made
everything go wild and crazy and jump up and down
across the whole screen, but we kept a subtle,
and that was much nicer. Finally-- EMILY FORTUNA: Go ahead. ANDREW FITZ GIBBON:
--sine wave is everywhere. EMILY FORTUNA: It's a
good place to start. So in summary, thank you
all for your attention. If you would like to
check out the code, you can scan that QR code. Hopefully it's large enough. Or, that is the same website
that the QR code takes you to. ANDREW FITZ GIBBON: Right. EMILY FORTUNA: And we'll
happily answer questions, probably after,
because I know we don't have a lot of time left. So thank you all. [APPLAUSE] [MUSIC PLAYING]
I really enjoyed this talk in Warsaw by Emily Fortuna and Andrew Fitz Gibbon, on flutter animations. Flutter enables some great animations, for transitions of for animated UI elements and this talk gave some really great practical pointers on the best ways to approach the various use cases. It was also a good primer on the best maths to use for the various effects.