<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>动画 on 首页 | Widegram</title>
    <link>https://widegram.com/tags/%E5%8A%A8%E7%94%BB/</link>
    <description>Recent content in 动画 on 首页 | Widegram</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Mon, 01 Nov 2021 20:40:45 +0000</lastBuildDate><atom:link href="https://widegram.com/tags/%E5%8A%A8%E7%94%BB/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Flutter动画全解（三）底层自定义动画——Hero、CustomPainter和Rive</title>
      <link>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%89%EF%BC%89-%E5%BA%95%E5%B1%82%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%A8%E7%94%BB/</link>
      <pubDate>Mon, 01 Nov 2021 20:40:45 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%89%EF%BC%89-%E5%BA%95%E5%B1%82%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%A8%E7%94%BB/</guid>
      <description>底层自定义动画 3.1 Flutter动画原理 ​	Flutter内部通过setState()函数刷新页面，且flutter优化得相当好，一秒执行60/120次也不会有卡顿。
​	Ticker原理：屏幕每刷新一帧，称为一个ticker：
Ticker _ticker = Ticker((elapsed) { print(&amp;#34;Tick: $elapsed&amp;#34;);	//elapsed是运行总时长 })..start(); ​	你可以通过下列方式手动制作一个Ticker（仅作为实验用途，不推荐）：
double _height = 300; @override void initState(){ Ticker _ticker = Ticker((_) =&amp;gt; setState((){ _height --; if(_height &amp;lt;= 0) _height = 300; }))..start(); super.initState(); } ​	上述程序虽然可以运行，但首先，它没有考虑一些特殊情况，如程序暂时退出、切换到其他程序时，动画应该暂停。其次，每一帧时_height--，则在60帧的屏幕下该动画需要5秒执行完毕，而在120hz屏幕下只需要2.5秒。
3.2 Hero动画（主动画） ​	将需要做Hero动画的控件包裹上Hero，并给它一个tag属性，两个页面对应控件的tag一致。
​	如果两个页面中的控件样式不一致，或者类型不统一，那么会瞬间切换，不会有控件切换时的动画效果。
3.3 CustomPaint ​	Tips：要使Container占满屏幕，使用
Container( constraints: BoxConstraints.expand(), //或者 width: double.infinity, height: double.infinity, ) 3.4 嵌入式Rive（Flare）插件动画 Rive New File</description>
      <content:encoded><![CDATA[<h2 id="底层自定义动画">底层自定义动画</h2>
<h3 id="31-flutter动画原理">3.1 Flutter动画原理</h3>
<p>​		Flutter内部通过<code>setState()</code>函数刷新页面，且flutter优化得相当好，一秒执行60/120次也不会有卡顿。</p>
<p>​		Ticker原理：屏幕每刷新一帧，称为一个ticker：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Ticker _ticker <span style="color:#f92672">=</span> Ticker((elapsed) {
</span></span><span style="display:flex;"><span>  print(<span style="color:#e6db74">&#34;Tick: </span><span style="color:#e6db74">$</span>elapsed<span style="color:#e6db74">&#34;</span>);	<span style="color:#75715e">//elapsed是运行总时长
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>})..start();
</span></span></code></pre></div><p>​		你可以通过下列方式手动制作一个Ticker（仅作为实验用途，不推荐）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">double</span> _height <span style="color:#f92672">=</span> <span style="color:#ae81ff">300</span>;
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> initState(){
</span></span><span style="display:flex;"><span>  Ticker _ticker <span style="color:#f92672">=</span> Ticker((_) <span style="color:#f92672">=&gt;</span> setState((){
</span></span><span style="display:flex;"><span>    _height <span style="color:#f92672">--</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span>(_height <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">0</span>) _height <span style="color:#f92672">=</span> <span style="color:#ae81ff">300</span>;
</span></span><span style="display:flex;"><span>  }))..start();
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		上述程序虽然可以运行，但首先，它没有考虑一些特殊情况，如程序暂时退出、切换到其他程序时，动画应该暂停。其次，每一帧时<code>_height--</code>，则在60帧的屏幕下该动画需要5秒执行完毕，而在120hz屏幕下只需要2.5秒。</p>
<h3 id="32-hero动画主动画">3.2 Hero动画（主动画）</h3>
<p>​		将需要做Hero动画的控件包裹上Hero，并给它一个tag属性，两个页面对应控件的tag一致。</p>
<p>​		如果两个页面中的控件样式不一致，或者类型不统一，那么会瞬间切换，不会有控件切换时的动画效果。</p>
<h3 id="33-custompaint">3.3 CustomPaint</h3>
<p>​		Tips：要使Container占满屏幕，使用</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>Container(
</span></span><span style="display:flex;"><span>	constraints: BoxConstraints.expand(),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//或者
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  width: <span style="color:#66d9ef">double</span>.infinity,
</span></span><span style="display:flex;"><span>  height: <span style="color:#66d9ef">double</span>.infinity,
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h3 id="34-嵌入式riveflare插件动画">3.4 嵌入式Rive（Flare）插件动画</h3>
<p><a href="https://rive.app/a/im-bradley/files/Flare/new-file" target="_blank">Rive New File</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（二）显式动画——Transition系列</title>
      <link>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%BA%8C%EF%BC%89%E6%98%BE%E5%BC%8F%E5%8A%A8%E7%94%BB-Transition%E7%B3%BB%E5%88%97/</link>
      <pubDate>Mon, 01 Nov 2021 20:33:45 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%BA%8C%EF%BC%89%E6%98%BE%E5%BC%8F%E5%8A%A8%E7%94%BB-Transition%E7%B3%BB%E5%88%97/</guid>
      <description>显式动画——Transition系列（配合AnimationController） 2.1 循环旋转——RotationTransition ​	我们需要使用RotationTransition配合AnimationController实现动画的手动控制，以实现隐式动画（Animated系列）无法完成的功能：循环重播、随时中断、多方协调等。
​	另外，要使用AnimationController，我们必须使用StatefulWidget，以使用生命周期函数initState()和dispose()。
​	我们先简单地看一个实例，在后文中，我们将详细解释其中的内容。
import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(MyApp()); class MyApp extends StatefulWidget { MyApp({Key? key}) : super(key: key); @override _MyAppState createState() =&amp;gt; _MyAppState(); } //SingleTickerProviderStateMixin用于垂直同步 class _MyAppState extends State&amp;lt;MyApp&amp;gt; with SingleTickerProviderStateMixin { late AnimationController _controller; bool _loading = false; @override void initState() { _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, ); super.initState(); } @override void dispose() { _controller.dispose(); //用完删除，否则内存泄漏 super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: Container( width: 300, height: 120, color: Colors.</description>
      <content:encoded><![CDATA[<h2 id="显式动画transition系列配合animationcontroller">显式动画——Transition系列（配合AnimationController）</h2>
<h3 id="21-循环旋转rotationtransition">2.1 循环旋转——RotationTransition</h3>
<p>​		我们需要使用<code>RotationTransition</code>配合<code>AnimationController</code>实现动画的手动控制，以实现隐式动画（Animated系列）无法完成的功能：循环重播、随时中断、多方协调等。</p>
<p>​		另外，要使用AnimationController，我们必须使用StatefulWidget，以使用生命周期函数<code>initState()</code>和<code>dispose()</code>。</p>
<p>​		我们先简单地看一个实例，在后文中，我们将详细解释其中的内容。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//SingleTickerProviderStateMixin用于垂直同步
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> _loading <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose(); <span style="color:#75715e">//用完删除，否则内存泄漏
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        child: RotationTransition(
</span></span><span style="display:flex;"><span>          turns: _controller,   <span style="color:#75715e">//这里直接使用controller即可，后文来解释
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          <span style="color:#75715e">//这里的动画还没有开始，调用_controller.forward()开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat,size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {		<span style="color:#75715e">//按一下按钮开始旋转，再按一下停止。
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          _loading <span style="color:#f92672">?</span> _controller.stop() <span style="color:#f92672">:</span> _controller.repeat();
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_loading;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>完成效果如下：</p>
<img src="https://i.loli.net/2021/11/01/yfwxzL6BWpk1F5E.jpg" alt="IMG_0848" style="zoom: 25%;" />
<h3 id="22-animationcontroller是什么">2.2 AnimationController是什么</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnimationController</span> <span style="color:#66d9ef">extends</span> Animation<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>
</span></span></code></pre></div><p>​		从本质上来说，<code>AnimationController</code>是一个<code>Animation&lt;double&gt;</code>，正如上面看到的那样，<code>RotationTransition</code>的turns属性本质上要求一个Animation。</p>
<p>​		AnimationController会在Duration时间内，生成0～1的double，如果要更改区间，使用lowerBound和upperBound属性。</p>
<p>​		示例代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//SingleTickerProviderStateMixin用于垂直同步，屏幕每次刷新，产生一个tick
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">/* class AnimationController extends Animation&lt;double&gt; */</span>
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">bool</span> _loading <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      lowerBound: <span style="color:#ae81ff">0.0</span>, <span style="color:#75715e">//与默认一致，生成0-1
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      upperBound: <span style="color:#ae81ff">1.0</span>,
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..addListener(() { <span style="color:#75715e">//dart语法：“..”表示返回值还是前面的表达式
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      <span style="color:#75715e">// _controller.value 在1s内自动生成0～1之间的数字（1秒60帧）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      print(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>_controller.value<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>);
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose(); <span style="color:#75715e">//用完删除，否则内存泄漏
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">//例如：RotateTransition、FadeTransition、ScaleTransition
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        child: FadeTransition(
</span></span><span style="display:flex;"><span>          opacity: _controller, <span style="color:#75715e">//这里本质上要求一个Animation&lt;double&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      )),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">?</span> _controller.stop() <span style="color:#f92672">:</span> _controller.repeat(reverse: <span style="color:#66d9ef">true</span>);
</span></span><span style="display:flex;"><span>          _loading <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_loading;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="23-控制器补间tween和曲线">2.3 控制器补间（Tween）和曲线</h3>
<p>​		我们可以使用drive函数将Animation的值映射到Tween上，也就是说，最终生成的值是在0.5到1.0之间，而不是原来的0.0～1.0了。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>ScaleTransition(
</span></span><span style="display:flex;"><span>  scale: _controller.drive(Tween(begin: <span style="color:#ae81ff">0.5</span>, end: <span style="color:#ae81ff">1.0</span>)),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		又比如说，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: _controller.drive(Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0.1</span>,<span style="color:#ae81ff">0</span>))),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//等价于
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0.1</span>,<span style="color:#ae81ff">0</span>)).animate(_controller),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		通过使用chain函数，Tween之间可以叠加，比如，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>SlideTransition(
</span></span><span style="display:flex;"><span>  position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1.0</span>))
</span></span><span style="display:flex;"><span>      .chain(CurveTween(curve: Curves.elasticOut))  <span style="color:#75715e">//叠加曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      .chain(CurveTween(curve: <span style="color:#66d9ef">const</span> Interval(<span style="color:#ae81ff">0.5</span>, <span style="color:#ae81ff">1.0</span>))) 
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">//使用Interval，限制动画播放时间，从 50% 时间开始到 100% ，其他时间不做任何事
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      .animate(_controller),
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		整体代码如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>  late <span style="color:#66d9ef">bool</span> _stop <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..repeat();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: SlideTransition(
</span></span><span style="display:flex;"><span>              position: Tween(begin: Offset.zero, end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">1.0</span>))
</span></span><span style="display:flex;"><span>                .chain(CurveTween(curve: Curves.elasticOut))  <span style="color:#75715e">//叠加曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                .chain(CurveTween(curve: <span style="color:#66d9ef">const</span> Interval(<span style="color:#ae81ff">0.5</span>, <span style="color:#ae81ff">1.0</span>))) 
</span></span><span style="display:flex;"><span>                      <span style="color:#75715e">//使用Interval，限制动画播放时间，从 50% 时间开始到 100% ，其他时间不做任何事
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                .animate(_controller),
</span></span><span style="display:flex;"><span>              child: Container(
</span></span><span style="display:flex;"><span>                width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>                height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>                color: Colors.blue,
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {
</span></span><span style="display:flex;"><span>          _stop <span style="color:#f92672">?</span> _controller.repeat() <span style="color:#f92672">:</span> _controller.reset();
</span></span><span style="display:flex;"><span>          _stop <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>_stop;
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="24-交错动画interval">2.4 交错动画——Interval</h3>
<p>​		如上文，使用Interval可以限制播放时间，如此一来，作如下安排，达到五个组件间到交错动画：</p>
<ol>
<li>
<p>时间0.0～0.2运行第一个动画</p>
</li>
<li>
<p>时间0.2～0.4运行第二个动画</p>
</li>
<li>
<p>时间0.4～0.6运行第三个动画</p>
</li>
<li>
<p>时间0.6～0.8运行第四个动画</p>
</li>
<li>
<p>时间0.8～1.0运行第五个动画</p>
<p>​	使用<code>Column</code>控件将以上五个<code>SlideTransition</code>组织起来，并将它们绑定到同一个_controller即可（SlideTransition本身也是Widget）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SlideTransition</span> <span style="color:#66d9ef">extends</span> AnimatedWidget { ... }
</span></span></code></pre></div></li>
</ol>
<h3 id="25-自定义动画animatedbuilder">2.5 自定义动画——AnimatedBuilder</h3>
<p>​		通过自己动手制作FadeTransition，我们可以看出Transition系列控件的内部原理。同时也可以看到，就像TweenAnimationBuilder那样，使用AnimatedBuilder时我们也可以对动画进行优化，避免反复渲染那些与动画无关的Widget：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//如何使用AnimatedBuilder制作出一个FadeTransition？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//每当animtion（_controller）的值变化，flutter就会重新调用builder函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  animation: _controller, 
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//还记得builder就是复杂版的child吗？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (context, child) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Opacity(
</span></span><span style="display:flex;"><span>      opacity: _controller.value,<span style="color:#75715e">//这里可以用Tween制作补间动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      child: child
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//下面的child是用于优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//这是内置的FadeTransition，使用起来更加简单，但是没有办法进行细致的绘制优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>child: FadeTransition(
</span></span><span style="display:flex;"><span>  opacity: _controller, <span style="color:#75715e">//这里本质上要求一个Animation&lt;double&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>​		整体代码如下：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> SingleTickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _controller;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _controller <span style="color:#f92672">=</span> AnimationController(
</span></span><span style="display:flex;"><span>      duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">2</span>),
</span></span><span style="display:flex;"><span>      vsync: <span style="color:#66d9ef">this</span>,
</span></span><span style="display:flex;"><span>    )..repeat();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _controller.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> Animation opacityAnimation <span style="color:#f92672">=</span> Tween(begin: <span style="color:#ae81ff">0.5</span>, end: <span style="color:#ae81ff">0.8</span>).animate(_controller);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: Container(
</span></span><span style="display:flex;"><span>              width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>              height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>              color: Colors.red,
</span></span><span style="display:flex;"><span>              child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>                  animation: _controller, <span style="color:#75715e">//每当animtion（_controller）的值变化，flutter就会重新调用builder函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                  builder: (context, child) { <span style="color:#75715e">//还记得builder就是复杂版的child吗？
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                    <span style="color:#66d9ef">return</span> Opacity(
</span></span><span style="display:flex;"><span>                      opacity: opacityAnimation.value,
</span></span><span style="display:flex;"><span>                      <span style="color:#75715e">//使用Tween制作补间
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                      <span style="color:#75715e">//需要注意的是这里的opacity是普通变量，所以需要使用Animation.value
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                      child: child
</span></span><span style="display:flex;"><span>                    );
</span></span><span style="display:flex;"><span>                  },
</span></span><span style="display:flex;"><span>                  <span style="color:#75715e">//下面的child是用于优化
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>                  child: <span style="color:#66d9ef">const</span> Icon(Icons.repeat, size: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.add),
</span></span><span style="display:flex;"><span>        onPressed: () {},
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="26-使用多个controller的例子tickerproviderstatemixin">2.6 使用多个Controller的例子——TickerProviderStateMixin</h3>
<p>​	有时我们会希望使用多个Controller（绝大多数情况下一个足矣），此时我们需要使用<code>TickerProviderStateMixin</code>。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  _MyAppState createState() <span style="color:#f92672">=&gt;</span> _MyAppState();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyAppState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>MyApp<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">with</span> TickerProviderStateMixin {
</span></span><span style="display:flex;"><span>  late AnimationController _expansionController;
</span></span><span style="display:flex;"><span>  late AnimationController _opacityController;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> initState() {
</span></span><span style="display:flex;"><span>    _expansionController <span style="color:#f92672">=</span> AnimationController(vsync: <span style="color:#66d9ef">this</span>);
</span></span><span style="display:flex;"><span>    _opacityController <span style="color:#f92672">=</span> AnimationController(vsync: <span style="color:#66d9ef">this</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.initState();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">void</span> dispose() {
</span></span><span style="display:flex;"><span>    _expansionController.dispose();
</span></span><span style="display:flex;"><span>    _opacityController.dispose();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">super</span>.dispose();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> Animation gradientAnimation <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>        Tween(begin: <span style="color:#ae81ff">0.3</span>, end: <span style="color:#ae81ff">1.0</span>).animate(_expansionController);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Center(
</span></span><span style="display:flex;"><span>          child: FadeTransition(
</span></span><span style="display:flex;"><span>              opacity: _opacityController.drive(Tween(begin: <span style="color:#ae81ff">1.0</span>, end: <span style="color:#ae81ff">0.5</span>)),
</span></span><span style="display:flex;"><span>              child: AnimatedBuilder(
</span></span><span style="display:flex;"><span>                animation: _expansionController,
</span></span><span style="display:flex;"><span>                builder: (context, child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>                  width: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>                  decoration: BoxDecoration(
</span></span><span style="display:flex;"><span>                    shape: BoxShape.circle,
</span></span><span style="display:flex;"><span>                    color: Colors.white,
</span></span><span style="display:flex;"><span>                    gradient: RadialGradient(colors: [
</span></span><span style="display:flex;"><span>                      Colors.blue.shade500,
</span></span><span style="display:flex;"><span>                      Colors.grey.shade50
</span></span><span style="display:flex;"><span>                    ], stops: [
</span></span><span style="display:flex;"><span>                      gradientAnimation.value,
</span></span><span style="display:flex;"><span>                      gradientAnimation.value <span style="color:#f92672">+</span> <span style="color:#ae81ff">0.2</span>
</span></span><span style="display:flex;"><span>                    ]),
</span></span><span style="display:flex;"><span>                  ),
</span></span><span style="display:flex;"><span>                ),
</span></span><span style="display:flex;"><span>              ))),
</span></span><span style="display:flex;"><span>      floatingActionButton: FloatingActionButton(
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Icon(Icons.badge),
</span></span><span style="display:flex;"><span>        onPressed: () <span style="color:#66d9ef">async</span> {
</span></span><span style="display:flex;"><span>          _expansionController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">3</span>);
</span></span><span style="display:flex;"><span>          _expansionController.forward();
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">await</span> Future.delayed(<span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">3</span>));
</span></span><span style="display:flex;"><span>          _opacityController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">4</span>);
</span></span><span style="display:flex;"><span>          _opacityController.forward();
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">await</span> Future.delayed(<span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">4</span>));
</span></span><span style="display:flex;"><span>          _expansionController.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>);
</span></span><span style="display:flex;"><span>          _expansionController.reverse();
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Flutter动画全解（一）隐式动画——Animated系列</title>
      <link>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%80%EF%BC%89%E9%9A%90%E5%BC%8F%E5%8A%A8%E7%94%BB-Animated%E7%B3%BB%E5%88%97/</link>
      <pubDate>Mon, 01 Nov 2021 20:19:45 +0000</pubDate>
      
      <guid>https://widegram.com/2021/11/Flutter%E5%8A%A8%E7%94%BB%E5%85%A8%E8%A7%A3%EF%BC%88%E4%B8%80%EF%BC%89%E9%9A%90%E5%BC%8F%E5%8A%A8%E7%94%BB-Animated%E7%B3%BB%E5%88%97/</guid>
      <description>隐式动画——Animated系列 1.1 快速开始——AnimatedContainer //原始main.dart import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(MyApp()); class MyApp extends StatelessWidget { MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Container(	//一个普普通通的Container width: 300, height: 300, color: Colors.blue, child: const Center( child: Text(&amp;#34;Hi&amp;#34;, style: TextStyle( fontSize: 72, )), ), ), )); } } //使用AnimatedContainer，并传入Duration，两行代码就可以动起来！ import &amp;#39;package:flutter/material.dart&amp;#39;; void main() =&amp;gt; runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: AnimatedContainer(	//将Container换成AnimatedContainer duration: const Duration(seconds: 1),	//同时加上duration参数即可 width: 300, height: 100, color: Colors.</description>
      <content:encoded><![CDATA[<p><img loading="lazy" src="https://i.loli.net/2021/11/01/VBE9gcASi5q6jaY.jpg" alt="DBAED6ED-4E11-4344-8500-8F9EA1EA816F_1_105_c.jpeg"  />
</p>
<h2 id="隐式动画animated系列">隐式动画——Animated系列</h2>
<h3 id="11-快速开始animatedcontainer">1.1 快速开始——AnimatedContainer</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//原始main.dart
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>      body: Container(		<span style="color:#75715e">//一个普普通通的Container
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>        color: Colors.blue,
</span></span><span style="display:flex;"><span>        child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>          child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,
</span></span><span style="display:flex;"><span>              style: TextStyle(
</span></span><span style="display:flex;"><span>                fontSize: <span style="color:#ae81ff">72</span>,
</span></span><span style="display:flex;"><span>              )),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    ));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//使用AnimatedContainer，并传入Duration，两行代码就可以动起来！
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>      home: Scaffold(
</span></span><span style="display:flex;"><span>        body: Center(
</span></span><span style="display:flex;"><span>          child: AnimatedContainer(		<span style="color:#75715e">//将Container换成AnimatedContainer
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),	<span style="color:#75715e">//同时加上duration参数即可
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>            height: <span style="color:#ae81ff">100</span>,
</span></span><span style="display:flex;"><span>            color: Colors.blue,
</span></span><span style="display:flex;"><span>            child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>              child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">72</span>)),
</span></span><span style="display:flex;"><span>            ),
</span></span><span style="display:flex;"><span>          ),
</span></span><span style="display:flex;"><span>        ),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>​		这里，<code>AnimatedContainer</code>不能管理其child属性的变化，只能在其自身属性变化时产生动画。</p>
<p>​		值得一提的是，<code>decoration</code>属性是非常好用的，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedContainer(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//color: Colors.blue,			//设置decoration属性后，color属性必须删除
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  decoration: BoxDecoration(
</span></span><span style="display:flex;"><span>    gradient: <span style="color:#66d9ef">const</span> LinearGradient(			<span style="color:#75715e">//设置渐变色
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      begin: Alignment.bottomCenter,    <span style="color:#75715e">//橙色从底部中心开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      end: Alignment.topCenter,         <span style="color:#75715e">//白色从顶部中心开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      colors: [Colors.orange, Colors.white],
</span></span><span style="display:flex;"><span>      stops: [<span style="color:#ae81ff">0.5</span>,<span style="color:#ae81ff">0.7</span>],                 <span style="color:#75715e">//只在这个区间内设置动画效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ),
</span></span><span style="display:flex;"><span>    boxShadow: <span style="color:#66d9ef">const</span> [BoxShadow(spreadRadius: <span style="color:#ae81ff">5</span>, blurRadius: <span style="color:#ae81ff">25</span>)],	<span style="color:#75715e">//边框半径5，阴影半径25
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    borderRadius: BorderRadius.circular(<span style="color:#ae81ff">150</span>)  <span style="color:#75715e">//设置300/2=150，出现正圆形；设置0，变为方形
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  ),
</span></span><span style="display:flex;"><span>  child: <span style="color:#66d9ef">const</span> Center(
</span></span><span style="display:flex;"><span>    child: Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">72</span>)),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><img src="https://i.loli.net/2021/11/01/nw7Zi82tVsqjIyr.jpg" alt="5514F21B-7194-4DA1-A5D0-165C90F6B1DC_1_105_c" style="zoom:33%;" />
<p>最终效果图如上。</p>
<h3 id="12-在不同控件间切换animatedswitcher">1.2 在不同控件间切换——AnimatedSwitcher</h3>
<p>​		AnimatedSwitcher主要监控child属性，如果其child属性发生变化，则会在替换控件时添加过渡动画。如果child变为null，那么该控件将渐渐消失。</p>
<p>​		AnimatedSwitcher根据 1.child的属性 2.key属性 来判断控件是否变化。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedSwitcher(
</span></span><span style="display:flex;"><span>  transitionBuilder: (child, animation) {   <span style="color:#75715e">//自定义动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//return FadeTransition(opacity: animation, child: child);  //默认效果：透明度渐变
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//return ScaleTransition(scale: animation, child: child); //缩放动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">return</span> RotationTransition(turns: animation, child: child); <span style="color:#75715e">//旋转动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  },
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),	<span style="color:#75715e">//动画持续1秒
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">//child: CircularProgressIndicator(),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: Text(<span style="color:#e6db74">&#34;h22i&#34;</span>,
</span></span><span style="display:flex;"><span>  	key: UniqueKey(),     <span style="color:#75715e">//每次都生成一个新的key，因此AnimatedSwitcher每次都会播放切换动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  	style: <span style="color:#66d9ef">const</span> TextStyle(fontSize: <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>在<code>transitionBuilder</code>中，也可以嵌套使用多种动画：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>transitionBuilder: (child, animation) {   <span style="color:#75715e">//自定义动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">return</span> FadeTransition(   <span style="color:#75715e">//不透明度渐变 + 旋转动画 的嵌套
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    opacity: animation,
</span></span><span style="display:flex;"><span>    child: RotationTransition(turns: animation, child: child), <span style="color:#75715e">//旋转动画
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  );
</span></span><span style="display:flex;"><span>},
</span></span></code></pre></div><p>如果你只有两个控件，要实现来回切换的效果，可以使用优化过后的<code>AnimatedCrossFade</code>控件。</p>
<h3 id="13-曲线curvesbounceout">1.3 曲线——Curves.bounceOut</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedOpacity(
</span></span><span style="display:flex;"><span>	duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>	curve: Curves.bounceOut,    <span style="color:#75715e">//设置回弹曲线
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>	opacity: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>	child: Container(width: <span style="color:#ae81ff">300</span>, height: <span style="color:#ae81ff">300</span>, color: Colors.blue),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>可以多个Animated控件嵌套，例如：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span>AnimatedPadding(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  padding: <span style="color:#66d9ef">const</span> EdgeInsets.only(top: <span style="color:#ae81ff">200.0</span>),
</span></span><span style="display:flex;"><span>  curve: Curves.bounceOut, <span style="color:#75715e">//设置回弹曲线，默认Curves.linear
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  child: AnimatedOpacity(   <span style="color:#75715e">//AnimatedPadding中嵌套AnimatedOpacity，但动画会一同开始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>    opacity: <span style="color:#ae81ff">0.5</span>,
</span></span><span style="display:flex;"><span>    child: Container(width: <span style="color:#ae81ff">300</span>, height: <span style="color:#ae81ff">300</span>, color: Colors.blue),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="14-animated还不够自制补间动画tweenanimationbuilder">1.4 Animated还不够？自制补间动画——TweenAnimationBuilder</h3>
<p>使用<code>TweenAnimationBuilder</code>和<code>Tween</code>来创建补间动画。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//使用TweenAnimationBuilder自制一个AnimatedOpacity
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//其中，begin的值只在开始时有效，后期如果修改了end，则会从当前状态（而不是begin）前往新的end
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">//从begin到end期间，builder函数会被反复调用，反复渲染新的（含value）的控件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(begin: <span style="color:#ae81ff">0.0</span>, end: <span style="color:#ae81ff">0.5</span>),   
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//builder是复杂形式的child（如ListBuilder）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (BuildContext context, <span style="color:#66d9ef">double</span> value, Widget<span style="color:#f92672">?</span> child)<span style="color:#f92672">=&gt;</span> Opacity(
</span></span><span style="display:flex;"><span>    opacity: value,	<span style="color:#75715e">//value也就是tween自动生成的那个值（0.0-0.5之间）
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: child  <span style="color:#75715e">//参数中的child不会被反复渲染，而会使用原来的child，达到一个优化的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  ),
</span></span><span style="display:flex;"><span>  child: Container( 
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//这里的child（也就是直属于TweenAnimationBuilder的child）就是builder函数中的child
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//builder不会反复渲染这里的child，达到一个优化的效果
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><p>使用<code>Transform</code>来定义变换：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//旋转变换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(begin: <span style="color:#ae81ff">1.0</span>, end: <span style="color:#ae81ff">6.0</span>),   <span style="color:#75715e">//忽略begin，则此时begin=end，启动时动画不播放
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  builder: (BuildContext context, <span style="color:#66d9ef">double</span> value, Widget<span style="color:#f92672">?</span> child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// * 任何平移旋转缩放操作都能用4*4矩阵表示
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">//Transform( transform: Matrix4.identity().translate(x),
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">// * 或者使用 Transform.scale缩放 / .rotate旋转(angle:0-2pi) / .translate平移
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    child: Center(child: Transform.rotate(	<span style="color:#75715e">//旋转
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      angle: value, 
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">70</span>)),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>   ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#75715e">//平移变换
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>  duration: <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//tween中可以直接放置offset
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  tween: Tween(begin: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">0</span>,<span style="color:#ae81ff">0</span>), end: <span style="color:#66d9ef">const</span> Offset(<span style="color:#ae81ff">40</span>,<span style="color:#ae81ff">0</span>)),
</span></span><span style="display:flex;"><span>  builder: (BuildContext context, Offset value, Widget<span style="color:#f92672">?</span> child) <span style="color:#f92672">=&gt;</span> Container(
</span></span><span style="display:flex;"><span>    width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    height: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>    color: Colors.red,
</span></span><span style="display:flex;"><span>    child: Center(child: Transform.translate(<span style="color:#75715e">//平移操作
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>      offset: value,
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> Text(<span style="color:#e6db74">&#34;Hi&#34;</span>,style: TextStyle(fontSize: <span style="color:#ae81ff">70</span>),),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>   ),
</span></span><span style="display:flex;"><span>  ),
</span></span><span style="display:flex;"><span>),
</span></span></code></pre></div><h3 id="15-计数器动画">1.5 计数器动画</h3>
<p>实现的效果如下：</p>
<img src="https://i.loli.net/2021/11/01/kQPf3DOgAhqXNZS.jpg" alt="6E7223F5-16FA-4D8C-AB06-57CF0EA8E594_1_201_a" style="zoom:33%;" />
<p>代码：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dart" data-lang="dart"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> main() <span style="color:#f92672">=&gt;</span> runApp(<span style="color:#66d9ef">const</span> MyApp());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MyApp</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> MyApp({Key<span style="color:#f92672">?</span> key}) <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> MaterialApp(
</span></span><span style="display:flex;"><span>        home: Scaffold(
</span></span><span style="display:flex;"><span>            body: Center(
</span></span><span style="display:flex;"><span>                child: Container(
</span></span><span style="display:flex;"><span>      width: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>      height: <span style="color:#ae81ff">120</span>,
</span></span><span style="display:flex;"><span>      color: Colors.blue,
</span></span><span style="display:flex;"><span>      child: <span style="color:#66d9ef">const</span> AnimatedCounter(<span style="color:#ae81ff">4</span>,duration: Duration(seconds: <span style="color:#ae81ff">1</span>)), <span style="color:#75715e">//使用滚动计数器
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    ))));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//滚动计数器
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnimatedCounter</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">int</span> number;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> Duration duration;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> AnimatedCounter(<span style="color:#66d9ef">this</span>.number, {<span style="color:#66d9ef">this</span>.duration <span style="color:#f92672">=</span> <span style="color:#66d9ef">const</span> Duration(seconds: <span style="color:#ae81ff">1</span>) ,Key<span style="color:#f92672">?</span> key})
</span></span><span style="display:flex;"><span>   <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> TweenAnimationBuilder(
</span></span><span style="display:flex;"><span>        duration: duration,
</span></span><span style="display:flex;"><span>        tween: Tween<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">double</span><span style="color:#f92672">&gt;</span>(end: number.toDouble()), <span style="color:#75715e">//调整end
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        builder: (context, <span style="color:#66d9ef">double</span><span style="color:#f92672">?</span> value, child) {
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">final</span> whole <span style="color:#f92672">=</span> value<span style="color:#f92672">!</span> <span style="color:#f92672">~/</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">//整数部分
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>          <span style="color:#66d9ef">final</span> decimal <span style="color:#f92672">=</span> value <span style="color:#f92672">-</span> whole;
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">return</span> Stack(children: [
</span></span><span style="display:flex;"><span>            _MyText(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">$</span>whole<span style="color:#e6db74">&#34;</span>, position: <span style="color:#f92672">-</span><span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> decimal, opacity: <span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> decimal),
</span></span><span style="display:flex;"><span>            _MyText(<span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>whole <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>,
</span></span><span style="display:flex;"><span>                position: <span style="color:#ae81ff">100</span> <span style="color:#f92672">-</span> decimal <span style="color:#f92672">*</span> <span style="color:#ae81ff">100</span>, opacity: decimal),
</span></span><span style="display:flex;"><span>          ]);
</span></span><span style="display:flex;"><span>        });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_MyText</span> <span style="color:#66d9ef">extends</span> StatelessWidget {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">String</span> text;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> position;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">final</span> <span style="color:#66d9ef">double</span> opacity;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> _MyText(<span style="color:#66d9ef">this</span>.text,
</span></span><span style="display:flex;"><span>      {<span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.position, <span style="color:#66d9ef">required</span> <span style="color:#66d9ef">this</span>.opacity, Key<span style="color:#f92672">?</span> key})
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">:</span> <span style="color:#66d9ef">super</span>(key: key);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#960050;background-color:#1e0010">@</span>override
</span></span><span style="display:flex;"><span>  Widget build(BuildContext context) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> Positioned(
</span></span><span style="display:flex;"><span>      child: Opacity(
</span></span><span style="display:flex;"><span>        opacity: opacity,
</span></span><span style="display:flex;"><span>        child: Text(text, style: <span style="color:#66d9ef">const</span> TextStyle(fontSize: <span style="color:#ae81ff">100</span>)),
</span></span><span style="display:flex;"><span>      ),
</span></span><span style="display:flex;"><span>      top: position,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
  </channel>
</rss>
