用Flutter制作动画应用

用Flutter制作动画应用

时间:2020-7-23 作者:gykj

任何平台的应用程序直观,美观,并为用户交互提供愉快的反馈,因此受到赞誉。动画是做到这一点的方法之一。

Flutter是一个跨平台框架,在过去两年中已经成熟,包括Web和桌面支持。它开发的应用程序流畅且美观,因此赢得了声誉。凭借其丰富的动画支持,声明性的UI编写方式,“热重载”以及其他功能,它现已成为一个完整的跨平台框架。

如果您是从Flutter开始的,并且想学习一种非常规的添加动画的方法,那么您来对地方了:我们将探索动画和运动小部件的领域,这是一种隐式添加动画的方法。

Flutter基于小部件的概念。应用程序的每个可视组件都是一个小部件-将其视为Android中的视图。Flutter使用Animation类,用于管理的“ AnimationController”对象和用于插值数据范围的“ Tween”来提供动画支持。这三个组件协同工作以提供流畅的动画。由于这需要手动创建和管理动画,因此被称为动画的一种显式方式。

现在,让我向您介绍动画和运动小部件。Flutter提供了许多固有支持动画的小部件。无需创建动画对象或任何控制器,因为所有动画均由此类小部件处理。只需为所需的动画选择适当的小部件,然后将其属性值传递给动画即可。该技术是一种隐式动画方法。

Flutter中的动画层次结构。(大型预览

上面的图表大致列出了Flutter中的动画层次,以及如何同时支持显式和隐式动画。

本文介绍的一些动画小部件包括:

  • AnimatedOpacity
  • AnimatedCrossFade
  • AnimatedAlign
  • AnimatedPadding
  • AnimatedSize
  • AnimatedPositioned

Flutter不仅提供预定义的动画小部件,而且还提供名为的通用小部件AnimatedWidget,可用于创建自定义的隐式动画小部件。从名称可以明显看出,这些小部件属于“动画”和“运动”小部件类别,因此它们具有一些共同的属性,这些属性使我们可以使动画更加平滑和美观。

现在让我解释这些通用属性,因为稍后将在所有示例中使用它们。

  • duration
    设置参数动画的持续时间。
  • reverseDuration
    反向动画的持续时间。
  • curve
    设置参数动画时要应用的曲线。内插值可以从线性分布中获取,或者,如果指定的话,可以从曲线中获取。

让我们通过创建一个简单的应用程序(称为“已报价”)开始旅程。每次应用启动时,它将显示随机报价。需要注意的两件事:首先,所有这些报价都将在应用程序中进行硬编码;其次,不会保存任何用户数据。

注意这些示例的所有文件都可以在GitHub上找到。

入门

应该安装Flutter,在继续之前,您需要对基本流程有所了解。一个很好的起点是“ 使用Google的Flutter进行真正的跨平台移动开发 ”。

在Android Studio中创建一个新的Flutter项目。

Android Studio中新的flutter项目菜单。(大型预览

这将打开一个新的项目向导,您可以在其中配置项目基础知识。

Flutter项目类型选择屏幕。(大型预览

在项目类型选择屏幕中,存在各种Flutter项目类型,每种类型都适合特定的场景。对于本教程,选择Flutter Application并按Next

现在,您需要输入一些特定于项目的信息:项目名称和路径,公司域等。看看下面的图片。

Flutter应用程序配置屏幕。(大型预览

添加项目名称,Flutter SDK路径,项目位置和可选的项目描述。按下一步

Flutter应用程序包名称屏幕。(大型预览

每个应用程序(无论是Android还是iOS)都需要一个唯一的程序包名称。通常,您使用网站域的反向名称;例如com.google或com.yahoo。按“ 完成”以生成有效的Flutter应用程序。

生成的样本项目。(大型预览

生成项目后,您应该会看到上面显示的屏幕。打开main.dart文件(在屏幕截图中突出显示)。这是主应用程序文件。该示例项目本身是完整的,可以直接在仿真器或物理设备上运行,而无需进行任何修改。

用以下代码片段替换main.dart文件的内容:

import 'package:animated_widgets/FirstPage.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'Animated Widgets',
     debugShowCheckedModeBanner: false,
     theme: ThemeData(
       primarySwatch: Colors.blue,
       accentColor: Colors.redAccent,
     ),
     home: FirstPage(),
   );
 }
}

这段代码通过添加与创建新应用程序有关的简单信息来清理main.dart文件。MyApp类返回一个对象:MaterialApp小部件,它提供了创建符合Material Design的应用程序的基本结构。为了使代码更加结构化,请在lib文件夹中创建两个新的dart文件:FirstPage.dartQuotes.dart

FirstPage.dart文件。(大型预览

FirstPage.dart将包含负责Quoted应用程序所需的所有可视元素(窗口小部件)的所有代码。所有动画都在此文件中处理。

注意在本文的后面,每个动画小部件的所有代码段均作为Scaffold小部件的子级添加到此文件中。有关更多信息,此示例在GitHub上可能有用。

首先将以下代码添加到FirstPage.dart。这是部分代码,稍后将在其中添加其他内容。

import 'dart:math';

import 'package:animated_widgets/Quotes.dart';
import 'package:flutter/material.dart';


class FirstPage extends StatefulWidget {
 @override
 State createState() {
   return FirstPageState();
 }
}

class FirstPageState extends State with TickerProviderStateMixin {

 bool showNextButton = false;
 bool showNameLabel = false;
 bool alignTop = false;
 bool increaseLeftPadding = false;
 bool showGreetings = false;
 bool showQuoteCard = false;
 String name = '';


 double screenWidth;
 double screenHeight;
 String quote;


 @override
 void initState() {
   super.initState();
   Random random = new Random();
   int quoteIndex = random.nextInt(Quotes.quotesArray.length);
   quote = Quotes.quotesArray[quoteIndex];
 }

 @override
 Widget build(BuildContext context) {

   screenWidth = MediaQuery.of(context).size.width;
   screenHeight = MediaQuery.of(context).size.height;

   return Scaffold(
     appBar: _getAppBar(),
     body: Stack(
       children: [
         // All other children will be added here.
      // In this article, all the children widgets are contained
      // in their own separate methods.
      // Just method calls should be added here for the respective child.
       ],
     ),
   );
 }
}
Quotes.dart文件。(大型预览

Quotes.dart文件包含了所有的硬编码的报价列表。这里要注意的一点是,该列表是静态对象。这意味着它可以在其他地方使用,而无需创建Quotes类的新对象。这是由设计选择的,因为上面的列表仅充当实用程序。

将以下代码添加到该文件:

class Quotes {
 static const quotesArray = [
   "Good, better, best. Never let it rest. 'Til your good is better and your better is best",
   "It does not matter how slowly you go as long as you do not stop.",
   "Only I can change my life. No one can do it for me."
 ];
}

现在项目骨架已经准备好了,让我们充实一下Quoted。

AnimatedOpacity

为了给应用程序增添个人风格,很高兴知道用户的名字,因此让我们提出要求并显示下一个按钮。在用户输入其名称之前,此按钮一直处于隐藏状态,并且在给出名称时会正常显示。我们需要按钮的可见性动画,但是有一个小部件吗?就在这里。

输入AnimatedOpacity。通过添加隐式动画支持,此窗口小部件以不透明度窗口小部件为基础。我们如何使用它?记住我们的场景:我们需要显示具有动画可见性的下一个按钮。我们将按钮窗口小部件包装在窗口小部件内AnimatedOpacity,输入一些适当的值,并添加条件以触发动画-Flutter可以处理其余部分。

_getAnimatedOpacityButton() {
  return AnimatedOpacity(
    duration: Duration(seconds: 1),
    reverseDuration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    opacity: showNextButton ? 1 : 0,
    child: _getButton(),
  );
}
用Flutter制作动画应用
下一个按钮的不透明度动画。(大型预览

AnimatedOpacity小部件具有两个必填属性:

  • opacity
    值1表示完全可见;值1表示完全可见。0(零)表示隐藏。进行动画制作时,Flutter在这两个极端之间进行插值。您可以看到如何放置条件以更改可见性,从而触发动画。
  • child
    将其可见性设置为动画的子窗口小部件。

现在,您应该了解使用隐式窗口小部件添加可见性动画是多么简单。并且所有这些小部件都遵循相同的准则,并且易于使用。让我们继续下一个。

AnimatedCrossFade

我们有用户名,但是小部件仍在等待输入。在上一步中,当用户输入其名称时,我们将显示下一个按钮。现在,当用户按下按钮时,我想停止接受输入并显示输入的名称。当然,有很多方法可以做到这一点,但是也许我们可以隐藏输入窗口小部件并显示不可编辑的文本窗口小部件。让我们使用AnimatedCrossFade小部件尝试一下。

此小部件需要两个子级,因为小部件会根据某种条件在它们之间交叉淡入淡出。使用此小部件时要记住的一件事是两个子项的宽度应相同。如果高度不同,则较高的窗口小部件将从底部修剪。在这种情况下,两个小部件将用作子级:输入和标签。

_getAnimatedCrossfade() {

  return AnimatedCrossFade(

    duration: Duration(seconds: 1),

    alignment: Alignment.center,

    reverseDuration: Duration(seconds: 1),

    firstChild: _getNameInputWidget(),

    firstCurve: Curves.easeInOut,

    secondChild: _getNameLabelWidget(),

    secondCurve: Curves.easeInOut,

    crossFadeState: showNameLabel ? CrossFadeState.showSecond : CrossFadeState.showFirst,

  );

}
用Flutter制作动画应用
输入小部件和名称小部件之间的淡入淡出。(大型预览

此小部件需要一组不同的强制性参数:

  • crossFadeState
    该状态确定要显示哪个孩子。
  • firstChild
    指定此小部件的第一个子级。
  • secondChild
    指定第二个孩子。

AnimatedAlign

此时,名称标签位于屏幕的中央。由于我们需要屏幕的中心来显示报价,因此顶部看起来会好得多。简而言之,名称标签窗口小部件的对齐方式应该从中心更改为顶部。并且将对齐方式更改与以前的淡入淡出动画一起设置动画不是很好吗?我们开始做吧。

与往常一样,可以使用多种技术来实现这一目标。由于名称标签窗口小部件已经居中对齐,因此对其进行动画处理比操作窗口小部件的顶部和左侧值简单得多。该AnimatedAlign小部件非常适合此工作。

要启动此动画,需要触发器。这个小部件的唯一目的是为路线更改设置动画,因此它只有几个属性:添加一个子项,设置其路线,触发路线更改,仅此而已。

_getAnimatedAlignWidget() {

  return AnimatedAlign(

duration: Duration(seconds: 1),

curve: Curves.easeInOut,

alignment: alignTop ? Alignment.topLeft : Alignment.center,

child: _getAnimatedCrossfade(),

  );

}
用Flutter制作动画应用
名称小部件的对齐动画。(大型预览

它只有两个强制属性:

  • child:
    其对齐方式将被修改的子级。
  • 对齐:
    必需的对齐值。

这个小部件确实很简单,但是结果很优雅。此外,我们看到了使用两个不同的动画小部件来创建更复杂的动画有多么容易。这就是动画小部件的美。

AnimatedPadding

现在,我们将用户名放在顶部,使用不同类型的动画小部件轻松地进行动画处理。我们在名称前添加问候语“嗨”。在顶部添加值“ Hi”的文本小部件将使其与问候文本小部件重叠,如下图所示。

问候语和名称小部件重叠。(大型预览

如果名称文本窗口小部件的左侧有填充,该怎么办?一定要增加左边的填充量,但是请稍等:我们可以增加一些动画的填充量吗?是的,那就是AnimatedPadding。为了使所有这些看起来更好,让问候文本小部件淡入,同时使名称文本小部件的填充增加。

_getAnimatedPaddingWidget() {

  return AnimatedPadding(

    duration: Duration(seconds: 1),

    curve: Curves.fastOutSlowIn,

    padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0),

    child: _getAnimatedCrossfade(),

  );

}

由于上面的动画应该仅在之前的动画对齐完成后才发生,因此我们需要延迟触发此动画。暂时脱离主题,这是一个讨论增加延迟的流行机制的好时机。Flutter提供了几种这样的技术,但是Future.delayed构造函数是更简单,更简洁和更具可读性的方法之一。例如,要在1秒后执行一段代码:

Future.delayed(Duration(seconds: 1), (){
    sum = a + b;    // This sum will be calculated after 1 second.
    print(sum);
});

由于延迟持续时间是已知的(根据先前的动画持续时间计算),因此可以在此间隔之后触发动画。

// Showing “Hi” after 1 second - greetings visibility trigger.
_showGreetings() {
  Future.delayed(Duration(seconds: 1), () {
    setState(() {
        showGreetings = true;
    });
  });
}

// Increasing the padding for name label widget after 1 second - increase padding trigger.
_increaseLeftPadding() {
  Future.delayed(Duration(seconds: 1), () {
    setState(() {
    increaseLeftPadding = true;
    });
  });
}
用Flutter制作动画应用
名称小部件的填充动画。(大型预览

该小部件仅具有两个必需属性:

  • child
    此小部件内的子项,将被应用填充。
  • padding
    要添加的空间量。

AnimatedSize

如今,任何具有某种动画的应用都将包括放大或缩小视觉组件以吸引用户的注意力(通常称为缩放动画)。为什么不在这里使用相同的技术?我们可以向用户显示从屏幕中心开始放大的励志名言。让我向您介绍该AnimatedSize小部件,该小部件可通过更改子级的大小来控制放大和缩小效果。

这个小部件在必需参数方面与其他小部件有所不同。我们需要Flutter所说的“股票行情指示器”。Flutter提供了一种方法,可以在触发新的框架事件时让对象知道。可以将其视为发出信号的信号,“立即执行!… 现在做!… 现在做!……”

AnimatedSize控件需要一个属性- VSYNC -它接受一个股票供应商。获取股票行情提供商的最简单方法是将Mixin添加到类中。有两种基本的股票代码提供程序实现:SingleTickerProviderStateMixin,它提供了一个单一股票; 和TickerProviderStateMixin,提供了几个。

代码的默认实现用于标记动画的帧。在这种情况下,采用后者。有关mixin的更多信息。

// Helper method to create quotes card widget.
_getQuoteCardWidget() {
  return Card(
    color: Colors.green,
    elevation: 8.0,
    child: _getAnimatedSizeWidget(),
  );
}
// Helper method to create animated size widget and set its properties.
_getAnimatedSizeWidget() {
  return AnimatedSize(
    duration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    vsync: this,
    child: _getQuoteContainer(),
  );
}
// Helper method to create the quotes container widget with different sizes.
_getQuoteContainer() {
  return Container(
    height: showQuoteCard ? 100 : 0,
    width: showQuoteCard ? screenWidth - 32 : 0,
    child: Center(
    child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 16),
        child: Text(quote, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 14),),
    ),
    ),
  );
}
// Trigger used to show the quote card widget.
_showQuote() {
  Future.delayed(Duration(seconds: 2), () {
    setState(() {
        showQuoteCard = true;
    });
  });
}
用Flutter制作动画应用
引号小部件的缩放动画。(大型预览

此小部件的强制属性:

  • vsync
    协调动画和帧更改所需的代码提供器。
  • child
    尺寸更改的孩子将被设置动画。

现在可以轻松驯服放大和缩小动画。

AnimatedPositioned

大!报价从中心放大,以引起用户的注意。如果在放大时从底部向上滑动怎么办?让我们尝试一下。此动作涉及使用报价小部件的位置并为位置属性的变化设置动画。AnimatedPositioned是完美的候选人。

每当指定位置更改时,此小部件都会在给定的时间内自动转换孩子的位置。需要注意的一点是:它仅在其父窗口小部件是“堆栈”时才有效。这个小部件非常简单易用。让我们来看看。

// Helper method to create the animated positioned widget.
// With position changes based on “showQuoteCard” flag.
_getAnimatedPositionWidget() {
  return AnimatedPositioned(
    duration: Duration(seconds: 1),
    curve: Curves.easeInOut,
    child: _getQuoteCardWidget(),
    top: showQuoteCard ? screenHeight/2 - 100 : screenHeight,
    left: !showQuoteCard ? screenWidth/2 : 12,
  );
}
用Flutter制作动画应用
用引号缩放动画定位。(大型预览

此小部件仅具有一个必需属性:

  • child
    将更改其位置的小部件。

如果不希望孩子的大小随其位置发生变化,则此小部件的更有效的选择是SlideTransition

这是我们完整的动画:

用Flutter制作动画应用
所有的动画小部件在一起。(大型预览

结论

动画是用户体验不可或缺的一部分。静态应用或带有垃圾动画的应用不仅降低了用户保留率,而且还降低了开发结果的声誉。

如今,大多数流行的应用程序都具有某种微妙的动画效果,以取悦用户。对用户请求的动画反馈还可以吸引他们探索更多内容。Flutter为跨平台开发提供了许多功能,包括对平滑和响应动画的丰富支持。

Flutter具有强大的插件支持,使我们能够使用其他开发人员的动画。现在它已经成熟到1.9版,受到了社区的热烈欢迎,Flutter必将在未来变得更好。我想说现在是学习Flutter的好时机!

版权所有:https://www.eraycloud.com 转载请注明出处