Flutter Text Scroller | Widget Workshop

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what is up fluttered Zevs damn it feels good to say that again it's been a while since I last posted a flutter live coding video I went and spent a couple years in the flutter team but now I'm back this video is the first in a brand new series that I'm calling widget workshops widget workshops are relatively brief live coding exercises where I show you how to create some specific widget or some specific visual effect in flutter these workshops will typically range from 20 minutes to an hour depending on what we're building now in today's workshop we're going to pay homage to my time working at nest we're going to create a little text scrolling widget that fades in at the top and fades out at the bottom it's inspired by a similar visual element on the home screen of the nest app that displays your address and the local weather flipping between the two we'll get into that workshop in just a moment but before we do I'd like to ask a favor I left the flutter team so that I could help show the world the power that flutter offers as a UI toolkit and to help the world adopt flutter I truly believe a world full of flutter UI's is a better world for all of us but getting that message out requires an audience and I need your help building it up I'm starting this YouTube thing all over again and I've got to build up from nothing with this channel so if you would spread the word like the videos if you enjoy them subscribe to the channel share this video with your own friends colleagues and your own audience in fact let's see how quickly we can get this channel to 10,000 subscribers I mean there are a few groups as great as the flutter community so I bet we can hit that number in no time what do you say will you help now enough about me let's get into the workshop welcome to today's widget workshop as you can see with the simulator on the screen I'm paying homage to my history working on the nest app we have a UI here that's a rough facsimile of what you would see in the nest app and we have this black box here that's waiting for us to fill it in now those of you who have never used the nest app you may have no idea what goes in that box but what we're looking to create here is a message that Scrolls like message 1 Scrolls down message 2 comes in message 2 Scrolls down message 1 comes back in and it just keeps flipping around and around so that's our goal for today the challenge here and at first you might think that's it's very easy use a column 2 items in the column animate the column up and down and maybe mask it or something the challenge here is that we want the bottom and the top of this message scrolling system to fade to the background we want the text to fade at the top and fade at the bottom and I actually don't know how to accomplish that with widgets so if any of you know please add it down in the comments but the challenge is then how do we get that fade at the top and the bottom and for me the answer is use a custom painter so that's going to be our primary approach here and that's what makes it a little more complicated than just throwing a few widgets together so with that said our goal here is to create that rotating text scroller with a fan at the top at the bottom using a custom paint so let's jump in and make it happen the first step for us is to introduce a custom paint and a custom painter in place of this container that's drawing the black box so let's go ahead and throw that in there instead of the container we want a custom paint just to make sure that it takes up all available space we can give the custom paint a size which we will make infinite which means take up all available space and then we're going to want a painter which we will call text scroll painter we will implement the two necessary methods within text scroll painter we don't care about optimizations for now so we will always repaint and now we need to ask ourselves what fundamental properties do we need to be able to paint this text that were concerned about these messages that we want to show we will need label one and we will need label two if you want to extend this creation you could have X number of labels and you can take that in as an array or something but we're going to hard-code this to two labels for now to make it easier on ourselves and we're also going to want some kind of scroll position so let's take all of these into a constructor and we'll make the labels required and we'll give it will give a default position of zero to the scroll position now back up here where we actually use the text scroll painter label one will be flutter education and label two will be flutter training both of which of course our services that I provide scroll position we will begin with zero for now until we get to a point where we can actually make use of that let's see what are we complaining about it's complaining about the infinite size so let's see what happens without that see if we're okay hmm no again needed that no I tell you what we don't okay we want to be as wide as possible but we don't want to be as tall as possible so we want a size which is infinitely wide but we actually only want to be 30 pixels tall and now we're fine so obviously we're not painting anything yet we need to actually implement this paint method but we're now configured to paint something and what is it that we're going to paint well we need to paint text we need to paint two labels one above the other and when you paint text it's a little more involved than just using a text widget you actually need a paragraph builder and then from the paragraph builder you build a paragraph and then you layout and paint the paragraph let's do that for one label and then we'll extract a function so we can easily do it for the second label first we need a paragraph builder a paragraph builder takes a paragraph style and what we want to specify in this paragraph style is that we are aligning our text to the center and in our case we only want one line of content and we're also going to add text to this builder in this case we'll add label 1 and then we want a paragraph which is based on the paragraph builder and we can also after we build we can also immediately call layout because again we don't live in the widget world here we can paint any dimensions we want so part of that process is providing these paragraph constraints and all we care about giving is a width our max width is whatever our drawable though when you look at essentially the width of the screen is that's the most width we want to give to this paragraph and at that point we have a paragraph and we can actually paint it so we can draw a paragraph we'll pass in the paragraph for the offset we'll go with zero for now and see what we get okay and there we see flutter education painted on the screen at the center of our drawable area the Y position is not correct yet and in fact let's just correct that right now so offset instead of zero will will make the X zero but for the y offset let's let's get the height of this paragraph and then let's take the available height so we'll take size dot height - line height / - and you'll notice now that our texts move down a little bit that's because we just vertically centered the text in our available space we've now created a paragraph builder with a given label we produced a paragraph from it we laid it out at the appropriate constraints and based on the line height we drew it at the center of the screen or center of available space and vertically centered but that's just one paragraph we need to do this for label 1 and label 2 now there's no need to repeat most of this so let's extract this into a local method that we can just invoke for both of our labels with build paragraph defined we can come back up here and say paragraph 1 equals build paragraph will send in label 1 and will send in the available size then we have line height and again we can draw a paragraph 1 because this text Scrolls what we want to do is position label 2 above label 1 and then we push them both down and then we move label one above label 2 and do it again that means that the position of label 2 is based on label 1 which means that this calculation that we do right here for the Y position we want to yank that out in fact we'll yank out the entire offset we can then use that offset for the second paragraph the final paragraph paragraph 2 equals build paragraph label 2 passing the available space which is size we don't care about the line height or we're going to assume the same line height for both of these because it's unclear what the design should be if the line height isn't the same and so now we need an offset for label 2 and we're going to say label 1 position translate no difference on the X we want to do something with the Y we're going to subtract the height of our available space so each label is vertically centered within an entire the entire size the entire height available to our painter so this is the available space for label 1 and label one's right there centered in it and then label 2 is up here centered right there let's see okay so in the build paragraph I need to use the incoming label not label one so that was a mistake and now if we look at the simulator even though it's hard to see what's going on here above flutter education is flutter training and that's the second label and that's the label that needs to come down to replace the first label in the next step we're gonna go ahead and introduce some animations so we get these labels pushing down and then after the animation is doing what it should we'll look at actually cropping out and fading this text to get the desired effect let's integrate an animation controller so we can get these labels moving up and down the way they're supposed to up here in our state full widget since we're gonna animate we need to bring in a single ticker provider state mixin and then we're going to declare an animation controller we will initialize the animation controller in init state we'll pass ourselves as the vsync and the duration will be two seconds we'll add a listener which is invoked every time the animation changes and we'll call set state to redraw ourselves we will also add a status listener and if the status is animation status completed we're going to restart the animation so that it's always running and after we create initialize the animation controller we're going to immediately start it running so it's going to start running immediately and it's always going to keep running of course we need to dispose of the animation controller in the dispose method now this animation controller is going to produce a value that we're going to send in for the scroll position but that's not going to do anything for us yet because we haven't implemented any math within our painter that actually accounts for the scroll position let's go look down in our painter and right here where we calculate label one position that essentially shouldn't be static this is fine this y-value is fine for the initial position but we want to add something to it we want to take the available height and we want to multiply it by the scroll position so when scroll position is zero our label is just sitting at its original position but every little incremental amount that scroll position changes and by the way that's a percentage value between zero and one we want to move an entire Heights worth down which brings label two into the center of the available space let's see what we get with this behavior we need to do a hot restart because we need to initialize the animation controller and there we go so we're moving up and down we're moving down as we expect so every two seconds we move down the desired amount but of course there are multiple visual issues here the first issue that we're going to solve is the fact that the moment that flutter training gets down to its final position it's correct to reset the position of the labels but we need to switch the labels every time the animation reaches the end label one needs to become label 2 and label 2 needs to become label 1 what that means is that these hard-coded values right here can't be hard-coded instead we need to come up here in our state and our state object we need to have a string which is label 1 which will initially be flutter education and string label 2 will be flutter training and then we will define a local method called switch labels and we will say if label 1 is equal to flutter education then label 1 needs to become flutter training and label 2 needs to become flutter education otherwise we do the opposite of course 1 it's it's not good practice to type this out because there might be a typo they should be in constants at least and 2 if you're dealing with more than two labels you need to do this kind of thing in some other fashion looping or or reference them based on index or something else but this is just because of how simple these two labels are and the fact that we're not really worried about being robust from a production standpoint anyways switch labels will flip the two and we want to execute that every time the animation completes we want to switch labels and then when we actually paint this thing label one should be label 1 and label 2 should be label 2 let's see what this does for us a hot restart now notice that visually it appears like whatever label is on the bottom jumps to the top and so now we can kind of see that stair casing effect where it looks like one message goes from top to bottom and the next message goes from top to bottom obviously we shouldn't be painting both of them at the same time but you know one problem at a time now a second issue that we want to address is that we actually we don't actually want these messages always moving at a constant rate from top to bottom we actually want them to pause for a moment and then continue switching again to accomplish this we are going to use a curve an animation curve but it's just called curve and we'll call it an animation curve and specifically we're going to use one that's called an interval an interval defines periods at the beginning and the end where nothing happens okay so zero point so for the first 30 percent we'll do nothing then 70 percent and the overall motion curve that's going to apply in between is going to be an ease in out curve that means instead of linear constant motion we speed up and then we slow down speed up slow down so interval again gives us some dead time let's jump in here and look at this definition a curve that is at 0% until the beginning value then curved and then 1 percent by the end and it stays at 1 percent so it's going back to our definition we're at zero until we hit 30% then we start animating and then when we hit 70% we're at 1 or 100% of any of the motion and we stop moving and then in between 30% and 70% we speed up and slow down and we're going to apply this curve directly we could set up some kind of tween I think it's unnecessary we just come down here and take the animation controller value and we can tell our animation curve to transform that value let's save and I'm gonna do a hot ok so went ahead and applied so you can see now that our motion has changed two things have happened you can clearly see that we're speeding up and slowing down and you can also see that there's a period of time where the text doesn't move so we have time to read it then it transitions and then we can read it and of course if you wanted you can make the duration longer you can make it shorter you can have the text stay still longer or not totally up to you but this interval and using this transform is how we accomplish that motion in the next step we're going to look at the basis for how we're going to achieve this fade-out so again even in the widget layer you could clip the desired area but we don't just want to clip we don't do we don't just want to hide the text that shouldn't be visible we want it to fade and so in the next section we're gonna look conceptually at how we achieve that to get us started with this next step I'm gonna begin by painting something additional onto the screen to show you how I'm thinking about the problem we're gonna paint a gradient so you see what it means to fade but then we're gonna use that gradient in a different way after that to actually achieve the fade okay the first thing we're gonna do is draw the gradient which isn't what we want to do in the end but it's what we're going to begin with we're gonna declare a linear gradient here and we're going to initialize it in our initializer list a linear gradient is a series of colors the colors don't really matter in this case so we'll just go with white but we're gonna the important part is that we're gonna start with no opacity we're gonna start completely transparent and then we're gonna go to completely opaque and we're gonna stay opaque for a while and then we're going to go to completely transparent again we're also going to establish where this begins and ends we're going from the top to the bottom and then we're going to give specific stops we don't want our colors to be evenly spaced we actually we want our particular colors to hit their primary point at different locations are at non-standard locations so we want the first stop to be just 5% from the top then we want the second stop to be 30% then 70% and finally 95% what this does what this has the effect of doing is it takes the transparent parts and it really pushes them to the fringe so we have just a little bit of transparency at the top and the bottom now with this gradient in hand I'm going to come down here into our paint method and we need to produce a paint object to paint with we'll call it I guess fade paint okay I forgot so the the fade okay so what we need to start with is a shader the linear gradient that which we called fade gradient it can create a shader now a shader needs to apply to a specific rectangle and we're going to base that rectangle on our size from zero zero to size dot width size height so this shader applies to all of our available space and then we can create a fade paint we create a paint and we set the shader to the fade shader and then finally we tell the canvas that we just want to essentially fill all available space with this paint now let's see what we've got all right so now you see you see this white gradient looking thing the reason I've drawn this here is because the part at the top and the bottom of this gradient where it it becomes white and then at the bottom it fades out from white we would like our text to be drawn in such a manner that it essentially intersects this gradient if our text intersects this gradient then our text won't be visible above or below this gradient and when the text initially comes in notice that the gradient is faded so our text will be faded and when the text goes out the bottom once again the gradient is faded so our text will be faded if we can somehow superimpose or intersect our text with this gradient that would seem to solve our problems and that's what we're gonna do in the next step in this step we're going to use the concept of a linear gradient and a shader to actually paint our text with that gradient and achieve a blending effect to do this we need well first we need to make a kind of syntactical syntax syntax syntax change paragraph builder and paragraph and all these other things they come from the dart UI package there's a naming conflict in that package that we're about to run into which is text text style is defined both in the widget space and in the dart UI package and there are actually different things we now need the text style from the dart UI package so I'm going to come up to the top here where we import dart UI and I'm going to refer to it as UI so everything from that package now requires UI to prefix it and that will get rid of our conflict but I first need to come down here and add UI dot before a number of these artifacts in front of paragraph in front of paragraph paragraph builder paragraph style paragraph constraints all right I think we're now back to normal and so now we can use UI dot text style without a conflict in here in build paragraph we have an opportunity to apply a desired paint let's take a paint in here here's the interesting part where we kind of accomplish the magic of this workshop when we create the paragraph builder we can push a style and this again is a UI text style it's not the normal one that you're used to working with it allows us to define a foreground paint and we can just provide our paint and of course the cool thing about paint is that it doesn't just have to be a color it can have again gradients blend modes all sorts of interesting things and we can apply that we can essentially intersect it with our text that's what we're doing here with this change to build paragraph let's come back up to our paint method and remember that we had this shader and in paint here let's we don't want to we don't want to paint the gradient anymore because obviously what you see here on the screen is not what we want but we do still want the shader and the paint so let's move that up to the top of our method here and we have this fade paint let's pass the fade paint to both of our build paragraph methods and now let's save that and see what happens do you guys see what's going on let me slow it way down for you to make sure that you see what's happening see that fade there's a fade at the bottom and there's a fade at the top because the path that represents our text is now essentially being intersected with that white gradient that we were previously painting on the screen and that's what gives us the fade and this by the way this is why I approach this with a custom painter and not a widget tree if you know how to do this with a widget tree by all means write it in the comments down below and I'd be happy to take a look but the only answer that I knew was to use the custom painter so this is definitely the coolest part of what we're trying to accomplish here but we do still need a tiny bit of cleanup and we're gonna do that in our final step for this final step I want to introduce just a couple of parameters that give us a little more control over our message display I would like for our text scroll painter to support a font size and will default to let's say 14 I would also like to receive a text color and actually so the color we can bring in as just a property but the font size we will save locally so down here we will introduce a font size as for the text color the reason that we don't need to hold on to that is because we're going to take that text color and we're going to apply it immediately to our linear gradient because maybe we don't want this text to always be white right so we want to be able to configure that gradient and again we want to be able to modify the font size and then so the the color is already applied for us now down here and build paragraph let's see you always forget which of these areas actually takes the font size okay so it looks like push style will allow us to reference the font size and with that we should have greater control over our tech scroll painter so let's give it the text color and just to make just to prove that this works let's go with I don't know green and then for the font size let's make it pretty big go with 22 and you can see over here large green font let me ha restart to get the faster animation time back all right green and we go yellow which is clearly my favorite color but now what we actually want here for this particular design is we want to drop back down to 16 for the font size and we want to go we still want white but we actually want an opacity let's say 60% opaque and that's really what we want for this design now we have control of the two labels that we pass in we can choose any font size we want we can use any font color with any maximum opacity and we have our text animating it sits for a little bit then it animates that'sit's then it animates and it fades at the top and it fades at the bottom so this is our text scroller completed and that's the end of this widget workshop I'll see you in the next one [Music]
Info
Channel: SuperDeclarative!
Views: 4,943
Rating: undefined out of 5
Keywords: Flutter
Id: HjJHO0NXI10
Channel Id: undefined
Length: 34min 52sec (2092 seconds)
Published: Thu Jul 02 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.