Flutter制作自己的第一个全平台APP学习之Animation篇

已收录   阅读次数: 818
2021-07-0306:54:03 发表评论
摘要

这一篇文章主要是学习flutter中各种Animation,也就是各种动画效果,比如旋转图片,左旋转,右旋转,上下颠倒,放大,缩小,隐藏,显示,旋转的圆等等之类的,会让整个App在运行过程中显得更加流畅,生动,可以达到增加用户留存度,常驻时间可以延长等作用……

分享至:
Flutter制作自己的第一个全平台APP学习之Animation篇

开篇寄语

这一篇文章主要是学习flutter中各种Animation,也就是各种动画效果,比如旋转图片,左旋转,右旋转,上下颠倒,放大,缩小,隐藏,显示,旋转的圆等等之类的,会让整个App在运行过程中显得更加流畅,生动,可以达到增加用户留存度,常驻时间可以延长等作用。

前情提要

内容详情

以下内容大多是更改lib下的main.dart文件内容,删掉里面的内容,复制粘贴代码,开启调试就可以出现了。

hero

  • 从源路线跳转到目标地路线的小部件。为源路由定义一个Hero,为目标路由定义另一个,并为每个角色分配相同的标签,试举例如下:
import 'package:flutter/material.dart';

void main() => runApp(HeroApp());

class HeroApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Transition Demo',
      home: MainScreen(),
    );
  }
}

class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Main Screen'),
      ),
      body: GestureDetector(
        onTap: () {
          Navigator.push(context, MaterialPageRoute(builder: (_) {
            return DetailScreen();
          }));
        },
        child: Hero(
          tag: 'imageHero',
          child: Image.network(
            'https://picsum.photos/250?image=9',
          ),
        ),
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: () {
          Navigator.pop(context);
        },
        child: Center(
          child: Hero(
            tag: 'imageHero',
            child: Image.network(
              'https://picsum.photos/250?image=9',
            ),
          ),
        ),
      ),
    );
  }
}

生成效果如下图所示:

AnimateOpacity

  • 透明度变低,变高,任意变换,试举例如下:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Woolha.com Flutter Tutorial',
      home: _AnimatedOpacityExample(),
    );
  }
}

class _AnimatedOpacityExample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AnimatedOpacityExampleState();
  }
}

class _AnimatedOpacityExampleState extends State<_AnimatedOpacityExample> {
  double _opacity = 0.1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Woolha.com Flutter Tutorial'),
      ),
      body: SizedBox(
        width: double.infinity,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            AnimatedOpacity(
              opacity: _opacity,
              duration: const Duration(seconds: 3),
              curve: Curves.bounceInOut,
              onEnd: () {
                print('Animation ends');
              },
              child: Container(
                width: 300,
                height: 50,
                color: Colors.redAccent,
                child: const Center(
                  child: const Text('luckydesigner.space',
                      style:
                          const TextStyle(fontSize: 24, color: Colors.white)),
                ),
              ),
            ),
            ElevatedButton(
              child: const Text('Change opacity'),
              onPressed: () {
                setState(() {
                  _opacity = _opacity == 0.1 ? 1 : 0.1;
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}

生成的效果如下图所示:

AnimatedContainer

  • Container容器的动画效果,效果复杂多变,试举例如下:
/// Flutter code sample for AnimatedContainer

// The following example (depicted above) transitions an AnimatedContainer
// between two states. It adjusts the `height`, `width`, `color`, and
// [alignment] properties when tapped.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

/// This is the main application widget.
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        body: const MyStatefulWidget(),
      ),
    );
  }
}

/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({Key? key}) : super(key: key);

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        setState(() {
          selected = !selected;
        });
      },
      child: Center(
        child: AnimatedContainer(
          width: selected ? 200.0 : 100.0,
          height: selected ? 100.0 : 200.0,
          color: selected ? Colors.red : Colors.blue,
          alignment: Alignment.center,
          duration: const Duration(seconds: 2),
          curve: Curves.fastOutSlowIn,
          child: const FlutterLogo(size: 75),
        ),
      ),
    );
  }
}

生成效果如下图所示:

Transform

  • 包括旋转,缩放和移动,试举例如下:

旋转,代码如下:

import 'dart:math' as math;
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: _AnimatedWidgetExample(),
    );
  }
}

class _AnimatedWidgetExample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new _AnimatedWidgetExampleState();
}

class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample>
    with TickerProviderStateMixin {
  late AnimationController _controller;

  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: Center(
        child: RotatingSquare(controller: _controller),
      ),
    );
  }
}

class RotatingSquare extends AnimatedWidget {
  const RotatingSquare({Key? key, required AnimationController controller})
      : super(key: key, listenable: controller);

  Animation<double> get _progress => listenable as Animation<double>;

  @override
  Widget build(BuildContext context) {
    return Transform.rotate(
      angle: _progress.value * 2.0 * math.pi,
      child: Container(
        width: 300.0,
        height: 300.0,
        color: Colors.teal,
        child: const Center(
          child: Text(
            'Luckydesigner.space',
            style: TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.bold,
              fontSize: 24,
            ),
          ),
        ),
      ),
    );
  }
}

缩放,代码如下:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: _AnimatedWidgetExample(),
    );
  }
}

class _AnimatedWidgetExample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new _AnimatedWidgetExampleState();
}

class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample>
    with TickerProviderStateMixin {
  late AnimationController _controller;

  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: Center(
        child: RotatingSquare(controller: _controller),
      ),
    );
  }
}

class RotatingSquare extends AnimatedWidget {
  const RotatingSquare({Key? key, required AnimationController controller})
      : super(key: key, listenable: controller);

  Animation<double> get _progress => listenable as Animation<double>;

  @override
  Widget build(BuildContext context) {
    return Transform.scale(
      scale: _progress.value,
      child: Container(
        width: 300.0,
        height: 300.0,
        color: Colors.teal,
        child: const Center(
          child: Text(
            'Luckydesigner.space',
            style: TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.bold,
              fontSize: 24,
            ),
          ),
        ),
      ),
    );
  }
}

移动,代码如下:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: _AnimatedWidgetExample(),
    );
  }
}

class _AnimatedWidgetExample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new _AnimatedWidgetExampleState();
}

class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample>
    with TickerProviderStateMixin {
  late AnimationController _controller;

  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: Center(
        child: RotatingSquare(controller: _controller),
      ),
    );
  }
}

class RotatingSquare extends AnimatedWidget {
  const RotatingSquare({Key? key, required AnimationController controller})
      : super(key: key, listenable: controller);

  Animation<double> get _progress => listenable as Animation<double>;

  @override
  Widget build(BuildContext context) {
    return Transform.translate(
      offset: Offset(0.0, _progress.value * 100),
      child: Container(
        width: 300.0,
        height: 300.0,
        color: Colors.teal,
        child: const Center(
          child: Text(
            'Luckydesigner.space',
            style: TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.bold,
              fontSize: 24,
            ),
          ),
        ),
      ),
    );
  }
}
Flutter制作自己的第一个全平台APP学习之Animation篇
旋转
Flutter制作自己的第一个全平台APP学习之Animation篇
缩放
Flutter制作自己的第一个全平台APP学习之Animation篇
移动

AnimatedList

  • 此小部件的 AnimatedListState 可用于动态插入或删除项目。要引用 AnimatedListState,请提供 GlobalKey 或使用项目输入回调中的静态方法,试举例如下:
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ListView Example',
      home: AnimatedListExample(),
    );
  }
}

class AnimatedListExample extends StatefulWidget {
  @override
  AnimatedListExampleState createState() => new AnimatedListExampleState();
}

class AnimatedListExampleState extends State<AnimatedListExample> {
  final GlobalKey<AnimatedListState> _listKey = GlobalKey();

  List<String> _data = [
    WordPair.random().toString(),
    WordPair.random().toString(),
    WordPair.random().toString(),
  ];

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Animated List'),
        backgroundColor: Colors.blueAccent,
      ),
      persistentFooterButtons: <Widget>[
        ElevatedButton(
          child: Text(
            'Add an item',
            style: TextStyle(fontSize: 20, color: Colors.white),
          ),
          onPressed: () {
            _addAnItem();
          },
        ),
        ElevatedButton(
          child: Text(
            'Remove last',
            style: TextStyle(fontSize: 20, color: Colors.white),
          ),
          onPressed: () {
            _removeLastItem();
          },
        ),
        ElevatedButton(
          child: Text(
            'Remove all',
            style: TextStyle(fontSize: 20, color: Colors.white),
          ),
          onPressed: () {
            _removeAllItems();
          },
        ),
      ],
      body: AnimatedList(
        key: _listKey,
        initialItemCount: _data.length,
        itemBuilder: (context, index, animation) =>
            _buildItem(context, _data[index], animation),
      ),
    );
  }

  Widget _buildItem(
      BuildContext context, String item, Animation<double> animation) {
    TextStyle textStyle = new TextStyle(fontSize: 20);

    return Padding(
      padding: const EdgeInsets.all(2.0),
      child: SizeTransition(
        sizeFactor: animation,
        axis: Axis.vertical,
        child: SizedBox(
          height: 50.0,
          child: Card(
            child: Center(
              child: Text(item, style: textStyle),
            ),
          ),
        ),
      ),
    );
  }

  void _addAnItem() {
    _data.insert(0, WordPair.random().toString());
    _listKey.currentState?.insertItem(0);
  }

  void _removeLastItem() {
    String itemToRemove = _data[0];

    _listKey.currentState?.removeItem(
      0,
      (BuildContext context, Animation<double> animation) =>
          _buildItem(context, itemToRemove, animation),
      duration: const Duration(milliseconds: 250),
    );

    _data.removeAt(0);
  }

  void _removeAllItems() {
    final int itemCount = _data.length;

    for (var i = 0; i < itemCount; i++) {
      String itemToRemove = _data[0];
      _listKey.currentState?.removeItem(
        0,
        (BuildContext context, Animation<double> animation) =>
            _buildItem(context, itemToRemove, animation),
        duration: const Duration(milliseconds: 250),
      );

      _data.removeAt(0);
    }
  }
}

生成效果如下图所示:

Flutter制作自己的第一个全平台APP学习之Animation篇

AnimatedCrossFade

  • 一个小部件,它在两个给定的子页面交叉淡入淡出并在它们的大小之间进行动画处理,试举例如下:
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: _AnimatedCrossFadeExample(),
    );
  }
}

class _AnimatedCrossFadeExample extends StatefulWidget {
  @override
  _AnimatedCrossFadeExampleState createState() =>
      new _AnimatedCrossFadeExampleState();
}

class _AnimatedCrossFadeExampleState extends State<_AnimatedCrossFadeExample> {
  CrossFadeState _crossFadeState = CrossFadeState.showFirst;

  @override
  void initState() {
    super.initState();

    Future.delayed(const Duration(seconds: 3), () {
      setState(() {
        _crossFadeState = CrossFadeState.showSecond;
      });
    });

    Future.delayed(const Duration(seconds: 5), () {
      setState(() {
        _crossFadeState = CrossFadeState.showFirst;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: Center(
        child: AnimatedCrossFade(
          crossFadeState: _crossFadeState,
          duration: const Duration(seconds: 2),
          reverseDuration: const Duration(seconds: 3),
          firstCurve: Curves.bounceInOut,
          secondCurve: Curves.easeInBack,
          firstChild: const Icon(Icons.text_rotate_up, size: 100),
          secondChild: const Icon(Icons.text_rotate_vertical, size: 200),
          layoutBuilder: (Widget topChild, Key topChildKey, Widget bottomChild,
              Key bottomChildKey) {
            return Stack(
              children: <Widget>[
                Positioned(
                  key: bottomChildKey,
                  left: 100.0,
                  top: 100.0,
                  child: bottomChild,
                ),
                Positioned(
                  key: topChildKey,
                  child: topChild,
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

生成效果如下图所示:

AnimatedPadding

  • Padding 的动画版本,只要给定的插图发生变化,它就会在给定的持续时间内自动转换缩进,官方版本给出的源代码,试举例如下:
/// Flutter code sample for AnimatedPadding

// The following code implements the [AnimatedPadding] widget, using a [curve] of
// [Curves.easeInOut].

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

/// This is the main application widget.
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        body: const MyStatefulWidget(),
      ),
    );
  }
}

/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({Key? key}) : super(key: key);

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  double padValue = 0.0;
  void _updatePadding(double value) {
    setState(() {
      padValue = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        AnimatedPadding(
          padding: EdgeInsets.all(padValue),
          duration: const Duration(seconds: 2),
          curve: Curves.easeInOut,
          child: Container(
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height / 5,
            color: Colors.blue,
          ),
        ),
        Text('Padding: $padValue'),
        ElevatedButton(
            child: const Text('Change padding'),
            onPressed: () {
              _updatePadding(padValue == 0.0 ? 100.0 : 0.0);
            }),
      ],
    );
  }
}

生成效果如下图所示:

SlideTransition

  • 变速加载动作特效,可以使元素以不同速度展示,试以官方文档给出的例子以演示:
/// Flutter code sample for SlideTransition

// The following code implements the [SlideTransition] as seen in the video
// above:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

/// This is the main application widget.
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        body: const Center(
          child: MyStatefulWidget(),
        ),
      ),
    );
  }
}

/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({Key? key}) : super(key: key);

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller = AnimationController(
    duration: const Duration(seconds: 2),
    vsync: this,
  )..repeat(reverse: true);
  late final Animation<Offset> _offsetAnimation = Tween<Offset>(
    begin: Offset.zero,
    end: const Offset(1.5, 0.0),
  ).animate(CurvedAnimation(
    parent: _controller,
    curve: Curves.elasticIn,
  ));

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _offsetAnimation,
      child: const Padding(
        padding: EdgeInsets.all(8.0),
        child: FlutterLogo(size: 150.0),
      ),
    );
  }
}

生成的效果如下图所示:

flip card

  • 纸牌翻转效果,实现它,需要安装一个名为flip card的支撑库,安装和使用地址可以到pub.dev查看。
  • 试举例如下:
import 'package:flutter/material.dart';
import 'package:flip_card/flip_card.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Welcome to Flutter',
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Welcome to Flutter'),
          ),
          body: Center(
            child: FlipCard(
              direction: FlipDirection.HORIZONTAL, // default
              front: FractionallySizedBox(
                widthFactor: 0.8,
                heightFactor: 0.3,
                alignment: Alignment.center,
                child: Container(
                    color: Colors.purple,
                    child: const Align(
                        alignment: Alignment.center,
                        child: Text(
                          "front",
                          textAlign: TextAlign.center,
                        ))),
              ),
              back: FractionallySizedBox(
                widthFactor: 0.8,
                heightFactor: 0.3,
                alignment: Alignment.center,
                child: Container(
                    color: Colors.red,
                    child: const Align(
                        alignment: Alignment.center,
                        child: Text(
                          "back",
                          textAlign: TextAlign.center,
                        ))),
              ),
            ),
          ),
        ));
  }
}

生成效果如下图所示:

  • 我的微信
  • 微信扫一扫加好友
  • weinxin
  • 我的微信公众号
  • 扫描关注公众号
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: