i want to add heart beat horizontal line for splash screen, it will start animating from one side and go all the way to other if the initial data is still not loaded it will continue animation
i tried below code but its only pulse kinda animation
import 'dart:math';
import 'package:flutter/material.dart';
class SpritePainter extends CustomPainter {
final Animation<double> _animation;
SpritePainter(this._animation) : super(repaint: _animation);
void circle(Canvas canvas, Rect rect, double value) {
double opacity = (1.0 - (value / 4.0)).clamp(0.0, 1.0);
Color color = Color.fromRGBO(0, 117, 194, opacity);
double size = rect.width / 2;
double area = size * size;
double radius = sqrt(area * value / 4);
final Paint paint = Paint()..color = color;
canvas.drawCircle(rect.center, radius, paint);
}
#override
void paint(Canvas canvas, Size size) {
Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
for (int wave = 3; wave >= 0; wave--) {
circle(canvas, rect, wave + _animation.value);
}
}
#override
bool shouldRepaint(SpritePainter oldDelegate) {
return true;
}
}
class SpriteDemo extends StatefulWidget {
#override
SpriteDemoState createState() => SpriteDemoState();
}
class SpriteDemoState extends State<SpriteDemo>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
);
//_startAnimation();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void _startAnimation() {
_controller
..stop()
..reset()
..repeat(period: const Duration(seconds: 1));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Pulse')),
body: CustomPaint(
painter: SpritePainter(_controller),
child: SizedBox(
width: 200.0,
height: 200.0,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _startAnimation,
child: new Icon(Icons.play_arrow),
),
);
}
}
void main() {
runApp(
MaterialApp(
home: SpriteDemo(),
),
);
}
I want it to start(animate) from center left and go all the way to right just like in image
Related
I want to create a moving point that moves across an image. The picture is drawn on canvas. The point should approach the specified x and y coordinates.Image and point are generated. The point should move to the new specified position every 500ms by a timer.But it only moves when I move the mouse over buttons on the app.
class five_og extends StatefulWidget {
#override
State createState() {
return _five_og();
}
}
class _five_og extends State<five_og> {
ui.Image? image;
Timer? timer;
void refreshImage() {
if (i >= xList.length - 1) {
i = 0;
}
ImagePainter(image!);
i++;
}
#override
void dispose() {
super.dispose();
timer!.cancel();
}
#override
void initState() {
super.initState();
loadImage('assets/images/Erdgeschoss.png');
timer = Timer.periodic(
Duration(milliseconds: 500),
(timer) {
refreshImage();
},
);
}
Future loadImage(String path) async {
final data = await rootBundle.load(path);
final bytes = data.buffer.asUint8List();
final image = await decodeImageFromList(bytes);
setState(() {
this.image = image;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Test'),
),
body: Center(
child: image == null
? CircularProgressIndicator()
: Container(
height: 600,
width: 600,
child: FittedBox(
child: SizedBox(
width: image!.width.toDouble(),
height: image!.width.toDouble(),
child: CustomPaint(
painter: ImagePainter(image!),
),
),
)
)
),
);
}
}
class ImagePainter extends CustomPainter {
final ui.Image image;
const ImagePainter(this.image);
#override
void paint(Canvas canvas, Size size) {
//final paint = Paint();
canvas.drawImage(image, Offset.zero, Paint());
final paint = Paint()
..color = Color.fromARGB(255, 15, 182, 253)
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(xList[i], yList[i]), 13, paint);
}
#override
bool shouldRepaint(ImagePainter oldDelegate) {
return false;
}
}
I'm currently building an App that has a lot of vector lines that I want to animate.
I have tried using flutter_svg but ultimatly wasn't able to make individual lines tapable because only the top svg inside the stack is changed.
The new solution was to use this tool: https://fluttershapemaker.com/ which is recommended by flutter_svg to use.
This converted my SVG to about 4000 lines of geometry data.
I started to add an animation that lets all lines glow up and then go back to darkness.
But the performance is quite terrible.
It starts with about 10 fps and after a minute or so goes down to 0.5fps and lower.
This is mainly due to the rendering engine.
This is the code I'm working on currently:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'svgtemp.dart';
import 'dart:math';
import 'dart:ui';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: Picture(),
);
}
}
class Picture extends StatefulWidget {
const Picture({Key? key}) : super(key: key);
#override
State<Picture> createState() => _PictureState();
}
class _PictureState extends State<Picture> with SingleTickerProviderStateMixin {
late Size size_;
final Random rng = Random();
static const double pictureScalar = 1.0;
static const double backgroundScalar = 1.00;
Color color_ = Colors.black;
late AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
)..repeat(reverse: true);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
size_ = Size(
MediaQuery.of(context).size.width, MediaQuery.of(context).size.height);
final pictureElements = <Widget>[];
for (var i = 0; i < svgdata.length; i++) {
pictureElements.add(createPicturePart(i, context));
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Material(
child: Stack(
alignment: Alignment.center,
children: backgroundElements + pictureElements),
color: Colors.black,
)
],
);
}
Widget createPicturePart(int id, BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
color_ = Color.fromARGB(
0xFF, rng.nextInt(255), rng.nextInt(255), rng.nextInt(255));
});
},
child: CustomPaint(
willChange: true,
size: Size(
(pictureScalar * size_.width),
(pictureScalar * size_.width * 1.4142756349952963)
.toDouble()),
painter: RPSCustomPainter(id, color_,
CurvedAnimation(parent: _controller, curve: Curves.easeInOut))),
);
}
}
class RPSCustomPainter extends CustomPainter {
final double maxval = 0.4;
final int id_;
final Color color_;
final Animation<double> animation_;
final Path path_ = Path();
RPSCustomPainter(this.id_, this.color_, this.animation_)
: super(repaint: animation_);
#override
void paint(Canvas canvas, Size size) {
path_.moveTo(
size.width * svgdata[id_][0][0], size.height * svgdata[id_][0][1]);
for (var i = 1; i < svgdata[id_].length; i++) {
path_.cubicTo(
size.width * svgdata[id_][i][0],
size.height * svgdata[id_][i][1],
size.width * svgdata[id_][i][2],
size.height * svgdata[id_][i][3],
size.width * svgdata[id_][i][4],
size.height * svgdata[id_][i][5]);
}
path_.close();
Paint paint0Fill = Paint()..style = PaintingStyle.fill;
int colorvalue = (animation_.value * maxval * 255).toInt();
paint0Fill.color =
Color.fromARGB(0xFF, colorvalue, colorvalue, colorvalue);
canvas.drawPath(path_, paint0Fill);
}
#override
bool shouldRepaint(RPSCustomPainter oldDelegate) {
return animation_ != oldDelegate.animation_;
}
#override
bool hitTest(Offset position) {
return path_.contains(position);
}
}
I've also read the flutter performance articles but they are pretty broad and I couldn't find anything to apply here.
Maybe you have any idea?
Thanks in advance!
P.S. I can add the svgtemp.dart if you need it. (~4000 lines)
I want to animate the line drawing in custom painter canvas. So far what I can do is create two circles at two points and then create a line between those two points. But I don't know how to animate the line as if it is going from one point to the other. I have tried something but I can't make it work. Please check the code and suggest me if you have any idea.
import 'package:flutter/material.dart';
import 'dart:math' as math;
class ProgressMonitorAnimation extends StatefulWidget {
#override
State<StatefulWidget> createState() => _ProgressMonitorAnimationState();
}
class _ProgressMonitorAnimationState extends State<ProgressMonitorAnimation> with TickerProviderStateMixin {
double _progress = 0.0;
Animation<double> animation;
#override
void initState() {
var controller = AnimationController(duration: Duration(milliseconds: 3000), vsync: this);
animation = Tween(begin: 1.0, end: 0.0).animate(controller)..addListener(() {
setState(() {
_progress = animation.value;
});
});
controller.forward();
super.initState();
}
#override
Widget build(BuildContext context) {
return Transform(
alignment: Alignment.center,
transform: Matrix4.rotationX(math.pi),
child: CustomPaint(
foregroundPainter: ProgressPainter(_progress),
),
);
}
}
class ProgressPainter extends CustomPainter {
double _progress;
ProgressPainter(this._progress);
#override
void paint(Canvas canvas, Size size) {
final Paint circlePainter = Paint()..color = Colors.green;
final Paint linePainter = Paint()..color = Colors.black..strokeWidth = 4..strokeCap = StrokeCap.round;
canvas.drawCircle(Offset(0.0, 30.0 * 3), 10.0, circlePainter);
canvas.drawCircle(Offset(15.0 * 2, 80.0 * 3), 10.0, circlePainter);
canvas.drawLine(Offset(0.0 / (_progress * 10), 30.0 * 3), Offset((30.0 * 3) + (15.0) / (_progress * 15) * 2, (80.0 * 3) / (_progress * 15)), linePainter);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
You can do as follows using flutter Custom Painter.
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class AnimatedPathPainter extends CustomPainter {
final Animation<double> _animation;
AnimatedPathPainter(this._animation) : super(repaint: _animation);
Path _createAnyPath(Size size) {
return Path()
// ..moveTo(size.height / 4, size.height / 4)
// ..lineTo(size.height, size.width / 2)
// ..lineTo(size.height / 2, size.width)
..quadraticBezierTo(size.height / 2, 100, size.width, size.height);
}
#override
void paint(Canvas canvas, Size size) {
final animationPercent = this._animation.value;
print("Painting + ${animationPercent} - ${size}");
final path = createAnimatedPath(_createAnyPath(size), animationPercent);
final Paint paint = Paint();
paint.color = Colors.black;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 4.0;
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
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 = new 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 AnimatedPathDemo extends StatefulWidget {
#override
_AnimatedPathDemoState createState() => _AnimatedPathDemoState();
}
class _AnimatedPathDemoState extends State<AnimatedPathDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Completer<GoogleMapController> _controllerMap = Completer();
static final CameraPosition _initialPosition = CameraPosition(
// target: LatLng(12.947437, 77.685345),
target: LatLng(7.8731, 80.7718),
zoom: 8,
);
void _startAnimation() {
_controller.stop();
_controller.reset();
_controller.repeat(
period: Duration(seconds: 2),
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: const Text('Animated Paint')),
body: Stack(
children: <Widget>[
GoogleMap(
// rotateGesturesEnabled: false,
mapType: MapType.normal,
compassEnabled: false,
initialCameraPosition: _initialPosition,
// polylines: _polylines,
// markers: _markers,
onMapCreated: (GoogleMapController controller) {
// controller.setMapStyle(Utils.mapStyles);
_controllerMap.complete(controller);
},
),
SizedBox(
height: 300,
width: 300,
child: new CustomPaint(
painter: new AnimatedPathPainter(_controller),
),
),
],
),
floatingActionButton: new FloatingActionButton(
onPressed: _startAnimation,
child: new Icon(Icons.play_arrow),
),
);
}
#override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
}
I'm trying to draw some pointing triangles on CustomPaint and I'm facing performance issues when I change the shape of the triangle. If you execute the current code the refresh rate is fast.
If you change the part Offset(x,y+8), with Offset(x,y+4), the triangle shape changes slightly but the performance drops and the refresh rate is poor.
Does anyone knows why that happens and how can I resolve this issue?
import 'dart:math' as Math;
import 'package:flutter/material.dart';
void main() =>
runApp(
MaterialApp(
title: 'Demo',
home: Scaffold(
body: Shapes()
),
)
);
class Shapes extends StatefulWidget {
#override
_ShapesState createState() => new _ShapesState();
}
class _ShapesState extends State<Shapes> with SingleTickerProviderStateMixin{
Animation<double> animation;
AnimationController animationController;
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
)..addListener(() => setState(() {}));
animation = Tween<double>(
begin: 50.0,
end: 120.0,
).animate(animationController);
animationController.forward();
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationController.repeat();
}
});
}
#override
Widget build(BuildContext context) {
return CustomPaint(painter: ShapesPainter());
}
}
class ShapesPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final paint = Paint();
paint.strokeWidth = 2;
for (var i = 0; i < 1000; i++) {
Math.Random rnd = new Math.Random();
double x = 5 + rnd.nextInt(300 - 5).toDouble();
double y = 5 + rnd.nextInt(1200 - 5).toDouble();
final Path path = Path();
path.addPolygon([
Offset(x+6,y+8),
Offset(x,y+-8),
Offset(x-6,y+8),
Offset(x,y+8),
], true);
paint.color = Color(0xFF3f6cbf);
paint.style = PaintingStyle.stroke;
canvas.drawPath(path, paint);
paint.color = Color.fromARGB(255, 255, 0, 0);
paint.style = PaintingStyle.fill;
canvas.drawPath(path, paint);
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
i am trying to achieved some thing like this in flutter
One way is with CustomPainter and an animation. Also look at SpriteWidget.
import 'dart:math';
import 'package:flutter/material.dart';
class SpritePainter extends CustomPainter {
final Animation<double> _animation;
SpritePainter(this._animation) : super(repaint: _animation);
void circle(Canvas canvas, Rect rect, double value) {
double opacity = (1.0 - (value / 4.0)).clamp(0.0, 1.0);
Color color = Color.fromRGBO(0, 117, 194, opacity);
double size = rect.width / 2;
double area = size * size;
double radius = sqrt(area * value / 4);
final Paint paint = Paint()..color = color;
canvas.drawCircle(rect.center, radius, paint);
}
#override
void paint(Canvas canvas, Size size) {
Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
for (int wave = 3; wave >= 0; wave--) {
circle(canvas, rect, wave + _animation.value);
}
}
#override
bool shouldRepaint(SpritePainter oldDelegate) {
return true;
}
}
class SpriteDemo extends StatefulWidget {
#override
SpriteDemoState createState() => SpriteDemoState();
}
class SpriteDemoState extends State<SpriteDemo>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
);
//_startAnimation();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void _startAnimation() {
_controller
..stop()
..reset()
..repeat(period: const Duration(seconds: 1));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Pulse')),
body: CustomPaint(
painter: SpritePainter(_controller),
child: SizedBox(
width: 200.0,
height: 200.0,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _startAnimation,
child: new Icon(Icons.play_arrow),
),
);
}
}
void main() {
runApp(
MaterialApp(
home: SpriteDemo(),
),
);
}
You can also use Flare with: flare_flutter. It's more simple.
For those looking to create an animation which is not circular, but rectangular with possible rounded borders, you can replace the SpritePainter from the top answer with:
class SpritePainter extends CustomPainter {
final Animation<double> _animation;
SpritePainter(this._animation) : super(repaint: _animation);
void roundedRect(Canvas canvas, Rect rect, double animValue, int waveAmount) {
double opacity = (1.0 - (animValue / waveAmount)).clamp(0.0, 1.0);
Color color = new Color.fromRGBO(0, 117, 194, opacity);
final pixelMiltiplier = 20;
final newWidth = rect.width + animValue*pixelMiltiplier;
final newHeight = rect.height + animValue*pixelMiltiplier;
final widthIncrease = newWidth/rect.width;
final heightIncrease = newHeight/rect.height;
final widthOffset = (widthIncrease - 1) / 2;
final heightOffet = (heightIncrease - 1) / 2;
final Paint paint = new Paint()..color = color;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(-rect.width * widthOffset, -rect.height * heightOffet,
rect.width * widthIncrease, rect.height * heightIncrease),
Radius.circular(15.0)),
paint);
}
#override
void paint(Canvas canvas, Size size) {
Rect rect = new Rect.fromLTRB(0.0, 0.0, size.width, size.height);
final waveAmount = 1;
if (!_animation.isDismissed) {
for (int wave = waveAmount-1; wave >= 0; wave--) {
roundedRect(canvas, rect, wave + _animation.value, waveAmount);
}
}
}
#override
bool shouldRepaint(SpritePainter oldDelegate) {
return true;
}
}