introduce
This function is part of the imitation of netease Cloud Music App:Copy the code
Imitation netease cloud music App
preview
We can see the card switching effect at the bottom of the page
Analysis of the
First, the animation is divided into two parts along the X-axis: text on the left and picture on the right
The picture on the right is divided into upper and lower parts along the Z-axis
If you look closely, you can see that its animation flow is roughly as follows:
The upper layer of the picture on the right fades out at the same time as the left text. 3. Then the lower layer moves up to the position of the upper layer picture. 4Copy the code
implementation
First we define a class
class MusicCalendar extends WidgetState with SingleTickerProviderStateMixin{}
Copy the code
Because this animation is quite complicated, I used provider in the actual development, and you may see musicCalendarVM in the code, which is mainly used to hold and control the state and some data. I will try to remove the code insideCopy the code
MusicCalendar
When the page is initialized, we call init() :
/// This method will be seen later, and its execution will start the animation, i.e., fade-move-fade /// in this case, to give you an idea of the process, Easy to understand the code behind the init () {if (streamSubscription. IsPaused) {streamSubscription. Resume (); }}Copy the code
Start by creating some variables
// Fade out/fade in AnimationController fadeController; Animation fadeAnim; Double aboveRightMax; double aboveRightMax; double aboveRightMax; double aboveRightMax; double aboveBottomMax;Copy the code
Let’s look at the layout again, there’s a lot of code, so I’ll put the instructions in the comments
//root layout Container(child: Stack(children: <Widget>) [/// /date this does not care, it does not have to do one of us. GetWidthPx (10), child: Text(' day after ',style: TextStyle(fontSize: getSp(28),color: color.black,fontWeight: Fontgravit.bold),),), /// this is the left side of the text, and the animation it will do is fade into and fade out of ___ (top: getWidthPx(60), child: Fade components FadeTransition (/ / use flutter opacity: musicCalendarVM. FadeAnim, / / and our animation binding child: Container (width: getWidthPx(430), child: Text('${creatives[musicCalendarVM.currentIndex].uiElement.mainTitle.title}', style: TextStyle(color: color.grey,fontSize: getSp(32)),maxLines: 2,),),), /// here is the right-hand corner of the image of space. getWidthPx(30), right: 0, child: imageSwitcher(), ), ], ), );Copy the code
Let’s look at this method: imageSwitcher()
Widget imageSwitcher(){return Container(){width: getWidthPx(150),height: GetWidthPx (150), child: Stack(children: <Widget>[/// /below /// this is the picture below, initially provides the lower right corner of the toy (right: 0, bottom: 0, // Function: controls the decaying of any picture. ShowImageUtil.showImageWithDefaultError(creatives[musicCalendarVM.currentIndex<=creatives.length-2 ?musicCalendarVM.currentIndex+1 : 0].uiElement.image.imageUrl , getWidthPx(130), getWidthPx(130),borderRadius: GetHeightPx (10)),),), /// /fake /// here I put an extra fake picture, and the following says small tracts (right: musicCalendarVM. Right, bottom: musicCalendarVM.bottom, child: Visibility( visible:musicCalendarVM.showFake , child: ShowImageUtil.showImageWithDefaultError(creatives[musicCalendarVM.fakeIndex].uiElement.image.imageUrl , GetWidthPx (130), getWidthPx(130),borderRadius: getHeightPx(10)),),), /// /// the upper picture starts as the upper corner of the uterus (left: 0, top: 0, // right: 0, // top: 0, // top: 0, // top: 0, // right: 0, /// MusicCalendarVM fadeAnim, / / and animation binding, like the previous text animation child: ShowImageUtil.showImageWithDefaultError(creatives[musicCalendarVM.currentIndex].uiElement.image.imageUrl , getWidthPx(130), getWidthPx(130),borderRadius: getHeightPx(10)), ), ), ], ), ); }Copy the code
First, we can see that there are three widgets in the stack:
The image below is "below". The image above is "Above". The fake image is "fake"Copy the code
below & above widget
Let’s look at ‘below’ and ‘above’. They both fade in and out, but they use different components.
"Below" uses Opacity and "above" uses FadeTransition, the same as the textCopy the code
The difference here is that they do not share the same animation because the actual execution and animation direction are different. Above, like the text, is controlled by the animation, so we don’t have to worry about it. Let’s look at below.
Its opacity property is bound to musicCalendarVM. Opacity, and the refresh of this opacity property mainly involves two methods
Just to review what it does, fade in (the actual move is done by fake, we'll get to that later)Copy the code
Timer = Timer. Milliseconds (Duration(milliseconds:); 1), (timer){if(opacity >=1.0){/// When opacity >=1.0, we end timer timer.cancel(); // This means "below" fades into complete, // Fade above and title fadecontroller.reverse (). WhenComplete ((){// when above fades in, Hide fake showFake = false; notifyListeners(); /// "fake" actually moves before "below" fades in to the top left corner. So "above" fades in, we reset fake to say "fake" right = 0; bottom = 0; FakeIndex = currentIndex <= creatives. Length-2? currentIndex+1:0; showFake = true; Opacity = 0; opacity = 0; notifyListeners(); }); return; } // Opacity +0.1 per 20 seconds =(opacity+0.1).clamp(0.0, 1.0); notifyListeners(); }); }Copy the code
Let’s see where showBelow() is called
If you're not familiar with the Provider or Bedrock framework, this simply means that when the page is first tested, clock listens and animations are performed. / / / at the beginning of that method init () {if (streamSubscription. IsPaused) {streamSubscription. Resume (); } } final Duration interval = Duration(seconds: 5); /// Switch MusicCalendarVM(this.block3, this.creatives,){clock = stream.periodic (interval,(index){}); /// Switch MusicCalendarVM(this.block3, this.creatives,){clock = stream.periodic (interval,(index){}); StreamSubscription = clock.listen((I) async{if(destroy)return; if(fadeController.status == AnimationStatus.completed|| fadeController.status == AnimationStatus.dismissed){ Fadecontroller.forward (). WhenComplete (){// Where right and bottom bind to fake, We'll talk about that later on. // In fact, right and bottom here are theoretically equal to the value on the right, but in fact, there is still a deviation. Here we calibrate right = aboveRightMax; bottom = aboveBottomMax; notifyListeners(); // update index = "dataList index incrementIndex(); /// insert a new below /// call our previous method showBelow(); //fadeController.reverse(); }); }}); streamSubscription.pause(); }Copy the code
fake widget
So above and below, let’s talk about fake,
///fake
Positioned(
right: musicCalendarVM.right,
bottom: musicCalendarVM.bottom,
child: Visibility(
visible:musicCalendarVM.showFake ,
child: ShowImageUtil.showImageWithDefaultError(creatives[musicCalendarVM.fakeIndex].uiElement.image.imageUrl
, getWidthPx(130), getWidthPx(130),borderRadius: getHeightPx(10)),
),
),
Copy the code
How fake works:
When the page starts: above is the first image, fake and below are the second above in the top right corner, below and fake in the bottom right corner, and fake blocks "below" when the animation starts: Above fades out and fake slides to the top left corner. When the fade is over, we add index+1 to above and below, where below is still invisible. We then fade "below" (it's always in the bottom right corner) and "above" (you can't see it fading because it's so fast and overlaps with fake) : We hide fake (top left) and adjust its right and bottom to 0, so that it moves to the bottom right, which then hides below, and the cycle is overCopy the code
Let’s take a look at the main attributes of fake:
Double right = 0; double bottom = 0; ShowFake // Controls the concealment of fake, as seen in the above methodCopy the code
The location to update these two properties is:
void updatePosition(){
right = aboveRightMax * (1-fadeAnim.value);
bottom = aboveBottomMax * (1-fadeAnim.value);
notifyListeners();
}
Copy the code
The updatePosition() method is called in the animationListener
musicCalendarVM.fadeController.addListener(musicCalendarVM.animationListener);
animationListener(){
if(fadeController.status == AnimationStatus.forward){
if(!showFake) showFake = true;
updatePosition();
}
}
Copy the code
At this point, the entire animation effect is realized, if a bit messy, you can compare the source code and the real machine effect in the demo to understand.
Thank you for reading and feel free to point out underspending 🙂
Demo
Internal search
Imitation netease Cloud music
This function is part of the imitation of netease Cloud Music App:Copy the code
Imitation netease cloud music App