Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed improvements and added more config #6

Merged
merged 5 commits into from
Nov 13, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 138 additions & 72 deletions lib/avatar_glow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,32 @@ import 'dart:async';
import 'package:flutter/material.dart';

class AvatarGlow extends StatefulWidget {
final bool repeat;
final Duration duration;
final Widget child;
final double endRadius;
final BoxShape shape;
final Duration duration;
final bool repeat;
final bool animate;
final Duration repeatPauseDuration;
final Widget child;
final Curve curve;
final bool showTwoGlows;
final Color glowColor;
final Duration startDelay;
final BoxShape shape;

AvatarGlow({
@required this.endRadius,
const AvatarGlow({
Key key,
@required this.child,
this.shape,
this.duration,
@required this.endRadius,
this.shape = BoxShape.circle,
this.duration = const Duration(milliseconds: 2000),
this.repeat = true,
this.repeatPauseDuration,
this.animate = true,
this.repeatPauseDuration = const Duration(milliseconds: 100),
this.curve = Curves.fastOutSlowIn,
this.showTwoGlows = true,
this.glowColor,
this.glowColor = Colors.white,
this.startDelay,
});
}) : super(key: key);

@override
_AvatarGlowState createState() => _AvatarGlowState();
Expand All @@ -37,41 +42,84 @@ class _AvatarGlowState extends State<AvatarGlow>
Animation<double> bigDiscAnimation;
Animation<double> alphaAnimation;
AnimationController controller;
void Function(AnimationStatus status) listener;

@override
void initState() {
super.initState();
controller = AnimationController(
duration: widget.duration ?? Duration(milliseconds: 2000), vsync: this);
final Animation curve =
CurvedAnimation(parent: controller, curve: Curves.decelerate);
duration: widget.duration,
vsync: this,
);

_createAnimation();

if (widget.animate) {
_startAnimation();
}
super.initState();
}

@override
void didUpdateWidget(AvatarGlow oldWidget) {
// Fields which will trigger new animation values
if (widget.duration != oldWidget.duration ||
widget.curve != oldWidget.curve ||
widget.endRadius != oldWidget.endRadius) {
controller.duration = widget.duration;
_createAnimation();
}

if (widget.animate != oldWidget.animate) {
if (widget.animate) {
_startAnimation();
} else {
_stopAnimation();
}
}
super.didUpdateWidget(oldWidget);
}

void _createAnimation() {
final Animation curve = CurvedAnimation(
parent: controller,
curve: widget.curve,
);

smallDiscAnimation = Tween(
begin: (widget.endRadius * 2) / 6,
end: (widget.endRadius * 2) * (3 / 4))
.animate(curve)
..addListener(() {
setState(() {});
});
bigDiscAnimation =
Tween(begin: 0.0, end: (widget.endRadius * 2)).animate(curve)
..addListener(() {
setState(() {});
});
begin: (widget.endRadius * 2) / 6,
end: (widget.endRadius * 2) * (3 / 4),
).animate(curve);

bigDiscAnimation = Tween(
begin: 0.0,
end: (widget.endRadius * 2),
).animate(curve);

alphaAnimation = Tween(begin: 0.30, end: 0.0).animate(controller);
controller.addStatusListener((_) async {

controller.removeStatusListener(listener);

listener = (_) async {
if (controller.status == AnimationStatus.completed) {
await Future.delayed(
widget.repeatPauseDuration ?? Duration(milliseconds: 100));
if (mounted && widget.repeat) {
await Future.delayed(widget.repeatPauseDuration);

if (mounted && widget.repeat && widget.animate) {
controller.reset();
controller.forward();
}
}
});
startAnimation();
};

controller.addStatusListener(listener);
}

void startAnimation() async {
@override
void dispose() {
controller.dispose();
super.dispose();
}

void _startAnimation() async {
if (widget.startDelay != null) {
await Future.delayed(widget.startDelay);
if (mounted) controller.forward();
Expand All @@ -80,48 +128,66 @@ class _AvatarGlowState extends State<AvatarGlow>
}
}

void _stopAnimation() async {
controller?.reset();
controller?.stop();
}

@override
Widget build(BuildContext context) {
return Container(
height: widget.endRadius * 2,
width: widget.endRadius * 2,
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
height: bigDiscAnimation.value,
width: bigDiscAnimation.value,
child: SizedBox(),
decoration: BoxDecoration(
shape: widget.shape ?? BoxShape.circle,
color: (widget.glowColor ?? Colors.white)
.withOpacity(alphaAnimation.value),
),
return AnimatedBuilder(
animation: alphaAnimation,
child: widget.child,
builder: (context, widgetChild) {
final decoration = BoxDecoration(
shape: widget.shape,
// If the user picks a curve that goes below 0 or above 1
// this opacity will have unexpected effects without clamping
color: widget.glowColor
.withOpacity(alphaAnimation.value.clamp(0.0, 1.0)),
);

return Container(
height: widget.endRadius * 2,
width: widget.endRadius * 2,
child: Stack(
alignment: Alignment.center,
children: <Widget>[
AnimatedBuilder(
animation: bigDiscAnimation,
builder: (context, widget) {
// If the user picks a curve that goes below 0,
// this will throw without clamping
final size =
bigDiscAnimation.value.clamp(0.0, double.infinity);

return Container(
height: size,
width: size,
decoration: decoration,
);
},
),
widget.showTwoGlows
? AnimatedBuilder(
animation: smallDiscAnimation,
builder: (context, widget) {
final size = smallDiscAnimation.value
.clamp(0.0, double.infinity);

return Container(
height: size,
width: size,
decoration: decoration,
);
},
)
: const SizedBox(height: 0.0, width: 0.0),
widgetChild,
],
),
widget.showTwoGlows
? Container(
height: smallDiscAnimation.value,
width: smallDiscAnimation.value,
child: SizedBox(),
decoration: BoxDecoration(
shape: widget.shape ?? BoxShape.circle,
color: (widget.glowColor ?? Colors.white)
.withOpacity(alphaAnimation.value),
),
)
: SizedBox(
height: 0.0,
width: 0.0,
),
widget.child,
],
),
);
},
);
}

@override
void dispose() {
controller.dispose();
super.dispose();
}
}