📜  Flutter – 使用动画

📅  最后修改于: 2021-09-02 05:53:37             🧑  作者: Mango

无论何时构建应用动画在设计用户体验方面都起着至关重要的作用。人们倾向于喜欢流畅且设计精美的应用程序。 Flutter包提供了多种方法来在我们的应用程序中创建和使用动画。我们将讨论用于处理动画的内置Flutter小部件。

Flutter的动画

如流程图所示,在Flutter处理动画的框架提供了不同容量和实现的小部件。所有动画小部件中存在的基本属性是DurationCurve 。持续时间是小部件动画的时间,曲线定义对象动画的方式和从开始到结束的方式(动画从开始到结束的流程)。 flutter内置的动画小部件可以分为两大类。

隐式小部件

这些是flutter提供的最简单的小部件。这些小部件无需开发人员做太多工作即可实现。这些是非常基本的动画技术,因此它们没有很多可供更改的选项。它们具有不连续的单向动画。隐式小部件又可以分为两类

  • AnimatedXYZ :这里的XYZ是可用于动画的特定小部件。这些是Flutter可用的基本小部件的动画版本。以下是现有 XYZ 小部件的一些隐式 AnimatedXYZ。
  1. 对齐 → 动画对齐
  2. 容器 → 动画容器
  3. DefaultTextStyle → AnimatedDefaultTextStyle
  4. 填充 → AnimatedPadding
  5. 定位 → 动画定位
  • TweenAnimationBuilder :这些小部件将给定的小部件从初始值 ( Tween.begin ) 动画到最终值 ( Tween.end )。此小部件可用于为简单动画制作自定义小部件。它接受一个 builder 属性,该属性根据其参数中提供的值构建动画。我们还可以在onEnd回调的帮助下提供动画完成时需要执行的操作。

显式小部件

这些小部件对动画小部件提供更精细的控制。它们具有控制小部件的重复和移动的属性。这些小部件需要一个 AnimationController 来实现它们提供的精细控制。这个控制器可以在initStatedispose中定义 状态 为了更好的使用。显式小部件可以归类为

  • XYZTransition :这里的XYZ是一个特定的小部件,可用作 Transition。这些是内置过渡,可提供对隐式动画的更多控制。它们可以被认为是AnimatedXYZ小部件的扩展。一些可用的显式XYZTransition是:
  1. 大小转换
  2. 淡入淡出
  3. 对齐过渡
  4. 旋转过渡
  5. 定位转换
  6. 装饰框过渡
  • AnimatedBuilder/ AnimatedWidget :当预定义的XYZTransition中没有可用的小部件时,我们可以使用AnimatedBuilder / AnimatedWidget 。它们适用于我们想要显式动画的自定义小部件。如果我们可以在同一个小部件中定义动画,那么我们可以使用 AnimatedBuilder 否则,如果我们定义一个新的小部件,我们可以使用 Animated Widget 扩展定义的小部件。

现在我们已经介绍了内置动画小部件的基本定义。我们将一一查看每个示例。

我们将设置应用程序。首先,我们将在根目录中创建图像目录并将两个图像添加为bird.png 下面提供了hypno.png

催眠.png

鸟.png

pubspec.yaml中,在assets 下添加以下几行,然后单击 Pub get 以使用图像

assets:
    - images/ #Add

现在已经添加了资源,我们将定义 lib 目录的内容。我们将创建四个新的dart文件作为animation_xyz。dart,吐温动画。dart,xyz_transition。 dart和 builder_animation。除了主要的dart。dart。

主要的。dart有以下代码

Dart
import 'package:flutter/material.dart';
import 'builder_animation.dart';
import 'xyz_transition.dart';
import 'tween_animation.dart';
import 'animated_xyz.dart';
  
void main() {
  runApp(MyApp());
}
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Animation Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
      ),
      home: Home(),
    );
  }
}
  
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 4,
      child: Scaffold(
          
        // define appbar here
        appBar: AppBar(
            
          // add tabs to the app
          bottom: TabBar(
            tabs: [
              Tab(text: 'Ani..XYZ'),
              Tab(text: 'Tween'),
              Tab(text: 'XYZTra..'),
              Tab(text: 'Builder'),
            ],
          ),
          title: Text('GeeksforGeeks'),
        ),
        body: TabBarView(
            
          // animations
          children: [
            AnimatedXYZ(),
            TweenAnimation(),
            XYZTransition(),
            BuilderAnimation(),
          ],
        ),
      ),
    );
  }
}


Dart
import 'package:flutter/material.dart';
  
class AnimatedXYZ extends StatefulWidget {
  @override
  _AnimatedXYZState createState() => _AnimatedXYZState();
}
  
// building the container class
class _AnimatedXYZState extends State {
  bool _toggle = true;
  
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Padding(
              padding: const EdgeInsets.all(20),
              child: Text(
                'AnimatedContainer',
                style: TextStyle(fontSize: 20),
              ),
            ),
              
            // using the AnimatedContainer widget
            AnimatedContainer(
              decoration: BoxDecoration(
                color: _toggle == true
                    ? Colors.blueAccent
                    : Colors.deepPurpleAccent,
                borderRadius: BorderRadius.all(Radius.circular(8)),
              ),
              curve: Curves.easeInOutBack,
              duration: Duration(seconds: 1),
              height: _toggle == true ? 100 : 400,
              width: _toggle == true ? 100 : 200,
            ),
            SizedBox(
              height: 20,
            ),
            RaisedButton(
              onPressed: () {
                setState(() {
                  _toggle = !_toggle;
                });
              },
              child: Text('Animate'),
            )
          ],
        ),
      ),
    );
  }
}


Dart
import 'package:flutter/material.dart';
  
class TweenAnimation extends StatefulWidget {
  @override
  _TweenAnimationState createState() => _TweenAnimationState();
}
  
class _TweenAnimationState extends State {
  Color c1 = Colors.white;
  Color c2 = Colors.yellow;
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'TweenAnimation',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(
              height: 10,
            ),
              
            // Using TweenAnimationBuilder
            TweenAnimationBuilder(
              tween: ColorTween(begin: c1, end: c2),
              duration: Duration(seconds: 1),
              builder: (_, Color color, __) {
                return ColorFiltered(
                    
                  // image assets
                  child: Image.asset(
                    'images/bird.png',
                    height: 180,
                  ),
                  colorFilter: ColorFilter.mode(color, BlendMode.modulate),
                );
              },
            ),
            SizedBox(
              height: 20,
            ),
              
            // button
            RaisedButton(
              onPressed: () {
                setState(() {
                  c1 = c1 == Colors.white ? Colors.yellow : Colors.white;
                  c2 = c2 == Colors.yellow ? Colors.white : Colors.yellow;
                });
              },
              child: Text('Change Color'),
            )
          ],
        ),
      ),
    );
  }
}


Dart
import 'package:flutter/material.dart';
  
class XYZTransition extends StatefulWidget {
  @override
  _XYZTransitionState createState() => _XYZTransitionState();
}
  
class _XYZTransitionState extends State
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  
  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(seconds: 3),
    )..repeat();
  }
  
  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'RotationalTransition',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(
              height: 10,
            ),
              
            // assign action to gestures
            GestureDetector(
              onTap: () {
                _animationController.isAnimating
                    ? _animationController.stop()
                    : _animationController.repeat();
              },
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                        
                      // defining the animation type
                      RotationTransition(
                        child: Image.asset('images/hypno.png',
                            height: 150, width: 150),
                        alignment: Alignment.center,
                        turns: _animationController,
                      ),
                      SizedBox(
                        height: 20,
                      ),
                      Text('Tap to STOP/ START')
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}


Dart
import 'package:flutter/material.dart';
  
class BuilderAnimation extends StatefulWidget {
  @override
  _BuilderAnimationState createState() => _BuilderAnimationState();
}
  
class _BuilderAnimationState extends State
    with TickerProviderStateMixin {
  Animation _starAnimation;
  AnimationController _starAnimationController;
  
  bool toggle = false;
  
  // animation controller
  @override
  void initState() {
    super.initState();
    _starAnimationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    _starAnimation = Tween(begin: 140.0, end: 160.0).animate(CurvedAnimation(
        curve: Curves.elasticInOut, parent: _starAnimationController));
  
    _starAnimationController.addStatusListener((AnimationStatus status) {
      if (status == AnimationStatus.completed) {
        _starAnimationController.repeat();
      }
    });
  }
  
  @override
  void dispose() {
    super.dispose();
    _starAnimationController?.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            'AnimatedBuilder',
            style: TextStyle(fontSize: 20),
          ),
          SizedBox(
            height: 10,
          ),
            
          // animated container 
          // goes as a child
          Container(
            height: 200,
            width: 200,
            child: AnimatedBuilder(
              animation: _starAnimationController,
              builder: (context, child) {
                return Center(
                  child: Container(
                    child: Center(
                      child: Icon(
                        Icons.audiotrack,
                        color: Colors.orangeAccent,
                        size: _starAnimation.value,
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          SizedBox(
            height: 10,
          ),
            
          // button
          RaisedButton(
            child: Text('START/ STOP'),
            onPressed: () {
              toggle = !toggle;
              toggle == true
                  ? _starAnimationController.forward()
                  : _starAnimationController.stop();
            },
          ),
        ],
      ),
    );
  }
}


在这里,我们制作了一个包含应用栏的 Scaffold,除此之外,应用栏还有一个Tabbar ,用于显示不同动画类型的不同屏幕。

默认情况下,主屏幕显示了我们在 animation_xyz 中定义的 AnimatedXYZ 小部件。dart文件。这显示了我们已经讨论过的第一种类型的隐式小部件。该文件具有以下代码

Dart

import 'package:flutter/material.dart';
  
class AnimatedXYZ extends StatefulWidget {
  @override
  _AnimatedXYZState createState() => _AnimatedXYZState();
}
  
// building the container class
class _AnimatedXYZState extends State {
  bool _toggle = true;
  
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Padding(
              padding: const EdgeInsets.all(20),
              child: Text(
                'AnimatedContainer',
                style: TextStyle(fontSize: 20),
              ),
            ),
              
            // using the AnimatedContainer widget
            AnimatedContainer(
              decoration: BoxDecoration(
                color: _toggle == true
                    ? Colors.blueAccent
                    : Colors.deepPurpleAccent,
                borderRadius: BorderRadius.all(Radius.circular(8)),
              ),
              curve: Curves.easeInOutBack,
              duration: Duration(seconds: 1),
              height: _toggle == true ? 100 : 400,
              width: _toggle == true ? 100 : 200,
            ),
            SizedBox(
              height: 20,
            ),
            RaisedButton(
              onPressed: () {
                setState(() {
                  _toggle = !_toggle;
                });
              },
              child: Text('Animate'),
            )
          ],
        ),
      ),
    );
  }
}

该文件有一个包含不同小部件的 Column 和一个AnimatedContainer ,除了正常功能外,还定义了曲线和持续时间。当按下按钮时,容器的大小和颜色会发生变化,如下所示:

在下一个屏幕中,我们在tween_animation 中制作了一个 tween 动画。dart文件。在这个动画中,我们有一张鸟的图片,按下按钮会改变鸟的颜色。该文件具有以下代码

Dart

import 'package:flutter/material.dart';
  
class TweenAnimation extends StatefulWidget {
  @override
  _TweenAnimationState createState() => _TweenAnimationState();
}
  
class _TweenAnimationState extends State {
  Color c1 = Colors.white;
  Color c2 = Colors.yellow;
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'TweenAnimation',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(
              height: 10,
            ),
              
            // Using TweenAnimationBuilder
            TweenAnimationBuilder(
              tween: ColorTween(begin: c1, end: c2),
              duration: Duration(seconds: 1),
              builder: (_, Color color, __) {
                return ColorFiltered(
                    
                  // image assets
                  child: Image.asset(
                    'images/bird.png',
                    height: 180,
                  ),
                  colorFilter: ColorFilter.mode(color, BlendMode.modulate),
                );
              },
            ),
            SizedBox(
              height: 20,
            ),
              
            // button
            RaisedButton(
              onPressed: () {
                setState(() {
                  c1 = c1 == Colors.white ? Colors.yellow : Colors.white;
                  c2 = c2 == Colors.yellow ? Colors.white : Colors.yellow;
                });
              },
              child: Text('Change Color'),
            )
          ],
        ),
      ),
    );
  }
}

在这个文件中,我们简单地定义了一个包含不同小部件的列,除此之外还有一个TweenAnimationBuilder ,它接受补间的类型(这里我们使用了ColorTween )和动画的持续时间。它还有一个 builder 属性,用于构建根据补间提供的小部件。

现在我们已经完成了隐式动画。我们将在接下来的两个屏幕中定义显式动画。显式动画小部件提供了对如何管理动画的更多手动控制。

第三个屏幕显示了RotationTransition的示例。代码如下

Dart

import 'package:flutter/material.dart';
  
class XYZTransition extends StatefulWidget {
  @override
  _XYZTransitionState createState() => _XYZTransitionState();
}
  
class _XYZTransitionState extends State
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  
  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(seconds: 3),
    )..repeat();
  }
  
  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'RotationalTransition',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(
              height: 10,
            ),
              
            // assign action to gestures
            GestureDetector(
              onTap: () {
                _animationController.isAnimating
                    ? _animationController.stop()
                    : _animationController.repeat();
              },
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                        
                      // defining the animation type
                      RotationTransition(
                        child: Image.asset('images/hypno.png',
                            height: 150, width: 150),
                        alignment: Alignment.center,
                        turns: _animationController,
                      ),
                      SizedBox(
                        height: 20,
                      ),
                      Text('Tap to STOP/ START')
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

显式小部件为我们提供了更多的手动控制。要访问这些控件,我们需要有一个控制器。所以我们为此定义了一个AnimationController对象。我们需要在构建屏幕时初始化动画,并在移动到另一个屏幕时处理它。这里我们使用RotationTransition无限旋转给定的图像。可以通过点击屏幕上提到的图像来停止/重新启动旋转。过渡接受AnimationController对象作为轮次。我们已经定义在initState 中一旦完成就重复动画。

上面说的显式动画在预定义的 XYZTransition 上工作,但是为了在我们的自定义对象上工作,我们可以使用AnimationBuilder来构建我们自己的动画。代码如下

Dart

import 'package:flutter/material.dart';
  
class BuilderAnimation extends StatefulWidget {
  @override
  _BuilderAnimationState createState() => _BuilderAnimationState();
}
  
class _BuilderAnimationState extends State
    with TickerProviderStateMixin {
  Animation _starAnimation;
  AnimationController _starAnimationController;
  
  bool toggle = false;
  
  // animation controller
  @override
  void initState() {
    super.initState();
    _starAnimationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    _starAnimation = Tween(begin: 140.0, end: 160.0).animate(CurvedAnimation(
        curve: Curves.elasticInOut, parent: _starAnimationController));
  
    _starAnimationController.addStatusListener((AnimationStatus status) {
      if (status == AnimationStatus.completed) {
        _starAnimationController.repeat();
      }
    });
  }
  
  @override
  void dispose() {
    super.dispose();
    _starAnimationController?.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            'AnimatedBuilder',
            style: TextStyle(fontSize: 20),
          ),
          SizedBox(
            height: 10,
          ),
            
          // animated container 
          // goes as a child
          Container(
            height: 200,
            width: 200,
            child: AnimatedBuilder(
              animation: _starAnimationController,
              builder: (context, child) {
                return Center(
                  child: Container(
                    child: Center(
                      child: Icon(
                        Icons.audiotrack,
                        color: Colors.orangeAccent,
                        size: _starAnimation.value,
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
          SizedBox(
            height: 10,
          ),
            
          // button
          RaisedButton(
            child: Text('START/ STOP'),
            onPressed: () {
              toggle = !toggle;
              toggle == true
                  ? _starAnimationController.forward()
                  : _starAnimationController.stop();
            },
          ),
        ],
      ),
    );
  }
}

这个小部件显示了一个反复增长的音乐曲目图标。大多数细节与上面定义的显式XYZTransition保持相同。但是在这里我们必须传递一个带有孩子和上下文的构建器。在这里,我们必须将控制器传递给AnimatedBuilder的 animation 属性。 Button 用于启动和停止动画。

所有动画屏幕的输出: