Flutter动画实现(1) - 几行代码轻松教你实现flutter基础动画

154 阅读4分钟

前言

Flutter动画其实很简单,之前一直感觉动画很难不敢尝试,这几天看了b站的视频感觉其实也不难,万事开头难,实现动画其实只需要几行代码,故在此做个笔记,顺便分享给大家。

实现简单的动画

首先写一个简单的动画组件,然后只需要修改一行代码,再hot reload一下就可以实现一个简单的动画效果。

代码如下:

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: MyHome(),
    ),
  );
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: AnimatedContainer( // 动画组件
          duration: const Duration(seconds: 1), // 动画效果需要花费的时候
          width: 300,
          height: 300,
          color: Colors.red,
        ),
      ),
    );
  }
}

修改宽高来实现动画

ezgif-5485e234245b2e.gif

修改颜色来实现动画

ezgif-8a6c7ad77b1a72.gif

实现基础组件切换的动画

我们不难发现在AnimatedContainer中进行代码的变更就可以实现动画,那我们继续拓展,如果对AnimatedContainer控件的child进行编辑能否实现动画呢

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: MyHome(),
    ),
  );
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: AnimatedContainer(
          duration: const Duration(seconds: 1),
          width: 300,
          height: 300,
          color: Colors.green,
          child: Image.network("https://p9-passport.byteacctimg.com/img/user-avatar/80afee19cc3f022dd5c82e660dfff50c~90x90.awebp"),
          // child: Center(child: CircularProgressIndicator())
        ),
      ),
    );
  }
}

ezgif-2168b3fbd4710d.gif 我们发现在hot realod的之后图像立即切换了,这是因为没有包裹AnimatedSwitcher,包裹后我们再看看

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: MyHome(),
    ),
  );
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: AnimatedContainer(
          duration: const Duration(seconds: 1),
          width: 300,
          height: 300,
          color: Colors.green,
          child: AnimatedSwitcher(
              duration: const Duration(seconds: 1),
              child: Image.network("https://p9-passport.byteacctimg.com/img/user-avatar/80afee19cc3f022dd5c82e660dfff50c~90x90.awebp")
            // child: Center(child: CircularProgressIndicator())
          ),
        ),
      ),
    );
  }
}

ezgif-64c000bfed7f16.gif 现在可以发现有默认的淡入淡出的动画效果了

一些常见的特殊情况

如果是对Text组件进行切换会发生什么呢

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: MyHome(),
    ),
  );
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: AnimatedContainer(
          duration: const Duration(seconds: 1),
          width: 300,
          height: 300,
          color: Colors.green,
          child: const AnimatedSwitcher(
              duration: Duration(seconds: 1),
              child: Text("ovo", style: TextStyle(fontSize: 50),)
          ),
        ),
      ),
    );
  }
}

ezgif-435c40c1a5c5b4.gif 奇怪的事情发生了,Text的切换并没有动画效果。其实是因为Text组件里面的文本虽然被修改了,但是被flutter判定为同一个组件了,这样子就不被视为切换组件,因此没有动画效果。所以这个时候key就派上用场了,可以参考我的这篇文章 一个例子直观的告诉你flutter中key的作用,这样不被视为一个组件就可以执行切换动画

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: MyHome(),
    ),
  );
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: AnimatedContainer(
          duration: const Duration(seconds: 1),
          width: 300,
          height: 300,
          color: Colors.green,
          child: AnimatedSwitcher(
              duration: const Duration(seconds: 1),
              child: Text("ovo", style: const TextStyle(fontSize: 50), key: UniqueKey(),)
          ),
        ),
      ),
    );
  }
}

ezgif-2e97626a8bc43a.gif 图片也是同理,感兴趣的读者可以自己增删key: UniqueKey()来试试效果

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: MyHome(),
    ),
  );
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  int _count = 0;
  String _url = "https://q9.itc.cn/q_70/images01/20250112/a68f493962224b89b779ec9c1d017340.png";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: AnimatedContainer(
          duration: const Duration(seconds: 1),
          width: 300,
          height: 300,
          color: Colors.green,
          child: AnimatedSwitcher(
              transitionBuilder: (child, animation) {
                // return FadeTransition(opacity: animation, child: child,); //淡入淡出动画
                // return RotationTransition(turns: animation,child: child,); // 转圈动画
                // return ScaleTransition(scale: animation, child: child,); // 大小动画
                return FadeTransition(opacity: animation, child: ScaleTransition(scale: animation, child: child,),);
              },
              duration: const Duration(seconds: 1),
              child:
              // Image.network("https://q9.itc.cn/q_70/images01/20250112/a68f493962224b89b779ec9c1d017340.png", key: UniqueKey(),)
              Image.network(_url, 
              key: UniqueKey() // 通过增删这里来测试动画效果
              )
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(onPressed: () {
        print(_count);
        setState(() {
          if (_count == 0){
            _count ++;
            _url = "https://p1.itc.cn/q_70/images03/20231204/ce139ddd9b0e4f4397fcd86afdd4b1b9.png";
          } else {
            _count --;
            _url = "https://q9.itc.cn/q_70/images01/20250112/a68f493962224b89b779ec9c1d017340.png";
          }
        });
      }),
    );
  }
}

简单切换动画方式

AnimatedContainer中默认采用的动画效果是FadeTransition,我们可以把他换成RotationTransition或者ScaleTransition来实现其他的动画,感兴趣的读者可以自己试试,做gif很麻烦我就不展示啦。值得一提的是,这里如果用FadeTransition嵌套ScaleTransition,可以实现淡入淡出+大小切换的动画效果

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: MyHome(),
    ),
  );
}

class MyHome extends StatefulWidget {
  const MyHome({super.key});

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: AnimatedContainer(
          duration: const Duration(seconds: 1),
          width: 300,
          height: 300,
          color: Colors.green,
          child: AnimatedSwitcher(
              transitionBuilder: (child, animation) {
                // return FadeTransition(opacity: animation, child: child,); //淡入淡出动画
                // return RotationTransition(turns: animation,child: child,); // 转圈动画
                // return ScaleTransition(scale: animation, child: child,); // 大小动画
                return FadeTransition(opacity: animation, child: ScaleTransition(scale: animation, child: child,),);
              },
              duration: const Duration(seconds: 1),
              child: Text("ovo", style: const TextStyle(fontSize: 50), key: UniqueKey(),)
          ),
        ),
      ),
    );
  }
}