flutter 动画和绘制

在 Flutter 中实现动画涉及到多个概念和组件。Flutter 提供了强大的动画库,使得创建流畅、富有表现力的动画变得相对简单。以下是实现动画的几个基本步骤:

1. 选择合适的动画类型

Flutter 中有两种主要类型的动画:

  • Tween 动画 :用于简单的从一个值过渡到另一个值的动画。
  • 物理模拟动画 :用于模拟物理现象,如弹簧、重力等。

2. 使用 AnimationController

AnimationController 是一个特殊的 Animation 对象,在给定的 Duration 内生成新值。

final AnimationController controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this, // 需要一个 TickerProvider 类型的对象
);

这里,vsync 参数防止屏幕外动画消耗不必要的资源。

3. 创建 Tween

Tween 对象定义了动画的开始和结束值。

final Tween<double> tween = Tween(begin: 0.0, end: 1.0);

4. 连接 Tween 和 Controller

通过调用 Tweenanimate 方法并传递 Controller 来创建 Animation 对象。

final Animation<double> animation = tween.animate(controller);

5. 监听动画状态

你可以监听动画的每一帧以及状态变化。

animation.addListener(() {
  setState(() {
    // 状态改变时重新构建 widget
  });
});

animation.addStatusListener((status) {
  if (status == AnimationStatus.completed) {
    // 动画完成时的操作
  }
});

6. 在 Widget 中使用 Animation

使用 AnimatedBuilderAnimatedWidget 将动画应用于 widget。

AnimatedBuilder(
  animation: animation,
  builder: (context, child) {
    return Opacity(
      opacity: animation.value,
      child: child,
    );
  },
  child: MyWidget(), // 需要动画的 Widget
);

7. 控制动画

最后,使用 controller 来控制动画的播放、停止等。

controller.forward(); // 开始动画

注意事项

  • 资源管理 :确保在 widget 销毁时释放 AnimationController
  • 性能 :对于不在屏幕上的动画,使用 vsync 停止其消耗资源。
  • 组合动画 :可以将多个动画和动画控制器组合以创建复杂的动画效果。

通过这些步骤,你可以在 Flutter 应用中创建各种动态和吸引人的动画效果,增强用户体验。

在 Flutter 中,有许多内置的动画组件,可以用来实现透明度动画、缩放动画、位置移动动画等效果。下面列出了一些常用的动画组件及其用途:

1. Opacity 动画

Opacity 组件用于调整子组件的不透明度。

Opacity(
  opacity: 0.5, // 透明度,0.0 完全透明,1.0 完全不透明
  child: MyWidget(),
)

2. Scale 动画

ScaleTransition 用于根据动画值缩放子组件。

ScaleTransition(
  scale: animation, // Animation<double> 类型
  child: MyWidget(),
)

3. 位置移动动画

  • PositionedTransition :用于相对于 Stack 的位置变化动画。

    PositionedTransition(
      rect: animation, // Animation<RelativeRect> 类型
      child: MyWidget(),
    )
    
    

    SlideTransition :用于沿指定方向滑动子组件的动画。

    SlideTransition(
      position: animation, // Animation<Offset> 类型
      child: MyWidget(),
    )
    
    

4. 大小变化动画

SizeTransition 可以使子组件的大小沿着指定轴线变化。

SizeTransition(
  sizeFactor: animation, // Animation<double> 类型
  axis: Axis.vertical, // 变化轴线
  child: MyWidget(),
)

5. 旋转动画

RotationTransition 可以使子组件根据动画值旋转。

RotationTransition(
  turns: animation, // Animation<double> 类型
  child: MyWidget(),
)

6. 渐变动画

AnimatedOpacityAnimatedCrossFade 用于创建渐变效果。

  • AnimatedOpacity

    AnimatedOpacity(
      opacity: 0.5,
      duration: Duration(seconds: 1),
      child: MyWidget(),
    )
    
    

    AnimatedCrossFade

    AnimatedCrossFade(
      firstChild: MyFirstWidget(),
      secondChild: MySecondWidget(),
      crossFadeState: CrossFadeState.showFirst,
      duration: Duration(seconds: 1),
    )
    
    

7. 容器变化动画

AnimatedContainer 在容器属性变化时自动创建动画效果。

AnimatedContainer(
  width: 200.0,
  height: 200.0,
  color: Colors.blue,
  duration: Duration(seconds: 1),
  child: MyWidget(),
)

8. 列表项动画

AnimatedList 可用于动画列表项的插入和移除。

AnimatedList(
  initialItemCount: _items.length,
  itemBuilder: (context, index, animation) {
    return SlideTransition(
      position: animation.drive(myOffset), // 自定义 Offset 动画
      child: MyListItem(_items[index]),
    );
  },
)

CustomPaint绘制

CustomPaint 是 Flutter 中一个非常强大的 widget,用于实现自定义的绘制操作。它通常与 CustomPainter 类一起使用,后者定义了在画布(Canvas)上绘制的具体内容。你可以使用 CustomPaint 来绘制复杂的形状、图案或任何其他自定义的绘图。

基本用法

  1. 创建 CustomPainter 类 :首先,你需要创建一个继承自 CustomPainter 的类,并重写 paintshouldRepaint 方法。

    class MyCustomPainter extends CustomPainter {
      @override
      void paint(Canvas canvas, Size size) {
        // 使用 canvas 绘制
        var paint = Paint()
          ..color = Colors.blue
          ..strokeWidth = 5;
    
        // 绘制一个简单的线条
        canvas.drawLine(Offset(0, 0), Offset(size.width, size.height), paint);
      }
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) {
        // 返回值决定是否需要重绘
        return false;
      }
    }
    
    

    使用 CustomPaint widget :然后,在你的 widget 树中使用 CustomPaint 并将你的 CustomPainter 实例传递给它。

    CustomPaint(
      painter: MyCustomPainter(),
      child: Container(
        width: 300,
        height: 300,
      ),
    )
    
    

注意事项

  • 性能 :自定义绘制可能会对性能产生影响,特别是在复杂的绘制或大量重绘时。确保你的 shouldRepaint 方法正确实现,以避免不必要的重绘。
  • 尺寸CustomPaint 的尺寸默认是它的父 widget 的尺寸。如果它有一个 child,那么它的尺寸会适应这个 child 的尺寸,除非你提供了 size 参数。
  • 画布坐标 :Canvas 的 (0, 0) 坐标位于 CustomPaint widget 的左上角。

Paint 对象在 Flutter 中用于描述如何绘制形状(线条、圆形、矩形等)。它包含了各种属性来定义绘图样式,如颜色、笔画宽度、填充类型等。下面是一些 Paint 的用法示例,展示了如何使用不同的属性来创建各种绘图效果。

示例 1:基本线条绘制

class LinePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 5.0;

    canvas.drawLine(Offset(0, 0), Offset(size.width, size.height), paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

示例 2:绘制带有边框的圆

class CirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 4.0;

    canvas.drawCircle(size.center(Offset.zero), 50.0, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

在这个例子中,Paint 被配置为红色,样式为 PaintingStyle.stroke,这意味着圆是空心的,仅绘制边框。

示例 3:渐变填充

class GradientPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Rect rect = Offset.zero & size;
    final Gradient gradient = LinearGradient(
      colors: [Colors.red, Colors.blue],
    );

    final paint = Paint()
      ..shader = gradient.createShader(rect);

    canvas.drawRect(rect, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

这个例子展示了如何使用线性渐变来填充一个矩形。Paintshader 属性设置了一个 LinearGradient

示例 4:绘制虚线

class DashedLinePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.green
      ..strokeWidth = 5.0
      ..style = PaintingStyle.stroke;

    var max = size.width;
    var dashWidth = 10.0;
    var dashSpace = 5.0;
    double startX = 0;

    while (startX < max) {
      canvas.drawLine(Offset(startX, 0), Offset(startX + dashWidth, 0), paint);
      startX += dashWidth + dashSpace;
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

这个例子中,Paint 用于绘制一系列短线段,从而形成虚线的效果。

封装的flutter基础框架: https:/gitee.com/kuaipai/jd_flutter,你可以参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容