Flutter完全开发手册
  • Flutter 动画详解系列——Flutter自定义动画

    为什么要使用TweenAnimationBuilder?假设您要创建一个基本的动画:一个不会永远重复的动画,它只是一个widget或widget 树。

    TweenAnimationBuilder自定义隐式动画

    tween:Twee类型,动画过程中会把 Tween 的中间插值传给 builder 来构建子组件,从而可以实现过渡动画效果。

    builder:组件构建方法,类型为ValueWidgetBuilder,具体定义如下,其中 value 参数就是 tween 动画过程中的中间插值。也就是我们在动画期间,会不断调用builder 重新绘制子组件。

    大小变化的动画:

    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key});
    
      @override
      State createState() => _HomePageState();
    }
    
    class _HomePageState extends State {
      bool flag = true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('大小变化'),
          ),
          floatingActionButton: FloatingActionButton(
            child: const Icon(Icons.refresh),
            onPressed: () {
              setState(() {
                flag = !flag;
              });
            },
          ),
          body: Center(
            child: TweenAnimationBuilder(
              tween: Tween(begin: 100.0, end: flag ? 100.0 : 200.0),
              duration: const Duration(seconds: 1),
              builder: ((context, value, child) {
                return Icon(
                  Icons.star,
                  color: Colors.red,
                  size: value,
                );
              }),
            ),
          ),
        );
      }
    }
    

    10秒看懂Flutter自定义隐式动画TweenAnimationBuilder

    效果:点击浮动按钮,五角星的大小会变化。

    透明度变化的动画:

    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key});
    
      @override
      State createState() => _HomePageState();
    }
    
    class _HomePageState extends State {
      bool flag = true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('透明度变化'),
          ),
          floatingActionButton: FloatingActionButton(
            child: const Icon(Icons.refresh),
            onPressed: () {
              setState(() {
                flag = !flag;
              });
            },
          ),
          body: Center(
            child: TweenAnimationBuilder(
              tween: Tween(begin: 0.0, end: flag ? 0.2 : 1.0),
              duration: const Duration(seconds: 1),
              builder: ((context, value, child) {
                return Opacity(
                  opacity: value,
                  child: Container(color: Colors.blue, width: 200, height: 200),
                );
              }),
            ),
          ),
        );
      }
    }
    

    10秒看懂Flutter自定义隐式动画TweenAnimationBuilder

    效果:点击浮动按钮,盒子的透明度会变化。

    AnimatedBuilder自定义显式动画

    透明度动画:

    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key});
    
      @override
      State createState() => _HomePageState();
    }
    
    class _HomePageState extends State
        with SingleTickerProviderStateMixin {
      late AnimationController _controller;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
          vsync: this,
          duration: const Duration(seconds: 1),
        )..repeat(reverse: true); //.. 连缀操作
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('透明度动画'),
          ),
          body: Center(
            child: AnimatedBuilder(
              animation: _controller,
              builder: (BuildContext context, Widget? child) {
                return Opacity(
                  opacity: _controller.value,	//从0到1变化	
                  child: Container(
                    width: 200,
                    height: 200,
                    color: Colors.blue,
                    child: const Text('我是一个Text组件'),
                  ),
                );
              },
            ),
          ),
        );
      }
    }
    

    10秒看懂Flutter自定义隐式动画TweenAnimationBuilder

    效果:盒子的透明度会自动不停变化。

    自定义变化范围:

    上面代码中 opacity 的值我们也可以使用 Tween 来设置:

    
    opacity: Tween(begin: 0.5, end: 1.0).animate(_controller).value, //从0.5到1变化
    

    位置变化:

    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key});
    
      @override
      State createState() => _HomePageState();
    }
    
    class _HomePageState extends State
        with SingleTickerProviderStateMixin {
      late AnimationController _controller;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
          vsync: this,
          duration: const Duration(seconds: 1),
        )..repeat(reverse: true); //.. 连缀操作
      }
    
      @override
      Widget build(BuildContext context) {
        Animation y = Tween(begin: -120.0, end: 120.0)
            .chain(CurveTween(curve: Curves.easeIn))
            // .chain(CurveTween(curve: const Interval(0.2, 0.6)))
            .animate(_controller);
    
        return Scaffold(
          appBar: AppBar(
            title: const Text('位置变化'),
          ),
          body: Center(
            child: AnimatedBuilder(
              animation: _controller,
              builder: (BuildContext context, Widget? child) {
                return Container(
                  width: 200,
                  height: 200,
                  color: Colors.blue,
                  transform: Matrix4.translationValues(0, y.value, 0),
                  child: const Text('我是一个Text组件'),
                );
              },
            ),
          ),
        );
      }
    }