Flutter实现抽屉动画

这篇会深化View拖拽实例,利用Flutter Animation、插值器以及AnimatedBuilder教大家实现带动画的抽屉效果。先来看效果:

通过构思,我们可以设想到实现抽屉的方式就是用Stack控件将两个Widget叠加显示,用GestureDetector监听手势滑动,动态移动顶层的Widget,当监听到手势结束的时候根据手势滑动的距离动态将顶部Widget利用动画效果滑动到结束位置即可。

实现底部Widget

class DownDrawerWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(child: Center(child: Text("底部Widget",),),);
  }
}

这个Widget太简单了,就不细说了。

实现顶部Widget

class UpDrawerWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(child: Center(child: Text("顶部Widget",),),);
  }
}

实现方式和底部是一样的。

实现可以移动的容器

上面两个Widget都是单纯用来显示的Widget,因此继承了StatelessWidget。接下来我们需要根据手势动态移动顶部的Widget,因此需要继承StatefulWidget。

// 顶部Widget
class HomePageWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => HomePageState();
}

class HomePageState extends State<HomePageWidget>
    with SingleTickerProviderStateMixin {

  @override
  void initState() {...}

  @override
  void dispose() {...}

  @override
  Widget build(BuildContext context) {...}

  void _onViewDragDown(DragDownDetails callback) {...}

  void _onViewDrag(DragUpdateDetails callback) {...}

  void _onViewDragUp(DragEndDetails callback) {...}
}

初始化状态initState()

这个方法是在Widget初始化的时候系统的回调函数,我们需要在该函数中初始化动画

AnimationController controller;
@override
void initState() {
    // 初始化动画控制器,这里限定动画时常为200毫秒
    controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
    // vsync对象会绑定动画的定时器到一个可视的widget,所以当widget不显示时,动画定时器将会暂停,当widget再次显示时,动画定时器重新恢复执行,这样就可以避免动画相关UI不在当前屏幕时消耗资源。
    // 当使用vsync: this的时候,State对象必须with SingleTickerProviderStateMixin或TickerProviderStateMixin;TickerProviderStateMixin适用于多AnimationController的情况。

    // 设置动画曲线,就是动画插值器
    // 通过这个链接可以了解更多差值器,https://docs.flutter.io/flutter/animation/Curves-class.html,我们这里使用带回弹效果的bounceOut。
    CurvedAnimation curve =
        new CurvedAnimation(parent: controller, curve: Curves.bounceOut);

    // 增加动画监听,当手势结束的时候通过动态计算到达目标位置的距离实现动画效果。curve.value为当前动画的值,取值范围0~1。
    curve.addListener(() {
      double animValue = curve.value;
      double offset = dragUpDownX - dragDownX;
      double toPosition;

      // 右滑
      if (offset > 0) {
        if (offset > maxDragX / 5) {
          // 打开
          toPosition = maxDragX;
          isOpenState = true;
        } else {
          if (isOpenState) {
            toPosition = maxDragX;
            isOpenState = true;
          } else {
            toPosition = 0.0;
            isOpenState = false;
          }
        }
      } else {
        if (offset < (-maxDragX / 2.0)) {
          // 关
          toPosition = 0.0;
          isOpenState = false;
        } else {
          if (isOpenState) {
            toPosition = maxDragX;
            isOpenState = true;
          } else {
            toPosition = 0.0;
            isOpenState = false;
          }
        }
      }

      dragOffset = (toPosition - dragUpDownX) * animValue + dragUpDownX;
      // 刷新位置
      setState(() {});
    });
  }

结束Widget dispose()

当Widget不可用将被回收的时候,系统会回调dispose()方法,我们在这里回收动画。

@override
void dispose() {
    controller.dispose();
}

记录按下的位置

double dragDownX = 0.0;
  void _onViewDragDown(DragDownDetails callback) {
    dragDownX = callback.globalPosition.dx;
  }

拖动的时候刷新View的位置

/**
   * 最大可拖动位置
   */
  final double maxDragX = 230.0;
  double dragOffset = 0.0;
  void _onViewDrag(DragUpdateDetails callback) {
    double tmpOffset = callback.globalPosition.dx - dragDownX;

    if (tmpOffset < 0) {
      tmpOffset += maxDragX;
    }

    // 边缘检测
    if (tmpOffset < 0) {
      tmpOffset = 0.0;
    } else if (tmpOffset >= maxDragX) {
      tmpOffset = maxDragX;
    }

    // 刷新
    if (dragOffset != tmpOffset) {
      dragOffset = tmpOffset;
      setState(() {});
    }
  }

离手的时候记录位置并执行动画

/**
   * 脱手时候的位置
   */
  double dragUpDownX = 0.0;
  void _onViewDragUp(DragEndDetails callback) {
    dragUpDownX = dragOffset;
    // 执行动画,每次都从第0帧开始执行
    controller.forward(from: 0.0);
  }

支持移动的Widget

@override
  Widget build(BuildContext context) {
    return Transform.translate(
      offset: Offset(dragOffset, 0.0),
      child: Container(
        child: GestureDetector(
              onHorizontalDragDown: _onViewDragDown,
              onVerticalDragDown: _onViewDragDown,
              onHorizontalDragUpdate: _onViewDrag,
              onVerticalDragUpdate: _onViewDrag,
              onHorizontalDragEnd: _onViewDragUp,
              onVerticalDragEnd: _onViewDragUp,
              child: Container(
                child: new UpDrawerWidget(),
          ),),),);}

Flutter动画

总结一下,想在Flutter中实现动画,需要先创建一个AnimationController控制器;如果有特殊的插值要求,再创建一个插值器,调用controller.forward()方法执行动画,通过addListener()的回调改变对应数值之后调用setState(() {})方法刷新位置即可。

Flutter API还提供AnimatedBuilder用来简化实现动画的复杂性,让我们不用手动调用addListener()方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • flutter实现底部抽屉效果

    本文实例为大家分享了flutter实现底部抽屉效果的具体代码,供大家参考,具体内容如下 安卓:showModalBottomSheetIOS:showCupertinoModalPopup 效果图 完整代码 import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; ///@作者: Q.L ///@创建日期: 2021-09-09 10:55 ///@描述: {底部抽屉} class Actio

  • Flutter Drawer抽屉菜单示例详解

    本文实例为大家分享了Flutter Drawer抽屉菜单示例代码,供大家参考,具体内容如下 一.Flutter Drawer组件简介 1.源码查看 const Drawer({     Key? key,     this.elevation = 16.0, //阴影效果大小     this.child, //内容元素     this.semanticLabel, //关闭/打开抽屉时的通知信息   })  二.抽屉菜单示例 1.菜单项,使用 ListTile 实现 Expanded(  

  • flutter实现一个列表下拉抽屉的示例代码

    目录 使用 源码 使用 通过监听滚动事件实现DragOpenDrawer 组件,可以给滚动组件添加一个下拉抽屉.其使用方式如下: DragOpenDrawer(   openDuration: Duration(microseconds: 900),   closeDuration: Duration(milliseconds: 300),   onOpen: (){     print("onOpen");   },  child: Column(       children: [

  • Flutter中抽屉组件Drawer使用详解

    本文实例为大家分享了Flutter中抽屉组件Drawer实现代码,供大家参考,具体内容如下 1.概述 Scalfold 是 Flutter MaterialApp 常用的布局 Widget,接受一个 drawer属性,支持配置 Drawer,可以实现从侧边栏拉出导航面板,好处是把一些功能菜单折叠起来,通常Drawer是和Listview组件或者 Column组合使用进行纵向布局.Listview组件是竖排排列的,上下可滑动. [注意]如果没有设置 AppBar 的 leading 属性,则当使用

  • Flutter Drawer实现抽屉菜单效果

    本文实例为大家分享了Flutter Drawer实现抽屉菜单的具体代码,供大家参考,具体内容如下 import 'package:flutter/material.dart'; void main() {   runApp(MyApp()); } class MyApp extends StatelessWidget {   @override   Widget build(BuildContext context) {     return MaterialApp(       debugSh

  • Flutter UI实现侧拉抽屉菜单

    在移动开发中,我们可以通过底部导航栏.标签页或是侧边抽屉菜单来实现导航.这是在小屏幕上可以充分利用空间.我们设计不仅要实用而且要有趣,这样才算得上好的 UI 设计.这件我们在 Scaffold 通常是上下结构,头部是标题栏下面主界面. @override Widget build(BuildContext context) {   // TODO: implement build   return Scaffold(     appBar: AppBar(title: Text(title),)

  • Flutter实现抽屉动画

    这篇会深化View拖拽实例,利用Flutter Animation.插值器以及AnimatedBuilder教大家实现带动画的抽屉效果.先来看效果: 通过构思,我们可以设想到实现抽屉的方式就是用Stack控件将两个Widget叠加显示,用GestureDetector监听手势滑动,动态移动顶层的Widget,当监听到手势结束的时候根据手势滑动的距离动态将顶部Widget利用动画效果滑动到结束位置即可. 实现底部Widget class DownDrawerWidget extends State

  • Android Flutter实现3D动画效果示例详解

    目录 前言 AnimatedWidget 简介 3D 旋转动画的实现 总结 前言 上一篇我们介绍了 Animation 和 AnimationController 的使用,这是最基本的动画构建类.但是,如果我们想构建一个可复用的动画组件,通过外部参数来控制其动画效果的时候,上一篇的方法就不太合适了.在 Flutter 中提供了 AnimatedWidget 组件用于构建可复用的动画组件.本篇我们用 AnimatedWidget 来实现组件的3D 旋转效果,如下图所示. AnimatedWidge

  • Android Flutter制作交错动画的示例代码

    目录 前言 动画解析 编码实现 总结 前言 之前一篇我们讲了 Flutter组合动画实现的方式 —— 交错动画.借助 GIF 和绘图技巧是可以做到类似 GIF 那种效果的.本篇我们来一个应用实例,我们让轮子在草地滚动着前进,而且还能粘上“绿色的草”,运行效果如下动画所示. 动画解析 上面实现的效果实际上由三个动画组成: 轮子前进的动画 轮子滚动 轮子的边缘颜色渐变(由黑色变成绿色) 这三个动画是同时进行的,因此需要使用到交错动画,即使用一个 AnimationController来控制三个 Tw

  • Android Flutter实现GIF动画效果的方法详解

    目录 前言 交错动画机制 代码实现 Interval 介绍 总结 前言 我们之前介绍了不少有关动画的篇章.前面介绍的动画都是只有一个动画效果,那如果我们想对某个组件实现一组动效,比如下面的效果,该怎么办? staggered animation 这个时候我们需要用到组合动效, Flutter 提供了交错动画(Staggered Animation)的方式实现.对于多个 Anmation 对象,可以共用一个 AnimationController,然后在不同的时间段执行动画效果.这就有点像 GIF

  • Flutter封装组动画混合动画AnimatedGroup示例详解

    目录 一.来源 二.AnimatedGroup使用示例: 三.AnimatedGroup源码 最后 一.来源 项目中遇到混合动画的情况,每次实现都需要生命一堆属性,让代码变得杂乱,难以维护. 参考 iOS 组动画 CAAimationGroup, 随花半天时间封装一个混合动画组件 AnimatedGroup. 此组件基于极简.高扩展.高适用的封装原则,基本满足当前项目开发. 二.AnimatedGroup使用示例: // // AnimatedGroupDemo.dart // flutter_

  • Flutter进阶之实现动画效果(三)

    在上一篇文章:Flutter进阶-实现动画效果(二)的最后,我们实现了一个控件,其中包含各种布局和状态处理控件.以及使用自定义的动画感知绘图代码绘制单个Bar的控件.还有一个浮动按钮控件,用于启动条形图高度的动画变化. 现在开始向我们的单个条形添加颜色,在Bar类的height字段下添加一个color字段,并且更新Bar.lerp以使其两者兼容.在上一篇文章中,介绍过"lerp"是"线性内插"或"线性插值"的一种简短形式. class Bar {

  • Flutter进阶之实现动画效果(二)

    在上一篇文章:Flutter进阶-实现动画效果(一)的最后,我们说到需要一个处理程序混乱的概念.在这一篇文章中,我们会引入补间,它是构建动画代码的一个非常简单的概念,主要作用是用面向对象的方法替代之前面向过程的方法.tween是一个值,它描述了其他值的空间中的两个点之间的路径,比如条形图的动画值从0运行到1. 补间在Dart中表示类型为Tween的对象 abstract class Tween<T> { final T begin; final T end; Tween(this.begin,

  • flutter PositionedTransition实现缩放动画

    本文实例为大家分享了flutter实现缩放动画的具体代码,供大家参考,具体内容如下 flutter 动画状态监听器 AnimationController //动画控制器 AnimationController controller; //AnimationController是一个特殊的Animation对象,在屏幕刷新的每一帧,就会生成一个新的值, // 默认情况下,AnimationController在给定的时间段内会线性的生成从0.0到1.0的数字 //用来控制动画的开始与结束以及设置

随机推荐