Issue
How to animate a border around the square content. tried to use this code but can't make it build with animation..
class RadialPainter extends CustomPainter {
final double progressRemoval;
final Color color;
final StrokeCap strokeCap;
final PaintingStyle paintingStyle;
final double strokeWidth;
final double progress;
RadialPainter(
{this.progressRemoval,
this.color,
this.strokeWidth,
this.strokeCap,
this.paintingStyle,
this.progress});
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..strokeWidth = strokeWidth
..color = color
..style = paintingStyle
..strokeCap = strokeCap;
var progressRemoval = 0.50;
var path = Path();
//LINEA SUPERIOR DEL CUADRADO
path.moveTo((size.width * 0.30), 0);
path.quadraticBezierTo((size.width * 0.30), 0, size.width, 0);
//LATERAL DERECHO
path.moveTo(size.width, 0);
path.quadraticBezierTo(size.width, 0, size.width, size.height);
//LINEA INFERIOR DEL CUADRADO
path.moveTo(size.width, size.height);
path.quadraticBezierTo(size.width, size.height, 0, size.height);
//LINEA IZQUIERDA
path.moveTo(0, size.height);
path.quadraticBezierTo(0, (size.height * 0.75), 0, ((size.height * 0.75)));
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(RadialPainter oldDelegate) {
return oldDelegate.progress != progress;
}
}
https://stackoverflow.com/questions/66870939/flutter-animate-border-color-of-a-container?rq=1
Solution
Here's a full example for your use case with a Container
and a repeating animation that you can run in DartPad.
For a detailed explanation see this answer.
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Border Animation',
home: Scaffold(body: ExampleOutlinePathWrapper()));
}
}
// Example code including two animations
class ExampleOutlinePathWrapper extends StatefulWidget {
@override
State<ExampleOutlinePathWrapper> createState() =>
_ExampleOutlinePathWrapperState();
}
class _ExampleOutlinePathWrapperState extends State<ExampleOutlinePathWrapper>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 2000,
),
);
_animation = _controller
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_controller.reset();
} else if (status == AnimationStatus.dismissed) {
_controller.forward();
}
});
_controller.forward();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomPaint(
foregroundPainter: AnimatedBorderPainter(
animation: _animation,
strokeColor: Colors.deepOrange,
pathType: PathType.circle,
animationDirection: AnimationDirection.counterclockwise,
startingPercentage: 50,
strokeWidth: 8.0,
),
child: Container(
height: 100,
width: 100,
decoration: const BoxDecoration(
color: Colors.grey,
shape: BoxShape.circle,
),
),
),
const SizedBox(
height: 20,
),
CustomPaint(
foregroundPainter: AnimatedBorderPainter(
animation: _animation,
strokeColor: Colors.deepOrange,
pathType: PathType.rect,
animationDirection: AnimationDirection.clockwise,
startingPercentage: 25,
strokeWidth: 4.0,
),
child: Container(height: 100, width: 100, color: Colors.grey),
),
],
),
);
}
}
class AnimatedBorderPainter extends CustomPainter {
final Animation<double> _animation;
final PathType _pathType;
final double _strokeWidth;
final Color _strokeColor;
final Radius _radius;
final int _startingPercentage;
final AnimationDirection _animationDirection;
AnimatedBorderPainter({
required animation,
PathType pathType = PathType.rect,
double strokeWidth = 2.0,
Color strokeColor = Colors.blueGrey,
Radius radius = const Radius.circular(4.0),
int startingPercentage = 0,
AnimationDirection animationDirection = AnimationDirection.clockwise,
}) : assert(strokeWidth > 0, 'strokeWidth must be greater than 0.'),
assert(startingPercentage >= 0 && startingPercentage <= 100,
'startingPercentage must lie between 0 and 100.'),
_animation = animation,
_pathType = pathType,
_strokeWidth = strokeWidth,
_strokeColor = strokeColor,
_radius = radius,
_startingPercentage = startingPercentage,
_animationDirection = animationDirection,
super(repaint: animation);
late Path _originalPath;
late Paint _paint;
@override
void paint(Canvas canvas, Size size) {
final animationPercent = _animation.value;
// Construct original path once when animation starts
if (animationPercent == 0.0) {
_originalPath = _createOriginalPath(size);
_paint = Paint()
..strokeWidth = _strokeWidth
..style = PaintingStyle.stroke
..color = _strokeColor;
}
final currentPath = _createAnimatedPath(
_originalPath,
animationPercent,
);
canvas.drawPath(currentPath, _paint);
}
@override
bool shouldRepaint(AnimatedBorderPainter oldDelegate) => true;
Path _createOriginalPath(Size size) {
switch (_pathType) {
case PathType.rect:
return _createOriginalPathRect(size);
case PathType.rRect:
return _createOriginalPathRRect(size);
case PathType.circle:
return _createOriginalPathCircle(size);
}
}
Path _createOriginalPathRect(Size size) {
Path originalPath = Path()
..addRect(
Rect.fromLTWH(0, 0, size.width, size.height),
)
..lineTo(0, -(_strokeWidth / 2));
if (_startingPercentage > 0 && _startingPercentage < 100) {
return _createPathForStartingPercentage(
originalPath, PathType.rect, size);
}
return originalPath;
}
Path _createOriginalPathRRect(Size size) {
Path originalPath = Path()
..addRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height),
_radius,
),
);
if (_startingPercentage > 0 && _startingPercentage < 100) {
return _createPathForStartingPercentage(originalPath, PathType.rRect);
}
return originalPath;
}
Path _createOriginalPathCircle(Size size) {
Path originalPath = Path()
..addOval(
Rect.fromLTWH(0, 0, size.width, size.height),
);
if (_startingPercentage > 0 && _startingPercentage < 100) {
return _createPathForStartingPercentage(originalPath, PathType.circle);
}
return originalPath;
}
Path _createPathForStartingPercentage(Path originalPath, PathType pathType,
[Size? size]) {
// Assumes that original path consists of one subpath only
final pathMetrics = originalPath.computeMetrics().first;
final pathCutoffPoint = (_startingPercentage / 100) * pathMetrics.length;
final firstSubPath = pathMetrics.extractPath(0, pathCutoffPoint);
final secondSubPath =
pathMetrics.extractPath(pathCutoffPoint, pathMetrics.length);
if (pathType == PathType.rect) {
Path path = Path()
..addPath(secondSubPath, Offset.zero)
..lineTo(0, -(_strokeWidth / 2))
..addPath(firstSubPath, Offset.zero);
switch (_startingPercentage) {
case 25:
path.lineTo(size!.width + _strokeWidth / 2, 0);
break;
case 50:
path.lineTo(size!.width - _strokeWidth / 2, size.height);
break;
case 75:
path.lineTo(0, size!.height + _strokeWidth / 2);
break;
default:
}
return path;
}
return Path()
..addPath(secondSubPath, Offset.zero)
..addPath(firstSubPath, Offset.zero);
}
Path _createAnimatedPath(
Path originalPath,
double animationPercent,
) {
// ComputeMetrics can only be iterated once!
final totalLength = originalPath
.computeMetrics()
.fold(0.0, (double prev, PathMetric metric) => prev + metric.length);
final currentLength = totalLength * animationPercent;
return _extractPathUntilLength(originalPath, currentLength);
}
Path _extractPathUntilLength(
Path originalPath,
double length,
) {
var currentLength = 0.0;
final path = Path();
var metricsIterator = _animationDirection == AnimationDirection.clockwise
? originalPath.computeMetrics().iterator
: originalPath.computeMetrics().toList().reversed.iterator;
while (metricsIterator.moveNext()) {
var metric = metricsIterator.current;
var nextLength = currentLength + metric.length;
final isLastSegment = nextLength > length;
if (isLastSegment) {
final remainingLength = length - currentLength;
final pathSegment = _animationDirection == AnimationDirection.clockwise
? metric.extractPath(0.0, remainingLength)
: metric.extractPath(
metric.length - remainingLength, metric.length);
path.addPath(pathSegment, Offset.zero);
break;
} else {
// There might be a more efficient way of extracting an entire path
final pathSegment = metric.extractPath(0.0, metric.length);
path.addPath(pathSegment, Offset.zero);
}
currentLength = nextLength;
}
return path;
}
}
enum PathType {
rect,
rRect,
circle,
}
enum AnimationDirection {
clockwise,
counterclockwise,
}
Answered By - hnnngwdlch
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.