免费领手机 网站,网站建设分金手指科捷11,网站建设培训 上海,建设银行个人网站登陆如何在 Flutter 中使用自定义动画和剪裁#xff08;clipping#xff09;实现一个简单的动画效果。
前置知识点学习
AnimationController
AnimationController 是 Flutter 动画框架中的一个核心类#xff0c;用于控制动画的生命周期和状态。它提供了一种灵活的方式来定义动…如何在 Flutter 中使用自定义动画和剪裁clipping实现一个简单的动画效果。
前置知识点学习
AnimationController
AnimationController 是 Flutter 动画框架中的一个核心类用于控制动画的生命周期和状态。它提供了一种灵活的方式来定义动画的开始、结束、暂停、反向和速度调节等功能。
主要属性
duration: 定义动画的时长。可以是 Duration 类型的值如 Duration(milliseconds: 500)。
vsync: 一个 TickerProvider用于防止动画在不需要时消耗资源。通常在 State 类中通过 SingleTickerProviderStateMixin 提供。
value: 表示动画当前的进度范围通常是 0.0 到 1.0。
lowerBound 和 upperBound: 定义动画值的范围默认是 0.0 到 1.0。
主要方法
forward(): 正向播放动画从当前值到 upperBound。
reverse(): 反向播放动画从当前值到 lowerBound。
repeat(): 循环播放动画可以设置次数和是否反向。
stop(): 停止动画。
reset(): 将动画值重置为 lowerBound。
dispose(): 销毁控制器释放资源。在 State 的 dispose 方法中调用。
监听器
addListener(): 添加一个回调函数每当动画的值改变时调用。
addStatusListener(): 添加一个回调函数每当动画的状态改变时调用比如开始、结束、正向播放、反向播放等。 使用示例
以下是一个简单的例子演示如何使用 AnimationController 创建一个简单的透明度动画
import package:flutter/material.dart;class MyAnimationControllerExample extends StatefulWidget {const MyAnimationControllerExample({super.key});override_MyAnimationControllerExampleState createState() {return _MyAnimationControllerExampleState();}
}class _MyAnimationControllerExampleStateextends StateMyAnimationControllerExamplewith SingleTickerProviderStateMixin {late AnimationController _controller;overridevoid initState() {super.initState();_controller AnimationController(vsync: this, duration: const Duration(seconds: 2));_controller.addListener(() {setState(() {});});_controller.forward();}overridevoid dispose() {_controller.dispose();super.dispose();}overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(AnimationController Example)),body: Center(child: Opacity(opacity: _controller.value,child: Container(width: 100,height: 100,color: Colors.blue,),),),);}
}解释
AnimationController: 控制动画的时长和进度。
SingleTickerProviderStateMixin: 为 vsync 提供 TickerProvider防止不必要的资源消耗。
addListener: 在动画值改变时更新 UI。
forward: 使动画从 lowerBound 开始到 upperBound 结束。 FloatingActionButton
FloatingActionButtonFAB是 Flutter 中一个用于执行主操作的圆形按钮。它通常悬浮在应用界面的某个位置用户可以通过点击它来触发特定的操作或功能。FAB 是 Material Design 的一部分常见于各种应用中用于吸引用户注意并方便地进行交互。 关键属性
child: 该属性用于指定按钮内部的内容通常是一个图标Icon或文本Text。这个内容会在按钮的中心显示。
onPressed: 一个回调函数当用户点击按钮时会被调用。这个属性是必需的因为它定义了按钮的行为。
tooltip: 当用户长按按钮时显示的提示文本通常用于描述按钮的功能。
backgroundColor: 按钮的背景颜色。
foregroundColor: 按钮内容如图标或文本的颜色。
elevation: 按钮的阴影深度影响按钮的浮动效果。
shape: 定义按钮的形状默认是圆形也可以自定义为其他形状。
heroTag: 用于在页面切换时标识 FAB 的唯一标识符默认提供避免动画冲突。 使用示例
下面是一个使用 FloatingActionButton 的简单示例
import package:flutter/material.dart;class FloatingActionButtonExample extends StatelessWidget {const FloatingActionButtonExample({super.key});overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(FloatingActionButton Example),),body: const Center(child: Text(Press the button below!),),floatingActionButton: FloatingActionButton(onPressed: () {print(FAB clicked!);},tooltip: Increment,child: const Icon(Icons.add),),floatingActionButtonLocation: FloatingActionButtonLocation.endDocked);}
}解释
Scaffold: Flutter 提供的一个布局结构支持 Material Design 的组件包括 FAB。
floatingActionButton: Scaffold 的一个属性用于指定屏幕上的 FAB。
onPressed: 定义当 FAB 被点击时的行为。在这个例子中它只是打印一条消息。
Icon: 在 FAB 中展示的内容在这个例子中是一个加号图标。
floatingActionButtonLocation: 用于定义 FAB 在屏幕中的位置如居中、靠右或靠左等。
常见使用场景
主要操作: FAB 通常用于执行应用的主要操作如在邮件应用中创建新邮件、在社交应用中发布新内容等。
辅助功能: 在一些应用中FAB 可以用于快速访问某些辅助功能。
动态操作: 在某些应用中FAB 的功能可能会根据上下文动态变化比如在不同的页面中执行不同的操作。
通过 FloatingActionButton开发者可以在 Flutter 应用中轻松实现符合 Material Design 指导原则的交互元素。它是一个非常直观且易于使用的组件用于增强用户体验。 CustomClipper
CustomClipper 是 Flutter 提供的一个抽象类用于创建自定义剪裁clipping效果。通过实现 CustomClipper你可以定义任意形状的剪裁路径应用于组件的外观。 主要方法
CustomClipper 包含两个主要方法你需要在子类中实现它们
1.getClip(Size size):
返回一个 Path 对象该对象定义了应该如何剪裁组件。
Size 参数提供了组件的大小你可以根据这个大小来计算剪裁路径。
2.shouldReclip(CustomClipper oldClipper):
返回一个布尔值决定是否需要重新剪裁。当剪裁路径依赖于某些动态变化的参数时你需要在这个方法中进行判断。通常如果你的剪裁路径是固定不变的可以返回 false。 使用方法
1.创建一个 CustomClipper 子类:
实现 getClip 方法来定义剪裁路径。
实现 shouldReclip 方法来决定何时重新剪裁。 2.使用 ClipPath 或其他 Clip* 组件:
将自定义 CustomClipper 实例传递给 ClipPath、ClipRect、ClipOval 等组件的 clipper 属性。 示例
以下是一个简单的示例展示如何使用 CustomClipper 来创建一个三角形剪裁效果
import package:flutter/material.dart;class TriangleClipper extends CustomClipperPath {overridePath getClip(Size size) {final path Path();path.moveTo(size.width / 2, 0);path.lineTo(size.width, size.height);path.lineTo(0, size.height);path.close();return path;}overridebool shouldReclip(covariant CustomClipperPath oldClipper) {// 如果路径不依赖外部状态可以返回 falsereturn false;}
}class CustomClipperExample extends StatelessWidget {const CustomClipperExample({super.key});overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(CustomClipper Example),),body: Center(child: ClipPath(clipper: TriangleClipper(), // 使用自定义的 TriangleClipperchild: Container(width: 200,height: 200,color: Colors.blue,),),),);}
}解释
TriangleClipper: 自定义的剪裁器实现了一个简单的三角形路径。
getClip 方法: 定义了一个三角形的路径。
ClipPath: 使用 TriangleClipper 将子组件剪裁成三角形。 使用场景
自定义形状: 当你需要超出标准形状的剪裁效果时比如特定的波浪形、星形等。
动态剪裁: 如果剪裁形状需要根据某些动态参数变化可以通过 shouldReclip 来控制重新剪裁。
视觉效果: 增强 UI 的视觉效果通过不规则的形状吸引用户注意力。 lerpDouble函数解析
在 Flutter 中lerpDouble 是一个用于在两个 double 值之间进行线性插值的方法。它通常用于动画和其他需要平滑过渡的场景。
主要功能
lerpDouble 的主要功能是根据给定的插值因子 t计算出两个 double 值之间的中间值。这个过程称为线性插值linear interpolation简称 lerp。 方法签名
double? lerpDouble(num? a,num? b,double t,
)
参数
a: 起始值可以是 double 或 null。如果为 null则在计算时视为 0.0。
b: 结束值可以是 double 或 null。如果为 null则在计算时视为 0.0。
t: 插值因子是一个介于 0.0 到 1.0 之间的 double。当 t 为 0.0 时返回 a当 t 为 1.0 时返回 b在这之间返回 a 和 b 的插值。 返回值
返回一个 double 类型的值表示 a 和 b 之间的插值。如果 a 和 b 都为 null则返回 null。 用法示例
以下是一个简单的示例展示如何使用 lerpDouble 计算两个值之间的插值
import dart:ui;void main() {double? start 10.0;double? end 20.0;double t 0.25; // 插值因子double? interpolatedValue lerpDouble(start, end, t);print(Interpolated Value: $interpolatedValue); // 输出: Interpolated Value: 12.5
}
解释
在上面的例子中start 是 10.0end 是 20.0t 是 0.25。
lerpDouble 返回两个值之间的 25% 位置上的值即 12.5。
使用场景
动画: 在动画过程中计算属性的中间值比如位置、大小、透明度等。
过渡效果: 在不同状态之间平滑过渡例如颜色渐变、尺寸变化等。
自定义插值: 在需要自定义插值逻辑的情况下用于计算中间值。
lerpDouble 是一个简单却强大的工具允许开发者在两个数值之间创建平滑的过渡效果非常适合用于动画和动态 UI 变化中。 Path
在 Flutter 中Path 是一个用于定义向量形状的类。它允许开发者创建复杂的几何图形通过一系列的直线和曲线来定义路径。Path 类可以用于绘制形状、创建剪裁区域以及生成自定义绘制效果。 基本用法
Path 提供了一系列方法用于定义形状的边界。以下是一些常用的方法
moveTo(double x, double y): 移动当前点到指定的坐标开始新的子路径。
lineTo(double x, double y): 从当前点绘制一条直线到指定的坐标。
arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo): 绘制一个圆弧基于一个矩形的边界。
quadraticBezierTo(double x1, double y1, double x2, double y2): 绘制一个二次贝塞尔曲线。
cubicTo(double x1, double y1, double x2, double y2, double x3, double y3): 绘制一个三次贝塞尔曲线。
close(): 关闭当前子路径连接最后一个点到第一个点形成一个封闭的形状。 示例
下面是一个简单的示例使用 Path 绘制一个三角形
import package:flutter/material.dart;class PathExampleDemo extends StatelessWidget {const PathExampleDemo({super.key});overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(Path Example),),body: Center(child: CustomPaint(size: const Size(200, 200),painter: TrianglePainter(),),),);}
}class TrianglePainter extends CustomPainter {overridevoid paint(Canvas canvas, Size size) {final paint Paint()..color Colors.blue..style PaintingStyle.fill;final path Path();//顶点path.moveTo(size.width / 2, 0);//右下角path.lineTo(size.width, size.height);//左下角path.lineTo(0, size.height);path.close();canvas.drawPath(path, paint);}overridebool shouldRepaint(covariant CustomPainter oldDelegate) {return false;}
}解释
CustomPainter: 用于自定义绘制。paint 方法中使用 Canvas 对象进行绘制。
Path: 定义路径的形状。在这个例子中绘制了一个简单的三角形。
Canvas.drawPath: 使用 Path 和 Paint 对象在画布上绘制路径。
使用场景
自定义形状: Path 可以定义任意形状用于自定义绘制或剪裁。
复杂图形: 使用贝塞尔曲线和弧形可以创建复杂的图形和路径。
动画路径: 在动画中可以使用 Path 来定义对象的运动轨迹。
注意事项
路径方向: 在定义路径时方向顺时针或逆时针可能会影响填充规则。
性能: 复杂路径可能会影响性能尤其是在动画中请合理使用。 AnimatedBuilder
AnimatedBuilder 是 Flutter 动画框架中的一个小部件用于将动画与 UI 组件进行绑定。它提供了一种高效的方法来重建与动画相关的部分 UI而无需重建整个 widget 树。 核心概念
AnimatedBuilder 通过监听一个 Listenable 对象通常是 AnimationController 或其他 Animation 对象来决定何时重建 UI。当动画对象的值发生变化时AnimatedBuilder 会调用其构建方法从而更新与动画相关的 UI。 主要属性
animation: 一个 Listenable 对象通常是 Animation 或 AnimationController。AnimatedBuilder 监听这个对象的变化。
builder: 一个回调函数接受两个参数BuildContext 和 Widget。在这个函数中你可以根据动画的当前状态来构建和返回一个新的 widget 树。
child: 一个可选的小部件当它在动画变化时不需要重建时可以作为优化传递给 builder。这样可以避免不必要的重建。 使用示例
以下是一个使用 AnimatedBuilder 的简单示例展示如何创建一个旋转动画
import package:flutter/material.dart;class AnimatedBuilderExample extends StatefulWidget {const AnimatedBuilderExample({super.key});override_AnimatedBuilderExampleState createState() {return _AnimatedBuilderExampleState();}
}class _AnimatedBuilderExampleState extends StateAnimatedBuilderExamplewith SingleTickerProviderStateMixin {late AnimationController _controller;overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(AnimatedBuilder Example)),body: Center(child: AnimatedBuilder(animation: _controller,builder: (context, child) {return Transform.rotate(angle: _controller.value * 2.0 * 3.14,child: child,);},child: Container(width: 100,height: 100,color: Colors.blue,),),),);}overridevoid initState() {super.initState();_controller AnimationController(vsync: this, duration: const Duration(seconds: 2))..repeat(); //无限循环动画}overridevoid dispose() {_controller.dispose();super.dispose();}
}解释
AnimationController: 控制动画的时长和进度。在这个例子中_controller 在 2 秒内从 0.0 到 1.0 循环。
AnimatedBuilder: 监听 _controller 的变化并在 builder 回调中根据动画的当前值更新 UI。
Transform.rotate: 根据动画的当前值旋转 child实现旋转效果。
child: 传递给 AnimatedBuilder 的 child 是一个蓝色的方块它在动画期间不会重建。 使用场景
动画优化: 当只有部分 UI 需要随着动画更新时AnimatedBuilder 可以避免整个 widget 树的重建。
复杂动画: 在需要多个动画组合或复杂动画效果时AnimatedBuilder 提供了一种灵活的方式来管理和应用这些动画。 自定义动画代码学习
import dart:math;
import dart:ui;import package:flutter/material.dart;class AnimaDemoPage22 extends StatefulWidget {const AnimaDemoPage22({super.key});override_AnimaDemoPageState22 createState() {return _AnimaDemoPageState22();}
}class _AnimaDemoPageState22 extends StateAnimaDemoPage22with SingleTickerProviderStateMixin {late AnimationController controller;Animation? animation;overridevoid initState() {super.initState();controller AnimationController(vsync: this,duration: const Duration(milliseconds: 500),);animation CurvedAnimation(parent: controller, curve: Curves.easeInSine);}overridevoid dispose() {controller.dispose();super.dispose();}overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(AnimaDemoPage22),),body: Container(color: Colors.blueGrey,child: MyCRAnimation(minR: 0,maxR: 250,offset: Offset(MediaQuery.sizeOf(context).width / 2,MediaQuery.sizeOf(context).height / 2),animation: animation as Animationdouble?,child: Center(child: Container(alignment: Alignment.center,height: 250,width: 250,color: Colors.greenAccent,child: const Text(动画测试),),),),),floatingActionButton: FloatingActionButton(onPressed: () {if (controller.status AnimationStatus.completed ||controller.status AnimationStatus.forward) {controller.reverse();} else {controller.forward();}},child: const Text(点击),),);}
}class MyCRAnimation extends StatelessWidget {final Offset? offset;final double? minR;final double? maxR;final Widget child;final Animationdouble? animation;const MyCRAnimation({super.key,required this.child,required this.animation,this.offset,this.minR,this.maxR});overrideWidget build(BuildContext context) {return AnimatedBuilder(animation: animation!,builder: (_, __) {return ClipPath(clipper: MyAnimationClipper(value: animation!.value,minR: minR,maxR: maxR,offset: offset),child: child,);});}
}class MyAnimationClipper extends CustomClipperPath {final double? value;final double? minR;final double? maxR;final Offset? offset;MyAnimationClipper({this.value, this.offset, this.minR, this.maxR});overridePath getClip(Size size) {var path Path();var offset this.offset ?? Offset(size.width / 2, size.height / 2);var maxRadius minR ?? radiusSize(size, offset);var minRadius maxR ?? 0;var radius lerpDouble(minRadius, maxRadius, value!)!;var rect Rect.fromCircle(center: offset, radius: radius);path.addOval(rect);return path;}overridebool shouldReclip(covariant CustomClipperPath oldClipper) {return true;}double radiusSize(Size size, Offset offset) {final height max(offset.dy, size.height - offset.dy);final width max(offset.dx, size.width - offset.dx);return sqrt(width * width height * height);}
}