[MUSIC PLAYING] TIYA CHOWDHURY: Welcome
back to "The Boring Show." I'm Tiya, and this
is my co-host, Craig. CRAIG LABENZ: And we
are here today to kind of demystify implicitly
animated widgets. We were talking before about
what we wanted to make. And one thing that popped
into our heads was the idea-- everyone kind of knows
the animated container, the animated position. There's a ton of them, whatever
all the other ones are. And they're pretty
neat, because you just change your value from
one frame to the next, probably via
setState or whatnot. And the widget kind of evolves. It linearly interpolates
from the old value that you passed it
to the new value. And we thought, how on
Earth do those work? So we're going to build one. And at the end, we'll appreciate
how cool the animated container is. And we'll never build
another one again. We'll use the
animated container. But sometimes, actually,
all kidding aside, you may find a thing that
you want to animate in one of your
widgets, and you'd like to really encapsulate it. And this pattern
can be pretty nice. So any other thoughts, Tiya? TIYA CHOWDHURY: No. I'm interested because I
haven't done much animation. So this will be quite cool. CRAIG LABENZ: All right. Well, let's get
our fingers dirty. If our keyboards are going
to get our fingers dirty, then we have other problems. All right. So we're in a fresh
Flutter create, and I'm going to do
the old initial get rid of the comments. So grab everything
and get rid of them. And then we'll allow VS
Code to reformat for us. OK. You didn't get it. TIYA CHOWDHURY: No. CRAIG LABENZ: We've de-- TIYA CHOWDHURY: [LAUGHS]
But I am following you. CRAIG LABENZ: The
[INAUDIBLE]---- wow. Really? Maybe refresh, because
that is super-busted. Anyway, so to start, let's
just draw a simple widget, and then worry about
implicitly animating it later. So we have-- TIYA CHOWDHURY: In a stateless-- stateful widget, right? CRAIG LABENZ: Right. Great question. I am unaware of any way to make
an implicitly animated widget without having it be
a stateful widget. Because it's going to
have to be able to know the difference between its
old value and its new value. It's going to have to know-- it's going to have to have
an animation controller. It's going to have to
do all those things. So yeah, it's going to have
to be a stateful widget. And it looks like you
are now synced up again. TIYA CHOWDHURY:
Yes, I am synced. CRAIG LABENZ: OK. So yeah, we're just in
the fresh Flutter create. So we're pretty much ready
to delete all the things. We do want the
scaffold, so I guess we can-- let's just
put a scaffold here. How about that? Or I mean, no. TIYA CHOWDHURY: Are
you going to put-- OK. CRAIG LABENZ: --a widget? No, we'll put a scaffold. And then we'll add
our own widget to it. So this is body, right? And should we call it-- we called the project
implicit widget, which is the incomplete name
of what we're trying to do. What should we call it? Implicit-- Widget? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: All right. TIYA CHOWDHURY: Yeah. Do you want me to clean up? CRAIG LABENZ: Sure. TIYA CHOWDHURY: I'll clean up. I've cleaned up. CRAIG LABENZ: Oh, but
now I didn't get that. Whoa, it's weird. Look. I got it on the sidebar,
but not in the main bar. Oh, I wonder if it's because
I also had unsaved changes. So I'm going to save,
and it may come back. TIYA CHOWDHURY: OK. CRAIG LABENZ: Well, this
is just working poorly-- [LAUGHTER] --unfortunately. Nice. Well, I'll delete it again. And are you seeing
the [INAUDIBLE]?? TIYA CHOWDHURY:
Should I refresh? No, I don't see anything. Should I refresh again? CRAIG LABENZ: Yeah. Wow. This is really quite poor. TIYA CHOWDHURY: Oh, no. CRAIG LABENZ: We'll
figure it out. OK. So we're going to make
a new stateful widget. Stateful, stateful--
there it is. And it's called ImplicitWidget. All right? Oh, are we-- pubspec. I'm surely on the new stuff. So this should be
Dart at least 17. I'm actually just going to
set this back to 17 for now. That's all I'm
willing to commit to. There we go. And this means we can use
super parameters, which is very exciting. So this will be just super.key,
and we can get rid of all this. How did I do that? TIYA CHOWDHURY:
That's a lot cleaner. CRAIG LABENZ: Right? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: That's what
the Dart folks thought. That's why they made it. I'm not going to worry about
this const lint, because we're about to bust that
completely wide open. Next, the things that
we want to-- oh, let's maybe center this. And then we'll build as
soon as we decide what we want to implicitly animate. So the animated
container is probably the most common implicitly
animated widget out there. Why don't we just make
the lowest-budget animated container of all time? TIYA CHOWDHURY: Container, yep. [LAUGHS] CRAIG LABENZ: All right. So ultimately-- oh, this
already returns a container. Brilliant. What properties do we want
to animate on our container? TIYA CHOWDHURY:
Probably the color. CRAIG LABENZ: That was coming
to mind for me as well. TIYA CHOWDHURY: OK. CRAIG LABENZ: So we'll
say final Color color. And what if we did
the name of the color? TIYA CHOWDHURY: OK. So the text? CRAIG LABENZ: Yeah, the text. We could have a text widget. And one thing that
is kind of fun-- it's very common to use
animation controllers for the kind of expected
things where your mind always goes-- color, border radius,
elevation, whatever else. But you can animate anything. You can animate strings
from one value to the next. And I think that would be fun. So let's have another parameter
that is not text, but a string. And it's a color name. And then here we'll
say required this.color and required this.colorName. So we haven't done
anything with our-- oh, let's give this some size,
I guess, so we can see it. So let's say size-- that's not even close to it-- height 200, width 200. And then its child
will be probably another Center widget with a
Text widget showing our color name. TIYA CHOWDHURY: Will it respond
to something, like a user? CRAIG LABENZ: Oh, yeah. We are going to have to
have a way to switch. What? String color-- oh,
it's widget.color and widget.colorName.
widget.colorName. And then the container will
have a background color, which is just that color. And that's also widget.color. OK. What is this? How did we already
do five things wrong? All right. We do owe it these
new parameters. So just to show
ourselves that we've written something that
renders, what color do we want to start with? TIYA CHOWDHURY: Blue. CRAIG LABENZ: A classic. An oldie, but a goodie. And then colorName will be Blue. And again, OK, fine,
we'll just say const. So let's build this. And we're on macOS. That's good. That's what I
always like to use. Start debugging. OK. So while this spins up,
we're going to need-- you mentioned a way to tell the
app that we're changing states. TIYA CHOWDHURY: Yep. Or would like to. CRAIG LABENZ: How do
you want to do that? TIYA CHOWDHURY: So
typically, you'd want to tap something, maybe. CRAIG LABENZ: OK. So a gesture detector? TIYA CHOWDHURY: OK. CRAIG LABENZ: Around
the widget itself? TIYA CHOWDHURY: OK, makes sense. CRAIG LABENZ: So I think
we should put it up here. Yeah. This gesture detector is
going to take a function. It makes me suspect-- oh, how timely. But there it is. It makes me suspect that this-- I don't know if
this is going to be able to stay stateless forever. But for now, we'll say
GestureDetector NU. Detector. TIYA CHOWDHURY: Oh, I
see why it can't be, because you'll need to know
what to pass it if it's changed. CRAIG LABENZ: Right. TIYA CHOWDHURY: Right? CRAIG LABENZ: Yeah. TIYA CHOWDHURY: OK. CRAIG LABENZ: So onTap
is going to do something. And then this is maybe-- can you not be const-ified,
because your constitution does not allow for this? So const. OK. So this seems good. We're going to need to-- so the whole point of an
implicitly animated widget is that-- and by the way, is
it working for you right now? TIYA CHOWDHURY: It isn't. CRAIG LABENZ: It isn't? So for you to drive,
you'd need to stand here. TIYA CHOWDHURY: Yes. CRAIG LABENZ: What a travesty. TIYA CHOWDHURY: So
you are driving. [LAUGHTER] CRAIG LABENZ: The plan was
to pass it back and forth. But you'll be
brainstorming with me. TIYA CHOWDHURY: Right. CRAIG LABENZ: So the whole
point of an implicitly animated widget is that you
pass a new value and it animates
to the new value. So we're going to need to
pass new things here, right? So I think we can-- let's convert this
into a stateful widget. Use that handy-dandy-- TIYA CHOWDHURY: That
is very, very nice. CRAIG LABENZ: Oh, do
y'all not have that? TIYA CHOWDHURY: Well, we do. But you also know how
to type it all out. CRAIG LABENZ: Oh. Well. So now we're going to have a-- basically, what I'm
imagining for this demo is that we kind of
have a couple states that we toggle between when
we use this gesture detector. And then it will
maybe move an index. So we could have
final List of Colors. That will be colors. And that will equal-- we already said Colors.blue
is where we start. And then let's just
say Colors dot-- what's not blue? TIYA CHOWDHURY: Red! CRAIG LABENZ: Quick,
name another color. TIYA CHOWDHURY: Red. CRAIG LABENZ: Red, OK. Whoo! So after that, then
we have our list of strings, which is going
to be the color names. And so this will be-- oh, we should give it
fancy marketing names, like Azure Sky. And then for red, it will be-- what's a good, like-- TIYA CHOWDHURY: Red is usually-- CRAIG LABENZ: What's the
name of your lipstick? What's the name of that color? TIYA CHOWDHURY: This is Icon. CRAIG LABENZ: Icon Red. Great. And then we need to know our-- yeah, I didn't mean to save. So now we need to know
our index in these lists. So this could be just
int index equals 0. And now in our app,
we pass colors.index and colorNames.index. And none of this
is const-ifiable. TIYA CHOWDHURY:
Because it now changes. CRAIG LABENZ:
Because it changes. And then if we
just do this part, then we should at least be able
to toggle it back and forth, and it will snap
from value to value. Right? So we can, in here,
call setState. And that takes a closure. And in this closure, we
will first flip the index. So index equals-- if
the index equals 0, then the new value
is 1; otherwise, 0. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: Does
that check out? TIYA CHOWDHURY: Index-- CRAIG LABENZ: It
doesn't think it does. Oh, no. I wrote it totally wrong. TIYA CHOWDHURY: Yeah,
I think you-- yeah. CRAIG LABENZ: Really, it just
couldn't have been more wrong. So if the index equals 1,
then we go to 0; otherwise, 1. OK. TIYA CHOWDHURY: Now it's good. CRAIG LABENZ: That
was really far off. TIYA CHOWDHURY: That's fine. CRAIG LABENZ: Samson. TIYA CHOWDHURY: We
got there in the end. CRAIG LABENZ: I was way off. And now we can just-- oh, that's actually it. Because we didn't have a single
variable for color and color name anymore. So we're actually done. So let's save this. And it didn't love
that we changed it to a stateful widget, so I
am expecting it to do this. And now-- oh, it
didn't like something. What we did? [INAUDIBLE] MyApp. Huh? What's going on? MyApp is not a subtype of
StatelessWidget in type cast. I'm starting over. I have no faith in the thing. Do you know why that happened? TIYA CHOWDHURY: I was going
to just scroll down your code, but I can't. I was going to see if you've
done anything [INAUDIBLE],, just converted it. But there was nothing crazy. CRAIG LABENZ: --crazy. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: Yeah. Whenever you change
a stateless widget to a stateful widget,
or vice versa-- I think it's probably not common
to go in the other direction-- it gets upset for a minute. But then you do a full
restart, and then it's happy. And it wasn't happy. You want to do the honors? You want to click it? TIYA CHOWDHURY: [LAUGHTER] CRAIG LABENZ: All right. TIYA CHOWDHURY: Wow. CRAIG LABENZ: We did it. TIYA CHOWDHURY: But that's
not very animated, is it. CRAIG LABENZ: Not very animated. Very snappy. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: So it's time. And you mentioned that you've
not done a ton with animation. TIYA CHOWDHURY: No, no. CRAIG LABENZ: Have
you done anything with your own
animation controller? TIYA CHOWDHURY: No. CRAIG LABENZ: All right. TIYA CHOWDHURY: I've played
around with it, but nothing beyond just-- CRAIG LABENZ: Well, you can
be my spotter and my Googler, because we're going to
need to look some stuff up. TIYA CHOWDHURY: OK. CRAIG LABENZ: So down here
in our implicit widget, we have a couple of
things that we need to do. First, we need an
animation controller. So in our State class, we will
create an AnimationController, and we'll call it controller. And then in our initState,
we can first initialize, controller equals
AnimationController. And it needs some things
it doesn't have yet-- duration, and access to the
clock, the real-world clock provided by Flutter. And its way to get that is
by saying with SingleTicker-- that's the one. And that now makes
our whole State class know how to keep time. So we can say vsync is this. And then we need a duration. How long should
our animation take? TIYA CHOWDHURY: 2 seconds? CRAIG LABENZ: OK. const Duration, seconds 2. Now, instead of having me
just type the whole time, do you want to keyboard
back and forth? TIYA CHOWDHURY:
Yeah, let's do that. CRAIG LABENZ: All right. So we're in the middle
of everything right now. But you can have a
turn at the wheel. TIYA CHOWDHURY: OK. Awesome. All right. So do we need to initialize
anything else here? CRAIG LABENZ: Well,
actually, the first thing-- why doesn't it like
our controller? TIYA CHOWDHURY:
--doesn't like this. Because we need to put that in. CRAIG LABENZ: Nice. Right, right, right, right. TIYA CHOWDHURY: OK. CRAIG LABENZ: OK, so we have
an animation controller. And now, the next
thing that we need-- this widget needs to know
when its current value is different than the last value. And there is just
such a lifecycle hook in the life of a
stateful widget. What is it, didWidgetUpdate? Something like that? Let's see if that gives us
any kind of autocomplete if you start typing that. TIYA CHOWDHURY: It's just-- CRAIG LABENZ: Yeah. Try just "did" to start. didWidgetUpdate? TIYA CHOWDHURY: Did
you make this up? Oh. CRAIG LABENZ: Now, one
thing I would expect is, if we have the right
name, it should be telling us we need to override it. Oh, see, now it may
be telling us-- yeah, we're overriding something
that has [INAUDIBLE].. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: That's all right. Oh, didUpdateWidget, probably. TIYA CHOWDHURY: didWidget-- oh. CRAIG LABENZ: didUpdateWidget. TIYA CHOWDHURY: OK. CRAIG LABENZ: "Did widget
update?" is a question. [LAUGHS] TIYA CHOWDHURY: didUpdateWidget. CRAIG LABENZ: Yeah. Now what's it mad about? Oh, it must call super. OK. Oh, you have the very
unfortunate situation of using someone else's
keyboard shortcuts. TIYA CHOWDHURY: This
is going to be tough. CRAIG LABENZ: Nobody--
that is tough. OK, so the parameter
that this is passed is the type of our widget. So that is a-- TIYA CHOWDHURY:
Inherited-- oh, sorry. ImplicitWidget? CRAIG LABENZ: Yes,
an ImplicitWidget. TIYA CHOWDHURY: OK. CRAIG LABENZ: So
you can grab that. And then that's called
oldWidget, as we see. TIYA CHOWDHURY: OK. CRAIG LABENZ: Right. Awesome. OK. Now, in here is where we detect
any differences, and if so, kind of push over some
starting domino which will animate the transition. TIYA CHOWDHURY: OK. CRAIG LABENZ: So the values
that we want to check, I think, are color and colorName. TIYA CHOWDHURY: --and text. OK. Oh, right, the name. CRAIG LABENZ: Oh, yeah,
what was it? colorName? TIYA CHOWDHURY:
No, you are right. It's colorName. So if oldWidget.colorName
doesn't equal to-- then the widget? CRAIG LABENZ: Widget, yeah. TIYA CHOWDHURY: OK. If I spot it correctly. Oh. Your Down key, it's not
where my Down key is. OK, sorry. CRAIG LABENZ: You really are
coding on hard mode right now-- different keyboard shortcuts,
different keyboard layout. It's impressive you're
typing anything at all. [LAUGHTER] TIYA CHOWDHURY: OK. So both of them? Do we check for both of them? CRAIG LABENZ: Oh,
that's a good question. So I think we do want to
trigger off either one. TIYA CHOWDHURY: Yep. CRAIG LABENZ: Yeah, let's-- I was wondering,
do we want to check for them in the same line? TIYA CHOWDHURY: Or
in a separate line? CRAIG LABENZ: Yeah, or in
the same "if" statement. Sorry. TIYA CHOWDHURY: Mm-hmm. CRAIG LABENZ: Because if we
do check them in the same "if" statement, then
once we get inside, we wouldn't know, necessarily,
which one changed. TIYA CHOWDHURY: Yes. CRAIG LABENZ: So we'd
have to be committed to-- TIYA CHOWDHURY:
If one changes, we will be changing the other one. CRAIG LABENZ: Yeah. And we might be interpolating
the same value to itself. Yeah. Let's keep going. TIYA CHOWDHURY: Just colorName? CRAIG LABENZ: Oh, no, I think-- yeah, let's put it
all in one line. This might present an edge
case later, but we'll find out. Nice. TIYA CHOWDHURY: OK. CRAIG LABENZ: So-- TIYA CHOWDHURY:
If that happens-- CRAIG LABENZ: If
that happens, then we need to push over some domino. We need to start something. TIYA CHOWDHURY: Make
some magic happen. CRAIG LABENZ: So we
can [INAUDIBLE] right in here, or new method. TIYA CHOWDHURY: New method
probably makes sense. OK. So let's call it animateWidget. CRAIG LABENZ: Sure. TIYA CHOWDHURY: Yeah? Not that. CRAIG LABENZ: That
was an unhelpful-- TIYA CHOWDHURY: That
was not helpful. CRAIG LABENZ: I call this
auto-incorrect, where you type the right
thing, and it's like, what if I screw it up for you? TIYA CHOWDHURY: [LAUGHS] OK. CRAIG LABENZ: OK. Yeah, nice. So in here, we're finally
ready to start the party. [LAUGHTER] So we need a-- we have our controller. And now we need a tween, right? And the tween class
is just called Tween. So let's make a new variable. I guess final tween equals. Yeah. Capital T, Tween, is the class. And then I think it has a type. You want to Command-click
on the Tween? Unless you know. TIYA CHOWDHURY: I don't, so-- CRAIG LABENZ: Go to Definition,
or Command-click any of those. Yeah. OK. So double goes here. TIYA CHOWDHURY: OK. CRAIG LABENZ: Lowercase d. And nice. Now, let's see what the
constructor on this is. Because it's a class. So now we can open
up the constructor. Oh, just beginning and end. And they are
probably T. You want to scroll down a little bit? Let's see. Yeah, T. OK, nice. So the convention here
is to say begin 0, end 1. You could have any
values, but this makes the math have no
unnecessary complication to it. TIYA CHOWDHURY: Unrelated
to our duration, then. CRAIG LABENZ: Correct. So what's going to happen
is this will animate-- this will basically
move a value-- it will start at 0. It will take 2 seconds, as
specified by our duration. And it will march its way
on up to the end value of 1. TIYA CHOWDHURY: 1. OK. CRAIG LABENZ: And
trying to remember. We might just need
to Google this. The next thing
that we write here, I can literally never
remember in my entire life. TIYA CHOWDHURY: OK, do
you want me to Google it? Shall I do it on my machine? CRAIG LABENZ: Sure. Yeah, sure. I can also potentially Google,
so they can see the results. TIYA CHOWDHURY: OK, fine. CRAIG LABENZ: So we need-- this is from months ago. We need to look up
Flutter animation. Let's see. Tween, animation. Animations tutorial. That seems promising. TIYA CHOWDHURY: Oh, yes. CRAIG LABENZ: So
searching here for Tween. Tween.animate, OK. So we got this far. That was pretty good. Oh, we skipped a step, though. Oh, IntTween? We don't want that. Huh? TIYA CHOWDHURY: Is this
telling what type of animation? CRAIG LABENZ: CurvedAnimation. Creates a curve value, which it
looks like it gets passed here. Sorry, what was the
last thing you said? TIYA CHOWDHURY: Is that telling
it what type of animation it should be doing? CRAIG LABENZ: Yeah. TIYA CHOWDHURY: Is
that what that line is? CRAIG LABENZ: Oh, I bet IntTween
is just a tween with type int. Probably. That's what I'm
going to tell myself. So I think if we copy this
and come back here-- and then, do you want to keep driving? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: OK. I guess you could have
just Googled it, then. We didn't even need
to pass it back. TIYA CHOWDHURY: That's OK. CRAIG LABENZ: So
we said IntTween, but we already made our tween. So I think line 88, yeah,
that's a little no good. But we need to call
this animate method. TIYA CHOWDHURY: So can we
just call that on there? CRAIG LABENZ: Yeah, I think so. TIYA CHOWDHURY: OK. CRAIG LABENZ: And then the curve
variable is what was passed in. TIYA CHOWDHURY: Ah, so
we want to go above that. CRAIG LABENZ: Yeah, sure. Yeah. Yeah, nice. OK. All right. Now we need a listener. There's two more
things we need to do. And on one of them,
I always spend a significant amount
of time wondering why my animation doesn't start. So we need to add a listener
so that as the animation moves, we can basically call
setState incrementally. And then we need to
start the controller. And that's what I always forget. TIYA CHOWDHURY: So
add a listener to-- CRAIG LABENZ: Yeah,
tween.addListener. Yeah. Nice, nice. And now, so have you
added a listener? Have you done any
of this before? TIYA CHOWDHURY: No. CRAIG LABENZ: No? OK. So the way the kind of raw
animation controller stuff works is, we said
before that this was going to march a double
value that it keeps track of, from our starting value of
0 to our ending value of 1. It's going to take 2
seconds to get there. And then 60-odd times per
second, potentially faster on higher refresh-rate screens,
it's going to call this method. So at every step
of the way, we're not going to know how
much time has passed. That's not our job. That was the vsync. We just know some amount
of time has passed. And critically, I
forget if it's cur-- it's probably tween.value
will be our progress on the path from 0 to 1. So we're going to call
setState here and update-- why don't we just
start with the color? And we'll do the color
name in a minute. TIYA CHOWDHURY: So based
on the tween value, we'll-- CRAIG LABENZ: Yeah. TIYA CHOWDHURY: OK. CRAIG LABENZ: So are you
familiar with color.lerp and all that? TIYA CHOWDHURY: A little bit. CRAIG LABENZ: OK. TIYA CHOWDHURY: Yes. CRAIG LABENZ: So-- what
are you writing here? TIYA CHOWDHURY:
Well, I figured-- would we use tween value
to determine what color, or would we do it based on-- CRAIG LABENZ: Yeah,
yeah, the tween value. Yeah. TIYA CHOWDHURY: OK. CRAIG LABENZ: So what's your
thought on the "if" statement? TIYA CHOWDHURY:
So between 0 and-- so if it's moving from 0 and
1, so first half, a color, and then the second
half, a color? CRAIG LABENZ: Oh, I
see, I see, I see. TIYA CHOWDHURY: Is
that how it would work? CRAIG LABENZ:
Well, I think we'll have to do that for
the string name, because strings don't
have a lerp function. TIYA CHOWDHURY: Right. CRAIG LABENZ: But
yeah, for the color-- TIYA CHOWDHURY: Is it simpler? CRAIG LABENZ: Yeah. Yeah, I think so. So we'll just set
our color value. So color equals. TIYA CHOWDHURY: OK. CRAIG LABENZ: Oh, and then
we need a variable for that. Yeah, we can't set
anything to widget. So we'll need a
variable for this. TIYA CHOWDHURY: OK. CRAIG LABENZ: Nice. TIYA CHOWDHURY:
And do we set it? CRAIG LABENZ: Yeah. The initial-- oh. Yeah. TIYA CHOWDHURY: We
didn't pass anything in. CRAIG LABENZ: But widget.color. TIYA CHOWDHURY: OK. CRAIG LABENZ: That will be
the first one, naturally. TIYA CHOWDHURY: Right. CRAIG LABENZ: And this, of
course, gets only run once. So that won't be run again
as the value changes. TIYA CHOWDHURY: OK. CRAIG LABENZ: And maybe,
does that need to be late? Yeah, I think-- TIYA CHOWDHURY: Ah, thank you. CRAIG LABENZ: It was your
genius from the last time. TIYA CHOWDHURY: OK. CRAIG LABENZ: All right. So we saved our initial color. And now we're ready to
move from that color to the target every frame. So what we are setting
this to is Color-- maybe it's Colors-- Color.lerp? Oh, there it is. TIYA CHOWDHURY: Singular, yeah. So is it starting-- ending color. CRAIG LABENZ: And
percentage of the way. So our starting color is color. Except I can
already see how this is going to be
problematic, because we keep overwriting color. We'll think about
that in a moment. widget.color, that seems right. Yeah. TIYA CHOWDHURY: It's
old and then new. CRAIG LABENZ: Yes. TIYA CHOWDHURY: Yeah. OK. CRAIG LABENZ: And then this
is tween.value, I believe. Dang. TIYA CHOWDHURY: If the
squigglies go away, then you're right. CRAIG LABENZ: And they didn't. TIYA CHOWDHURY:
[LAUGHS] They didn't. CRAIG LABENZ: So, oh,
right. color.lerp-- I don't know why lerp
returns a nullable color, but we're pretty sure it will
be a real color, so yeah. TIYA CHOWDHURY: OK. CRAIG LABENZ: Nice. OK. Now, we already said-- well, first of all, do we think
this is going to work at all? Oh, no, because of
the second thing. TIYA CHOWDHURY: Go on. Go on. [LAUGHS] CRAIG LABENZ: We have
to start the controller. TIYA CHOWDHURY: Right, OK. So the forward. CRAIG LABENZ: Yeah, yeah. TIYA CHOWDHURY: I do it
outside the listener. CRAIG LABENZ: Yes. TIYA CHOWDHURY: OK. CRAIG LABENZ: So here, yeah,
it's controller.forward. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: And
then yeah, no "from." We don't care about that. OK. This looks good. You ready? TIYA CHOWDHURY: Yeah. Do you have hot reload on this? CRAIG LABENZ: Command-S. It must
be a different button for you? TIYA CHOWDHURY: Yes, Shift-R. CRAIG LABENZ: Shift-R? TIYA CHOWDHURY:
Yeah, for reload. CRAIG LABENZ: Oh, because
you go from the command line. TIYA CHOWDHURY: Yes. CRAIG LABENZ: Gotcha. All right. So yeah, let's switch
back to the UI. TIYA CHOWDHURY: No. [INAUDIBLE] CRAIG LABENZ: So I would do
three up, grab, and click it. Oh, good-- controller
has not been initialized. We may have typed for a long
time without running anything. TIYA CHOWDHURY: We have
the controller there. CRAIG LABENZ: Yeah, we did. TIYA CHOWDHURY: Should
I just reboot it? CRAIG LABENZ: Yeah, hot restart. Yeah, the green circle. That's the equivalent
of your Shift-R. TIYA CHOWDHURY: OK. CRAIG LABENZ:
Uh-oh, still wrong. What did we do? It's really quite
the stack trace. No, no. It was right. It's committed to us not
having initialized that. TIYA CHOWDHURY: OK. CRAIG LABENZ: So I guess
it's on line 28, I think. Oh, gesture detector? TIYA CHOWDHURY: 28 is here. CRAIG LABENZ: What
is it talking about? Oh, wait. And then implicit
widget, main 1. Animated widget-- oh,
I guess it's on 86. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: Controller. I really thought we
did that in initState. TIYA CHOWDHURY:
Completely perfect code. 86. So this line? CRAIG LABENZ: Yeah. So wait, will you scroll up? TIYA CHOWDHURY: Mm-hmm. CRAIG LABENZ: What, what, what? Really looks-- initialize. How? Could we put a print
statement in the init? TIYA CHOWDHURY: OK. CRAIG LABENZ: Let's just see-- OK. And then what do you think
about putting another one after the super call? I mean, the super call
can't be getting-- TIYA CHOWDHURY: Oh,
here, with init? CRAIG LABENZ: I was thinking
maybe like between 67 and 68, to see if somehow the
super call is leading to something else happening. Nice. All right. Save. We did save. Very good. No? Wait a minute. TIYA CHOWDHURY: Does
it need to be rebuilt? CRAIG LABENZ: This
time it didn't error. TIYA CHOWDHURY: It didn't error. Yeah, OK. We're good. CRAIG LABENZ: Nice. Never in doubt. [LAUGHS] TIYA CHOWDHURY: We did
write perfect code. OK. CRAIG LABENZ: All right,
so it's still very snappy. Oh, but we knew there was
the starting color thing. So if we go to our
animateWidget method, I think line 94 is like
eating its own tail. Right? Because every frame through,
we say animate from color to the target color at
a certain percentage. But that color variable
doesn't keep holding on to our initial color. In the beginning, it's blue. And then we click it, and
widget.color becomes red. And so we start animating
from blue to red. Except we overwrite the
place that's storing blue. So then soon we start
animating from purple to red. And then we start animating
from almost red to red. But really, we
were only supposed to be not that far along
in the animation yet. So we need to save the
initial color, which I think we can just do in that method. If we go back into
animateWidget, at the top, why don't we just say
starting color equals color? TIYA CHOWDHURY: OK. CRAIG LABENZ: Do
we need it up here? That's an interesting question. TIYA CHOWDHURY: Or
maybe we don't need it. At the top of this one? CRAIG LABENZ: Yeah, maybe there. I think it might be good here. Yeah, nice. TIYA CHOWDHURY: startColour? CRAIG LABENZ: Sure. TIYA CHOWDHURY: OK. CRAIG LABENZ: But boy, do we
spell that word differently. TIYA CHOWDHURY: I'm sorry. Oh, yeah. I'll re-spell it. CRAIG LABENZ: OK. TIYA CHOWDHURY: Sorry. CRAIG LABENZ: All right. So now we put that in line 94,
right in the first parameter to lerp. Does that make sense? TIYA CHOWDHURY: Yep. CRAIG LABENZ: OK. So we'll see if it works. [LAUGHS] TIYA CHOWDHURY: It
was [INAUDIBLE].. CRAIG LABENZ: So
it should be good. TIYA CHOWDHURY: OK. CRAIG LABENZ: And now
it's a three-finger up. TIYA CHOWDHURY: A
three-finger up. OK. CRAIG LABENZ: Yeah. And-- oh, lordy. Not even a little bit right. TIYA CHOWDHURY: No. CRAIG LABENZ: Ay. We need to put some print
statements in somewhere, I guess in that listener. What do you think? TIYA CHOWDHURY: In here? CRAIG LABENZ: Yeah. TIYA CHOWDHURY: OK. Inside the setState? CRAIG LABENZ: Yeah. Oh, either, I guess. Let's just maybe print
tween.value and see it work-- see it go from 0 to 1. Oh, wait. Wait, wait, wait. Can you scroll down
into the build method? Yeah, look. We use widget.color
and widget.colorName, and we made a local
variable for that. And it was just color. TIYA CHOWDHURY: So
we should just-- CRAIG LABENZ: And we haven't
done it yet for colorName. TIYA CHOWDHURY: But
that's really OK. CRAIG LABENZ: Yeah. OK. TIYA CHOWDHURY:
Is that the magic? Should we see it? CRAIG LABENZ: I think this is
going to really improve things. [LAUGHTER] Hey! TIYA CHOWDHURY: OK. CRAIG LABENZ: Let's go. TIYA CHOWDHURY: Hey. CRAIG LABENZ: Let's go. TIYA CHOWDHURY: [LAUGHS] CRAIG LABENZ: All right. Now, what happens if
you click it again? TIYA CHOWDHURY: I
clicked it too fast, and then it didn't do anything. CRAIG LABENZ: Uh-oh. Oh, it's not working at all. So the controller
needs some love. TIYA CHOWDHURY: Oh, is it
because we're not resetting it? CRAIG LABENZ: Yes. TIYA CHOWDHURY: OK. CRAIG LABENZ: Yep. So on line 98, I
think we can just make that a little smarter-- like you said, reset it first. You're totally right. TIYA CHOWDHURY:
Reset it before-- CRAIG LABENZ: Yeah,
so controller.reset. And then you probably have
to cascade both of those, because they each
return null by default. TIYA CHOWDHURY: Right. CRAIG LABENZ: All right. Let's see how that works. OK, so give it a click. TIYA CHOWDHURY: Do
you want to do it? I think I'm taking all the-- CRAIG LABENZ: [GASPS] OK. TIYA CHOWDHURY:
[LAUGHS] Go ahead. CRAIG LABENZ: I feel so honored
to run the code that you just laboriously wrote. Great. And now does it go backwards? Click again. Oh, man. Now-- TIYA CHOWDHURY: Nice. CRAIG LABENZ: Because-- I think because we save
the starting color here, what's nice about using your
own animation controllers-- and animated widgets do
this nicely as well-- but when you're
doing this yourself, you can kind of set it so
that if you only get halfway through the animation and
something yanks you back, it should behave correctly. And I think this will do it. So let's try to click, and
then click halfway through. So click, and then click again. And we're back to blue. TIYA CHOWDHURY: Yeah. OK. CRAIG LABENZ: Yeah! All right. So now we are ready
to animate the string. TIYA CHOWDHURY: Right. CRAIG LABENZ: Let's
think about this. How could we animate the string? What might we do? TIYA CHOWDHURY: You could
make the letters appear. Based on the tween value,
you can make certain letters appear and disappear, maybe. CRAIG LABENZ: Yeah. That's kind of where my brain
was going as well, almost like a cursor is
deleting the old name and then typing
out the new name. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: That
would be kind of cool. TIYA CHOWDHURY: Mm-hmm. CRAIG LABENZ: So do you
want to keep driving, or what would you like? TIYA CHOWDHURY: You can drive. CRAIG LABENZ: OK. TIYA CHOWDHURY: I feel
like I can talk at you whilst you're driving. CRAIG LABENZ: OK, great. TIYA CHOWDHURY: Do you
take the tween value and then divide it by
the number of letters? How would we know when
to remove a letter? CRAIG LABENZ: Right. TIYA CHOWDHURY: And that
could be different-- CRAIG LABENZ: We want
something like that, for sure. TIYA CHOWDHURY:
Something like that. CRAIG LABENZ: One change
that we need to make is kind of following this
pattern for color name. So we can scroll up, and
this will be colorName, which is a string. Name. And then we'll save
the initial value. We have to do that. I guess we'll just do this-- Name. Yeah, yeah. TIYA CHOWDHURY: We know
to ignore those ones. CRAIG LABENZ: Wow. I really thought that would
fix it, and it just doesn't. Huh. Odd. It's been a long day. This may be obvious
to the viewer at home. And if so, I apologize. [LAUGHTER] But we're now ready
to think about-- where the hell are we? I mean, heck. We're here. This is the one. Well, I made
scrolling look hard. TIYA CHOWDHURY: It's fine. CRAIG LABENZ: OK, so
it does the thing. And the name didn't
update, because we're not making that smarter. So, hmm-hmm-hmm. So maybe we spend half of the
time deleting the first value, and the second half of the
time typing the second value. TIYA CHOWDHURY: OK. CRAIG LABENZ: Seem reasonable? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: And this
was the "if" statement you were writing. TIYA CHOWDHURY: Right. Yeah. So it's actually
between 0 and 0.5. CRAIG LABENZ: Yes. Yes. TIYA CHOWDHURY: OK. CRAIG LABENZ: So if
tween.value is less than 0.5, then we'll do something;
otherwise, we'll do something else. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: So in here, we
are deleting the current name, so deleting the old/current-- depending on how
you look at it-- name. Now, like you said,
we kind of need to figure out how long
to spend on each letter. So is this like an integer
division situation? What do we want? TIYA CHOWDHURY:
This is a double, or at least a double, right? Or a float? I don't know. How granular does it go? CRAIG LABENZ: Oh, quite. TIYA CHOWDHURY: OK. CRAIG LABENZ: But
we could potentially just say, with
millisecond granularity, we've got 500 milliseconds. So we could divide the-- what is it? Oh, we'll need to save the
starting string, too, won't we? Final startName, or
colorName, equals colorName. TIYA CHOWDHURY:
(WHISPERING) Capital C. CRAIG LABENZ: Ooh. Nice. So now we need to figure out
how many milliseconds per thing. So we have 500. I have no idea how to
do integer division. Do you remember the
syntax for that? It's like this or something? TIYA CHOWDHURY: Yeah, yeah. CRAIG LABENZ: That's it? TIYA CHOWDHURY: Well, not that. CRAIG LABENZ: Oh. TIYA CHOWDHURY: Just slash. CRAIG LABENZ: Oh, this does
integer division by default? TIYA CHOWDHURY: Yeah,
I feel like it does. CRAIG LABENZ: I don't-- TIYA CHOWDHURY: Now you're
making me doubt myself. Do you want me to look it up? CRAIG LABENZ: I just
learned how to program. TIYA CHOWDHURY:
Oh, don't say that. CRAIG LABENZ: So 500 divided
by the startColor dot-- oh, could we make a
lerp method for strings? Is that crazy talk? TIYA CHOWDHURY: I
feel like you're going to try it anyways
if I said [INAUDIBLE].. [LAUGHTER] CRAIG LABENZ: Maybe
we spitball it here. And then if it is
coherent, then we'll wrap it up in an
extension method. That would be sweet. TIYA CHOWDHURY: OK. CRAIG LABENZ: So 500
divided by the length of startColorName.length. And this tells us how many
milliseconds per letter. Right? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: So final
milliseconds per letter that we're deleting. And then we'll figure out how
many of those have passed. So this would be
tween.value divided by milliseconds per letter. There's probably so many
better ways to do this. This is final number
deleted letters. And so now we can
setState to say-- I'm laughing because
I just have no idea if this is going
to work. colorName will equal the
startColorName.substring. And we are at 0, and then this
is the number that we deleted. So it's the length minus
the number deleted. Right? Does that compute? TIYA CHOWDHURY:
Yeah, I think so. And then minus 1. CRAIG LABENZ: Dot
length minus-- no. What am I typing? Yeah, dot length minus-- oh. TIYA CHOWDHURY: Minus 1, because
we have an index, not length. CRAIG LABENZ: numDeletedLetters. And we need another minus 1? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: You are so
many steps ahead of me, it's not even funny. Hey, what's wrong with this? Oh, numDeletedLetters
is probably a double. Yeah. So how do you do that? toInt? Excellent. Excellent, Smithers. So if this works, it
will just incrementally delete "Azure Sky,"
or whatever we said. Are you ready? TIYA CHOWDHURY: But
only one name, right? CRAIG LABENZ: Right. And it won't type
"Icon Red" yet. TIYA CHOWDHURY: OK. Fine. CRAIG LABENZ: Here we go. [GASPS] TIYA CHOWDHURY: It
removed one letter. CRAIG LABENZ: We got one letter. TIYA CHOWDHURY: OK. CRAIG LABENZ: Great. [LAUGHTER] I was so excited. I thought it was going to work. TIYA CHOWDHURY: But
we got one letter. That's pretty good. CRAIG LABENZ: What a tease. OK. What happened here? So the length, so
let's say if it was-- TIYA CHOWDHURY: Does it need
to be in a loop or something? CRAIG LABENZ: Well,
we're in the loop, because this whole thing
is in the loop, right? TIYA CHOWDHURY: OK, right. OK. CRAIG LABENZ: So let's just-- TIYA CHOWDHURY: It's
just removing one, and then stopping. CRAIG LABENZ: Print
milliseconds per letter. So let's do like this, and
then number deleted letters. And do these numbers
even make sense? So restart. Come here. Azure Sky. Click. What? So numDeletedLetters-- oh,
it's getting really small. So numDeletedLetters,
this isn't close to right. Do we just need to
divide in the other way? Milliseconds per letter. No. Why didn't this work? tween.value. Oh, we didn't
multiply it by 500. This is really going
to help, I theorize. TIYA CHOWDHURY: When the
number's this small, yes. CRAIG LABENZ: Yeah, right. So redo it. I don't know if we
needed to do that. Click. Oh, oh, oh. Oh, it stopped. TIYA CHOWDHURY: Oh, it stopped. CRAIG LABENZ: Why? Yeah, it did conclude at
the end that it only wanted to get rid of four letters. I wonder if we have a rounding
error that's adding up. But no, that would be a lot. So our milliseconds
per letter was 55. How long is "Azure Sky"? 5 for Azure, 4 for Sky
and the space, so 9. So what's 500 divided by 9? Of course, it can
math correctly. So why did-- TIYA CHOWDHURY: It
just stops at 4. CRAIG LABENZ: Where
even is the code? Yeah. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: So
what's going on here? tween.value times 500,
because this is our-- is times 500 right? tween.value-- well, if it was
too low, then it would have-- oh, it's times 1,000. We need all of the milliseconds. TIYA CHOWDHURY: --it is halfway. Because, yeah, it is
only going halfway. CRAIG LABENZ: We need
all of the milliseconds. Great. I love it. OK, very good. And come back. And now-- [CLAPPING] TIYA CHOWDHURY: Well done. CRAIG LABENZ: Let's go! TIYA CHOWDHURY:
That is very good. Very, very good. CRAIG LABENZ: And so now
it's an exercise of how much can we flip this logically, or
the back 9, as they call it? Retyping the new,
now-current name. So these parameters--
oh, this does change, because this is now
widget.colorName. This is the target name. TIYA CHOWDHURY: Yeah. OK. CRAIG LABENZ: So
widget.colorName.length. And then, so this
should be the same. But it's not deleted
letters anymore. It's now typed letters. So let's say numTypedLetters. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: And
now our substring-- TIYA CHOWDHURY: It's
going from the other-- so you'll probably swap those? CRAIG LABENZ: Well,
we're going to still type from the beginning
of the string. TIYA CHOWDHURY: Oh. OK, yes. I take it back. CRAIG LABENZ: So
startColorName also-- this isn't it. Oh, we're starting from-- I have literally no idea
what to write in this line. TIYA CHOWDHURY: So you were
deleting things from right to left, and now
you're adding things. CRAIG LABENZ: Yeah. TIYA CHOWDHURY: So we're
going from 0, and then-- CRAIG LABENZ: So we want
this to just work its way. Oh, it's just numTypedLetters. I'm going to feel really silly. And do we need a
minus 1 this time? TIYA CHOWDHURY: Do we
always need the minus 1, because of the-- CRAIG LABENZ: Don't know. TIYA CHOWDHURY: You
could try it, and then-- CRAIG LABENZ: toInt, minus 1. Why don't we use clamp? TIYA CHOWDHURY: OK. CRAIG LABENZ: Clamp at 0
and widget.colorName.length. TIYA CHOWDHURY: There you go. CRAIG LABENZ: Yeah. Then we just don't have to
care about it at all, unless we don't get the last letter. TIYA CHOWDHURY: I like that. I like that. OK. CRAIG LABENZ: Because
then we would-- but I don't think
that will happen. All right. If you were a betting
woman, what would you bet? TIYA CHOWDHURY: It's
going to work perfectly. That's exclusive, right? So it won't include the last-- CRAIG LABENZ: These
are great questions. You know the right
questions to ask. Quick delete. Oh, very wrong. Not close. So startColorName-- first of
all, that isn't what we want. Oh, I think I see what
the problem is here. Huh? Oh, yeah. We've got to get
rid of the length. So we're going to need
a minus 500 somewhere. I don't know where. So tween.value-- TIYA CHOWDHURY: Why
are we minusing 500? CRAIG LABENZ: Because
the tween.value starts at 500 milliseconds. But for us, that's 0% of the
way through the second half. TIYA CHOWDHURY: OK. CRAIG LABENZ: Right? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: So we
need to figure out how to capture that somewhere. And I think it may be
by subtracting 500, which would be like doing this. TIYA CHOWDHURY: OK. CRAIG LABENZ: Who knows? Hmm? Nope. It-- TIYA CHOWDHURY: Halfway again. CRAIG LABENZ: Oh, interesting. Yeah. All right. Let's use our noggins and
figure out what's wrong. So right away, it just
dropped in four letters. So numTypedLetters, the
first time through-- sorry. Do you want to drive? TIYA CHOWDHURY:
No, no, no, no, no. CRAIG LABENZ: OK. So numTypedLetters,
the first time through, immediately resolved to 4. TIYA CHOWDHURY: Halfway. CRAIG LABENZ: So
why did that happen? Let's do this. Oh, I was real smart, and
I got rid of those prints. No, I didn't. It's right here. OK, so come with me. Oh, we're already printing. Great. So let's do it again, and then
we won't have the other prints. That will be better. So you clear, and now print. Whoa. It started right away. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: That was odd. TIYA CHOWDHURY: It
started with 5, right? CRAIG LABENZ: I think it
was because this closure was already bound. So we are going to
have to redo things. So our update in this closure--
that's not a thing that can hot-reload, which is
quite the foot gun. TIYA CHOWDHURY: Yep. CRAIG LABENZ: OK. So it deletes, and yes,
it starts right there. So let's look at this
first print statement. TIYA CHOWDHURY: It starts at 4. CRAIG LABENZ: Yes. Why is this happening? So numTypedLetters. We said tween.value,
which at that point is going to basically
be 0.5, right? TIYA CHOWDHURY: Right. CRAIG LABENZ: Like 0.50000001. And then-- that's not the
number of milliseconds. TIYA CHOWDHURY: And you
changed that 500 to 1,000. CRAIG LABENZ: Yeah, and that
science experiment failed. That was terrible. Well, you know
what's interesting? It started right
at the end before. And then when I cut it in half,
it started halfway through. TIYA CHOWDHURY: Halfway through. CRAIG LABENZ: But why would we
not need to multiply this time? If it was such that
this would work, why would this be the case? Is this possibly the case? So the tween value
will be about 0.5. TIYA CHOWDHURY: 0.5
to 1, it travels. CRAIG LABENZ: And we'll divide
by milliseconds per letter. So 0.5 over 62 is
a very big number. So this is just totally wrong. So what number do
we need to put here? I feel like we've
bitten off a riddle. TIYA CHOWDHURY: Yeah,
and we're so close. CRAIG LABENZ: The first value
that we want it to produce is 0. So we want 0.5 times some value. I think we still want
that times 1,000. TIYA CHOWDHURY:
Yeah, I don't think that we should remove that. CRAIG LABENZ: We
shouldn't get rid of it. Yeah, yeah, yeah. So times 1,000. TIYA CHOWDHURY: What
happens when we do that? CRAIG LABENZ: I think it
started right at the end. TIYA CHOWDHURY: OK. CRAIG LABENZ: Oh, right. There's some 50% we need
to chop off somewhere. And we just haven't figured
out where to get it. So that's where we still are. So this will delete "Azure
Sky" and then start right at the end, because we didn't
account for the fact that 50-- oh, wait. Do we just subtract
0.5 from the tween before we multiply by 1,000? Because we just said we
wanted 50% of the way through to count as 0%. TIYA CHOWDHURY: To
count as 0%, yeah. CRAIG LABENZ: Through
this second animation. But then we still
want 100% of the way through the animation to
count as 100% of the way. TIYA CHOWDHURY: Yes. CRAIG LABENZ: So
we kind of say-- what on Earth do we say? So tween.value, maybe minus
0.5, because that much we-- TIYA CHOWDHURY: --have
already traveled? CRAIG LABENZ: Yeah,
that much doesn't count. So let's think about how
this would work, then. Eventually, it's
going to get to 1. So we'll say 1 minus 0.5 is
0.5, times 1,000 milliseconds per letter. I feel like it's only going
to type half the word. TIYA CHOWDHURY: Let's see? CRAIG LABENZ: Let's find out. Refresh. Rerun. Re-cry. TIYA CHOWDHURY: No. CRAIG LABENZ: What? I literally don't even know. TIYA CHOWDHURY: Don't cry. You can't cry. Well done. CRAIG LABENZ: It worked? TIYA CHOWDHURY: Yes. CRAIG LABENZ: How? TIYA CHOWDHURY: Well,
let's have a look. CRAIG LABENZ: Yeah,
so at the end value-- TIYA CHOWDHURY: That
was your minus 0.5. Is that what you
were gunning for? We needed to put it
somewhere, to work out that we were already gone-- CRAIG LABENZ: It's
because we have 500 here. TIYA CHOWDHURY: Right. OK. CRAIG LABENZ: If
we had 1,000 here, then it would have
only typed half. TIYA CHOWDHURY: OK. CRAIG LABENZ: We did it. It just clicked for me. Did it click for you yet? TIYA CHOWDHURY: Not really. CRAIG LABENZ: OK. TIYA CHOWDHURY: OK. CRAIG LABENZ: So because
the milliseconds per letter is only judged against
500 milliseconds, that means we only need to climb
all the way to 500 milliseconds to complete. TIYA CHOWDHURY:
And not to 1,000. CRAIG LABENZ: And not to 1,000. TIYA CHOWDHURY: Right, OK. CRAIG LABENZ: And now it worked. TIYA CHOWDHURY: OK. CRAIG LABENZ: And folks, that's
how you linearly interpolate a [? stress. ?] Easy-peasy. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: I think we
need to watch it again. Because oh man, that's
a juicy animation. And what's kind of fun-- TIYA CHOWDHURY: That is smooth. That is very smooth. CRAIG LABENZ: --is the curve
is involved here, right? Curved, easeOut. We can see it started typing
faster, and then slowed down. TIYA CHOWDHURY: But you
can change that, as well. CRAIG LABENZ: Yeah. What's a really nutty curve? Oh, there are some
that dip below 0 and kind of ascend above 1. TIYA CHOWDHURY: OK. CRAIG LABENZ: And that would
not make sense for strings. So we can't use those. I want a dynamic one, though. How about easeInOutQuart? TIYA CHOWDHURY: Yeah. CRAIG LABENZ: This should
be a really slow finish, or some such. [HUMS] So slow, and
then it zips around, and then it finishes, like yeah. So the curve is also
taken into account. TIYA CHOWDHURY: We
could pass that in and make it very custom. CRAIG LABENZ: Oh, right. Should we? TIYA CHOWDHURY: We could. CRAIG LABENZ: OK. Do you want to drive for that? TIYA CHOWDHURY: Sure. CRAIG LABENZ: All right. TIYA CHOWDHURY: OK. So going to go straight
back up to the top. CRAIG LABENZ: Yeah, I think. Yeah. TIYA CHOWDHURY: And
pass it in to here. CRAIG LABENZ: Yeah,
that makes sense. TIYA CHOWDHURY: Should
we make it required? CRAIG LABENZ: I feel
like we shouldn't. We should have linear
maybe be the default. TIYA CHOWDHURY: OK. Maybe we should-- CRAIG LABENZ: Yeah, Curve. Yeah, nice. So just this.curve. I don't know why I'm saying
it as if you don't know. TIYA CHOWDHURY: That's OK. CRAIG LABENZ: But we will
need to give it the default. TIYA CHOWDHURY: Yeah. OK, so do we set it here? CRAIG LABENZ: In
the constructor. TIYA CHOWDHURY: OK,
in the constructor. OK, right. Curves? CRAIG LABENZ: Yeah. Just like Color, it's
in the plural there. And then linear. Yeah, nice. All right. Now, the only other
thing we have to change is we use widget.curve. TIYA CHOWDHURY: Right. OK. CRAIG LABENZ: Which is
down in the something. TIYA CHOWDHURY: Where
are we setting-- CRAIG LABENZ: We just passed it. Line 90-something. 2. TIYA CHOWDHURY: Here. CRAIG LABENZ: Yep. TIYA CHOWDHURY: Right. CRAIG LABENZ: widget.curve. Whoo! OK. So you run it again, and we
should see a linear curve. It should just not do
anything interesting. Amazing. We did it. Ooh, click it halfway
through, and let's see the-- because we did the color. Oh, no. TIYA CHOWDHURY: That
does not look good. OK. CRAIG LABENZ: We have
[INAUDIBLE],, right? Yeah, let's see. Where did we get-- let's look at the stack trace,
see where we pass something. Oh, probably in our substring. Oh. There's probably a
fair amount of juggling to take care of in
that, which isn't a thing that I want to do. [LAUGHTER] Unless you want to. TIYA CHOWDHURY: I feel like,
yeah, we shouldn't do that. CRAIG LABENZ: But
we've demonstrated the concept of this. And now, this was highly
academic and fairly contrived, because if you wanted to do
this, you wouldn't do this. You would use-- do you
remember what it's called? TIYA CHOWDHURY: I want
to say inherited widget. CRAIG LABENZ: Or
implicit something. TIYA CHOWDHURY: Sorry, implicit. Ah, let me-- CRAIG LABENZ:
Passwords are so hard. TIYA CHOWDHURY: Yeah. Third time. CRAIG LABENZ: Impossible. But yeah, there is a class
that you can subclass. It's also what all of the
animated container, position, et cetera-- they subclass this. TIYA CHOWDHURY: Yes. Implicitly animated
widget class. CRAIG LABENZ: There it is. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: So that's
actually what you would use if you needed to do this. But I always feel
like being at peace with what is going on in those
widgets is pretty helpful. TIYA CHOWDHURY: Yeah. CRAIG LABENZ: And
also, you could always peek into that widget,
and you will very likely see an implementation far
smarter than what we just stapled together. TIYA CHOWDHURY:
It's the best way to understand how
something works. CRAIG LABENZ: Right. TIYA CHOWDHURY:
Yeah, do it yourself. CRAIG LABENZ: Well, Tiya,
any closing thoughts? TIYA CHOWDHURY: Try it
yourself, definitely. I think that I've learned loads. Thank you. CRAIG LABENZ: Amazing. Yeah, well thank you for
co-piloting nicely and braving a foreign keyboard, one of
my greatest fears in life. But folks, this-- wait. You read us in. Do you want to read us out? TIYA CHOWDHURY: Thank you
very much for joining us on "The Boring Show." [MUSIC PLAYING]