Giới thiệu

Animation là một quá trình hiển thị một loạt hình ảnh / hình ảnh theo một thứ tự cụ thể trong một khoảng thời gian cụ thể để tạo ảo giác về chuyển động. Các khía cạnh quan trọng nhất của Animation như sau -

Animation có hai giá trị riêng biệt: Giá trị bắt đầu và Giá trị kết thúc. Animation bắt đầu từ giá trị Bắt đầu và trải qua một loạt các giá trị trung gian và cuối cùng kết thúc ở giá trị Kết thúc. Ví dụ: để tạo hiệu ứng động cho tiện ích mờ dần, giá trị ban đầu sẽ là độ mờ hoàn toàn và giá trị cuối cùng sẽ là độ mờ bằng không.

Các giá trị trung gian có thể là tuyến tính hoặc phi tuyến tính (đường cong) về bản chất và nó có thể được cấu hình. Hiểu rằng Animation hoạt động như được định cấu hình. Mỗi cấu hình cung cấp một cảm giác khác nhau cho hình ảnh động. Ví dụ: làm mờ một tiện ích sẽ có tính chất tuyến tính trong khi độ nảy của một quả bóng sẽ có tính chất phi tuyến tính.

Thời lượng của quá trình Animation ảnh hưởng đến tốc độ (chậm hoặc nhanh) của Animation.

Khả năng kiểm soát quá trình Animation như bắt đầu Animation, dừng Animation, lặp lại Animation theo số lần đã đặt, đảo ngược quá trình Animation, v.v.,

Trong Flutter, hệ thống Animation không thực hiện bất kỳ Animation thực nào. Thay vào đó, nó chỉ cung cấp các giá trị cần thiết ở mọi khung hình để hiển thị hình ảnh.

Ứng dụng làm việc

Hãy để chúng tôi viết một ứng dụng dựa trên Animation đơn giản để hiểu khái niệm về Animation trong khung Flutter.

  • Tạo một ứng dụng Flutter mới trong Android studio, product_animation_app.

  • Sao chép thư mục nội dung từ product_nav_app sang product_animation_app và thêm nội dung bên trong tệp pubspec.yaml.

flutter: 
   assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
  • Xóa mã khởi động mặc định (main.dart).

  • Thêm nhập khẩu và chức năng chính cơ bản.

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp());
  • Tạo tiện ích MyApp bắt nguồn từ StatefulWidgtet.
class MyApp extends StatefulWidget { 
   _MyAppState createState() => _MyAppState(); 
}
  • Tạo tiện ích _MyAppState và triển khai initState và loại bỏ ngoài phương thức xây dựng mặc định.
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin { 
   Animation<double> animation; 
   AnimationController controller; 
   @override void initState() {
      super.initState(); 
      controller = AnimationController(
         duration: const Duration(seconds: 10), vsync: this
      ); 
      animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller); 
      controller.forward(); 
   } 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      controller.forward(); 
      return MaterialApp(
         title: 'Flutter Demo',
         theme: ThemeData(primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page', animation: animation,)
      ); 
   } 
   @override 
   void dispose() {
      controller.dispose();
      super.dispose();
   }
}

Ở đây,

  • Trong phương thức initState, chúng ta đã tạo một đối tượng điều khiển hoạt ảnh (controller), một đối tượng hoạt ảnh (animation) và bắt đầu hoạt ảnh bằng controller.forward.

  • Trong phương thức xử lý, chúng tôi đã xử lý đối tượng điều khiển hoạt hình (bộ điều khiển).

  • Trong phương thức xây dựng, gửi hoạt ảnh tới tiện ích MyHomePage thông qua hàm tạo. Giờ đây, tiện ích MyHomePage có thể sử dụng đối tượng hoạt ảnh để tạo hoạt ảnh cho nội dung của nó.

  • Bây giờ, hãy thêm tiện ích ProductBox

    class ProductBox extends StatelessWidget {
     ProductBox({Key key, this.name, this.description, this.price, this.image})
        : super(key: key);
     final String name; 
     final String description; 
     final int price; 
     final String image; 
     
     Widget build(BuildContext context) {
        return Container(
           padding: EdgeInsets.all(2), 
           height: 140, 
           child: Card( 
              child: Row( 
                 mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                 children: <Widget>[ 
                    Image.asset("assets/appimages/" + image), 
                    Expanded( 
                       child: Container( 
                          padding: EdgeInsets.all(5), 
                          child: Column( 
                             mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                             children: <Widget>[ 
                                Text(this.name, style: 
                                   TextStyle(fontWeight: FontWeight.bold)), 
                                Text(this.description), 
                                   Text("Price: " + this.price.toString()), 
                             ], 
                          )
                       )
                    )
                 ]
              )
           )
        ); 
     }
    }
    
    
  • Tạo một tiện ích mới, MyAnimatedWidget để thực hiện hoạt ảnh mờ dần đơn giản bằng cách sử dụng độ mờ.

class MyAnimatedWidget extends StatelessWidget { 
   MyAnimatedWidget({this.child, this.animation}); 
      
   final Widget child; 
   final Animation<double> animation; 
   
   Widget build(BuildContext context) => Center( 
   child: AnimatedBuilder(
      animation: animation, 
      builder: (context, child) => Container( 
         child: Opacity(opacity: animation.value, child: child), 
      ), 
      child: child), 
   ); 
}
  • Ở đây, chúng tôi đã sử dụng AniatedBuilder để thực hiện hoạt ảnh của mình. AnimatedBuilder là một tiện ích xây dựng nội dung của nó đồng thời thực hiện hoạt ảnh. Nó chấp nhận một đối tượng hoạt ảnh để nhận giá trị hoạt ảnh hiện tại. Chúng tôi đã sử dụng giá trị hoạt hình, animation.value để đặt độ mờ của tiện ích con. Trên thực tế, tiện ích sẽ tạo hoạt ảnh cho tiện ích con bằng cách sử dụng khái niệm độ mờ.

  • Cuối cùng, tạo tiện ích MyHomePage và sử dụng đối tượng hoạt hình để tạo hoạt ảnh cho bất kỳ nội dung nào của nó.

class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title, this.animation}) : super(key: key); 
   
   final String title; 
   final Animation<double> 
   animation; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Listing")),body: ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[
               FadeTransition(
                  child: ProductBox(
                     name: "iPhone", 
                     description: "iPhone is the stylist phone ever", 
                     price: 1000, 
                     image: "iphone.png"
                  ), opacity: animation
               ), 
               MyAnimatedWidget(child: ProductBox(
                  name: "Pixel", 
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), animation: animation), 
               ProductBox(
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox(
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ),
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ),
            ],
         )
      );
   }
}

Ở đây, chúng tôi đã sử dụng FadeAnimation và MyAnimationWidget để tạo hoạt ảnh cho hai mục đầu tiên trong danh sách. FadeAnimation là một lớp hoạt hình tích hợp, mà chúng tôi đã sử dụng để tạo hoạt ảnh cho con của nó bằng cách sử dụng khái niệm độ mờ.

  • Mã hoàn chỉnh như sau:
import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatefulWidget { 
   _MyAppState createState() => _MyAppState(); 
} 
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
   Animation<double> animation; 
   AnimationController controller; 
   
   @override 
   void initState() {
      super.initState(); 
      controller = AnimationController(
         duration: const Duration(seconds: 10), vsync: this); 
      animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller); 
      controller.forward(); 
   } 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      controller.forward(); 
      return MaterialApp( 
         title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page', animation: animation,) 
      ); 
   } 
   @override 
   void dispose() {
      controller.dispose();
      super.dispose(); 
   } 
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title, this.animation}): super(key: key);
   final String title; 
   final Animation<double> animation; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, 
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[
               FadeTransition(
                  child: ProductBox(
                     name: "iPhone", 
                     description: "iPhone is the stylist phone ever", 
                     price: 1000, 
                     image: "iphone.png"
                  ), 
                  opacity: animation
               ), 
               MyAnimatedWidget(
                  child: ProductBox( 
                     name: "Pixel", 
                     description: "Pixel is the most featureful phone ever", 
                     price: 800, 
                     image: "pixel.png"
                  ), 
                  animation: animation
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox(
                  name: "Tablet",
                  description: "Tablet is the most useful device ever for meeting",
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ], 
         )
      ); 
   } 
} 
class ProductBox extends StatelessWidget { 
   ProductBox({Key key, this.name, this.description, this.price, this.image}) :
      super(key: key);
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 140, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded(
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(
                                 this.name, style: TextStyle(
                                    fontWeight: FontWeight.bold
                                 )
                              ), 
                              Text(this.description), Text(
                                 "Price: " + this.price.toString()
                              ), 
                           ], 
                        )
                     )
                  ) 
               ]
            )
         )
      ); 
   } 
}
class MyAnimatedWidget extends StatelessWidget { 
   MyAnimatedWidget({this.child, this.animation}); 
   final Widget child; 
   final Animation<double> animation; 
 
   Widget build(BuildContext context) => Center( 
      child: AnimatedBuilder(
         animation: animation, 
         builder: (context, child) => Container( 
            child: Opacity(opacity: animation.value, child: child), 
         ), 
         child: child
      ), 
   ); 
}
  • Biên dịch và chạy ứng dụng để xem kết quả. Phiên bản đầu tiên và cuối cùng của ứng dụng như sau:
    image1
    image2