I want to draw animated border around the square container with infinity loop (never stop) like this photo , I'm trying animated container but not help me
so can anyone tell me how to implement line animation
I'm using this code to draw the square but I 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
CodePudding user response:
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:math' as math;
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: 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: CustomPaint(
foregroundPainter: AnimatedBorderPainter(
animation: _animation,
strokeColor: Colors.deepOrange,
pathType: PathType.rect,
animationDirection: AnimationDirection.clockwise,
startingIndex: 1,
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 double _radius;
final int _startingIndex;
final AnimationDirection _animationDirection;
AnimatedBorderPainter({
required animation,
pathType = PathType.rect,
strokeWidth = 2.0,
strokeColor = Colors.blueGrey,
radius = 4.0,
startingIndex = 0,
animationDirection = AnimationDirection.clockwise,
}) : assert(strokeWidth > 0, 'strokeWidth must be greater than 0.'),
assert(
pathType == PathType.rect ? startingIndex <= 3 : startingIndex <= 7,
'startingIndex may not exceed the number of points the path is constructed from'),
_animation = animation,
_pathType = pathType,
_strokeWidth = strokeWidth,
_strokeColor = strokeColor,
_radius = radius,
_startingIndex = startingIndex,
_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:
final maxAllowedRadius =
[size.height / 2, size.width / 2, _radius].reduce(math.min);
if (_animationDirection == AnimationDirection.clockwise) {
return _createOriginalPathRRectClockwise(size, maxAllowedRadius);
} else {
return _createOriginalPathRRectCounterclockwise(
size, maxAllowedRadius);
}
}
}
Path _createOriginalPathRect(Size size) {
final tlPoint = Point(0, 0);
final trPoint = Point(size.width, 0);
final brPoint = Point(size.width, size.height);
final blPoint = Point(0, size.height);
final points = _animationDirection == AnimationDirection.clockwise
? [tlPoint, trPoint, brPoint, blPoint]
: [tlPoint, blPoint, brPoint, trPoint];
int startingIndex = _startingIndex;
final path = Path();
// Move to starting point
final startingPoint = points[startingIndex];
path.moveTo(startingPoint.x, startingPoint.y);
// Contruct the complete path by moving from point to point
for (var i = 0; i < points.length; i ) {
final index = startingIndex == points.length - 1 ? 0 : startingIndex 1;
final pointLineTo = points[index];
path.lineTo(pointLineTo.x, pointLineTo.y);
if (startingIndex == points.length - 1) {
startingIndex = 0;
continue;
}
startingIndex ;
}
return path;
}
Path _createOriginalPathRRectClockwise(Size size, double radius) {
final tlPoint = Point(0, 0 radius);
final tlBezierPoint = BezierPoint(0, 0, 0 radius, 0);
final trPoint = Point(size.width - radius, 0);
final trBezierPoint = BezierPoint(size.width, 0, size.width, 0 radius);
final brPoint = Point(size.width, size.height - radius);
final brBezierPoint =
BezierPoint(size.width, size.height, size.width - radius, size.height);
final blPoint = Point(0 radius, size.height);
final blBezierPoint = BezierPoint(0, size.height, 0, size.height - radius);
final points = [
tlPoint,
tlBezierPoint,
trPoint,
trBezierPoint,
brPoint,
brBezierPoint,
blPoint,
blBezierPoint
];
return _createOriginalPathRRect(points);
}
Path _createOriginalPathRRectCounterclockwise(Size size, double radius) {
final tlPoint = Point(0 radius, 0);
final tlBezierPoint = BezierPoint(0, 0, 0, 0 radius);
final trPoint = Point(size.width, 0 radius);
final trBezierPoint = BezierPoint(size.width, 0, size.width - radius, 0);
final brPoint = Point(size.width - radius, size.height);
final brBezierPoint =
BezierPoint(size.width, size.height, size.width, size.height - radius);
final blPoint = Point(0, size.height - radius);
final blBezierPoint = BezierPoint(0, size.height, 0 radius, size.height);
final points = [
tlPoint,
tlBezierPoint,
blPoint,
blBezierPoint,
brPoint,
brBezierPoint,
trPoint,
trBezierPoint,
];
return _createOriginalPathRRect(points);
}
Path _createOriginalPathRRect(List<Object> points) {
int startingIndex = _startingIndex;
final path = Path();
// Move to starting point
final startingPoint = points[startingIndex];
if (startingPoint is Point) {
path.moveTo(startingPoint.x, startingPoint.y);
} else {
path.moveTo((startingPoint as BezierPoint).x2, startingPoint.y2);
}
// Contruct the complete path by moving from point to point
for (var i = 0; i < points.length; i ) {
final index = startingIndex == points.length - 1 ? 0 : startingIndex 1;
final nextPoint = points[index];
if (nextPoint is Point) {
path.lineTo(nextPoint.x, nextPoint.y);
} else {
path.quadraticBezierTo((nextPoint as BezierPoint).x1, nextPoint.y1,
nextPoint.x2, nextPoint.y2);
}
if (startingIndex == points.length - 1) {
startingIndex = 0;
continue;
}
startingIndex ;
}
return path;
}
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 = originalPath.computeMetrics().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 = metric.extractPath(0.0, remainingLength);
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;
}
}
class Point {
final double x;
final double y;
Point(this.x, this.y);
@override
String toString() {
return 'Point($x, $y)';
}
}
class BezierPoint {
final double x1;
final double y1;
final double x2;
final double y2;
BezierPoint(this.x1, this.y1, this.x2, this.y2);
@override
String toString() {
return 'BezierPoint($x1, $y1, $x2, $y2)';
}
}
enum PathType {
rect,
rRect,
}
enum AnimationDirection {
clockwise,
counterclockwise,
}