mirror of https://github.com/flutter/samples.git
Drop `web/particle_background` (#1453)
parent
cb46fade86
commit
ee72fc8ad7
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Felix Blaschke
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,7 +0,0 @@
|
||||
Flutter app demonstrating
|
||||
[simple_animations](https://pub.dev/packages/simple_animations) in action.
|
||||
|
||||
Contributed by Felix Blaschke.
|
||||
|
||||
One of a series of samples
|
||||
[published here](https://github.com/felixblaschke/simple_animations/tree/2.4.2/example).
|
@ -1,11 +0,0 @@
|
||||
include: ../../analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: true
|
||||
implicit-dynamic: true
|
||||
|
||||
linter:
|
||||
rules:
|
||||
constant_identifier_names: false
|
||||
|
Before Width: | Height: | Size: 20 KiB |
@ -1,181 +0,0 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:particle_background/simple_animations_package.dart';
|
||||
|
||||
void main() => runApp(const ParticleApp());
|
||||
|
||||
class ParticleApp extends StatelessWidget {
|
||||
const ParticleApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const MaterialApp(
|
||||
home: Scaffold(
|
||||
body: ParticleBackgroundPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ParticleBackgroundPage extends StatelessWidget {
|
||||
const ParticleBackgroundPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: const [
|
||||
Positioned.fill(child: AnimatedBackground()),
|
||||
Positioned.fill(child: Particles(30)),
|
||||
Positioned.fill(child: CenteredText()),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Particles extends StatefulWidget {
|
||||
final int numberOfParticles;
|
||||
|
||||
const Particles(this.numberOfParticles, {super.key});
|
||||
|
||||
@override
|
||||
State<Particles> createState() => _ParticlesState();
|
||||
}
|
||||
|
||||
class _ParticlesState extends State<Particles> {
|
||||
final Random random = Random();
|
||||
|
||||
final List<ParticleModel> particles = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
List.generate(widget.numberOfParticles, (index) {
|
||||
particles.add(ParticleModel(random));
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Rendering(
|
||||
startTime: const Duration(seconds: 30),
|
||||
onTick: _simulateParticles,
|
||||
builder: (context, time) {
|
||||
return CustomPaint(
|
||||
painter: ParticlePainter(particles, time),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_simulateParticles(Duration time) {
|
||||
for (var particle in particles) {
|
||||
particle.maintainRestart(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ParticleModel {
|
||||
late Animatable tween;
|
||||
late double size;
|
||||
late AnimationProgress animationProgress;
|
||||
Random random;
|
||||
|
||||
ParticleModel(this.random) {
|
||||
restart();
|
||||
}
|
||||
|
||||
restart({Duration time = Duration.zero}) {
|
||||
final startPosition = Offset(-0.2 + 1.4 * random.nextDouble(), 1.2);
|
||||
final endPosition = Offset(-0.2 + 1.4 * random.nextDouble(), -0.2);
|
||||
final duration = Duration(milliseconds: 3000 + random.nextInt(6000));
|
||||
|
||||
tween = MultiTrackTween([
|
||||
Track("x").add(
|
||||
duration, Tween(begin: startPosition.dx, end: endPosition.dx),
|
||||
curve: Curves.easeInOutSine),
|
||||
Track("y").add(
|
||||
duration, Tween(begin: startPosition.dy, end: endPosition.dy),
|
||||
curve: Curves.easeIn),
|
||||
]);
|
||||
animationProgress = AnimationProgress(duration: duration, startTime: time);
|
||||
size = 0.2 + random.nextDouble() * 0.4;
|
||||
}
|
||||
|
||||
maintainRestart(Duration time) {
|
||||
if (animationProgress.progress(time) == 1.0) {
|
||||
restart(time: time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ParticlePainter extends CustomPainter {
|
||||
List<ParticleModel> particles;
|
||||
Duration time;
|
||||
|
||||
ParticlePainter(this.particles, this.time);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()..color = Colors.white.withAlpha(50);
|
||||
|
||||
for (var particle in particles) {
|
||||
var progress = particle.animationProgress.progress(time);
|
||||
final animation = particle.tween.transform(progress);
|
||||
final position =
|
||||
Offset(animation["x"] * size.width, animation["y"] * size.height);
|
||||
canvas.drawCircle(position, size.width * 0.2 * particle.size, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) => true;
|
||||
}
|
||||
|
||||
class AnimatedBackground extends StatelessWidget {
|
||||
const AnimatedBackground({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tween = MultiTrackTween([
|
||||
Track("color1").add(
|
||||
const Duration(seconds: 3),
|
||||
ColorTween(
|
||||
begin: const Color(0xff8a113a), end: Colors.lightBlue.shade900)),
|
||||
Track("color2").add(const Duration(seconds: 3),
|
||||
ColorTween(begin: const Color(0xff440216), end: Colors.blue.shade600))
|
||||
]);
|
||||
|
||||
return ControlledAnimation(
|
||||
playback: Playback.MIRROR,
|
||||
tween: tween,
|
||||
duration: tween.duration,
|
||||
builder: (context, dynamic animation) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [animation["color1"], animation["color2"]])),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CenteredText extends StatelessWidget {
|
||||
const CenteredText({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: Text(
|
||||
"Welcome to Flutter for web",
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w200),
|
||||
textScaleFactor: 4,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,455 +0,0 @@
|
||||
// Package simple_animations:
|
||||
// https://pub.dev/packages/simple_animations
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
/// Widget to easily create a continuous animation.
|
||||
///
|
||||
/// You need to specify a [builder] function that gets the build context passed
|
||||
/// along with the [timeElapsed] (as a [Duration]) since the rendering started.
|
||||
/// You can use this time to specify custom animations on it.
|
||||
///
|
||||
/// The [builder] rebuilds all sub-widgets every frame.
|
||||
///
|
||||
/// You define an [onTick] function that is called before builder to update
|
||||
/// you rendered scene. It's also utilized during fast-forwarding the animation.
|
||||
///
|
||||
/// Specify a [startTime] to fast-forward your animation in the beginning.
|
||||
/// The widget will interpolate the animation by calling the [onTick] function
|
||||
/// multiple times. (Default value is `20`. You can tune it by setting the
|
||||
/// [startTimeSimulationTicks] property.)
|
||||
class Rendering extends StatefulWidget {
|
||||
final Widget Function(BuildContext context, Duration timeElapsed) builder;
|
||||
final Function(Duration timeElapsed)? onTick;
|
||||
final Duration startTime;
|
||||
final int startTimeSimulationTicks;
|
||||
|
||||
const Rendering(
|
||||
{required this.builder,
|
||||
this.onTick,
|
||||
this.startTime = Duration.zero,
|
||||
this.startTimeSimulationTicks = 20,
|
||||
super.key});
|
||||
|
||||
@override
|
||||
State<Rendering> createState() => _RenderingState();
|
||||
}
|
||||
|
||||
class _RenderingState extends State<Rendering>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late Ticker _ticker;
|
||||
Duration _timeElapsed = const Duration(milliseconds: 0);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.startTime > Duration.zero) {
|
||||
_simulateStartTimeTicks();
|
||||
}
|
||||
|
||||
_ticker = createTicker((elapsed) {
|
||||
_onRender(elapsed + widget.startTime);
|
||||
});
|
||||
_ticker.start();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _onRender(Duration effectiveElapsed) {
|
||||
if (widget.onTick != null) {
|
||||
widget.onTick!(effectiveElapsed);
|
||||
}
|
||||
setState(() {
|
||||
_timeElapsed = effectiveElapsed;
|
||||
});
|
||||
}
|
||||
|
||||
void _simulateStartTimeTicks() {
|
||||
if (widget.onTick != null) {
|
||||
for (var i in Iterable.generate(widget.startTimeSimulationTicks + 1)) {
|
||||
final simulatedTime = Duration(
|
||||
milliseconds: (widget.startTime.inMilliseconds *
|
||||
i /
|
||||
widget.startTimeSimulationTicks)
|
||||
.round());
|
||||
widget.onTick!(simulatedTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_ticker.stop(canceled: true);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.builder(context, _timeElapsed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Animatable that tweens multiple parallel properties (called [Track]s).
|
||||
/// ---
|
||||
/// The constructor of [MultiTrackTween] expects a list of [Track] objects.
|
||||
/// You can fetch the specified total duration via [duration] getter.
|
||||
/// ---
|
||||
/// Example:
|
||||
///
|
||||
/// ```dart
|
||||
/// final tween = MultiTrackTween([
|
||||
/// Track("color")
|
||||
/// .add(Duration(seconds: 1), ColorTween(begin: Colors.red, end: Colors.blue))
|
||||
/// .add(Duration(seconds: 1), ColorTween(begin: Colors.blue, end: Colors.yellow)),
|
||||
/// Track("width")
|
||||
/// .add(Duration(milliseconds: 500), ConstantTween(200.0))
|
||||
/// .add(Duration(milliseconds: 1500), Tween(begin: 200.0, end: 400.0),
|
||||
/// curve: Curves.easeIn)
|
||||
/// ]);
|
||||
///
|
||||
/// return ControlledAnimation(
|
||||
/// duration: tween.duration,
|
||||
/// tween: tween,
|
||||
/// builder: (context, values) {
|
||||
/// ...
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
class MultiTrackTween extends Animatable<Map<String, dynamic>> {
|
||||
final _tracksToTween = <String, _TweenData>{};
|
||||
var _maxDuration = 0;
|
||||
|
||||
MultiTrackTween(List<Track> tracks)
|
||||
: assert(tracks.isNotEmpty, "Add a List<Track> to configure the tween."),
|
||||
assert(tracks.where((track) => track.items.isEmpty).isEmpty,
|
||||
"Each Track needs at least one added Tween by using the add()-method.") {
|
||||
_computeMaxDuration(tracks);
|
||||
_computeTrackTweens(tracks);
|
||||
}
|
||||
|
||||
void _computeMaxDuration(List<Track> tracks) {
|
||||
for (var track in tracks) {
|
||||
final trackDuration = track.items
|
||||
.map((item) => item.duration.inMilliseconds)
|
||||
.reduce((sum, item) => sum + item);
|
||||
_maxDuration = max(_maxDuration, trackDuration);
|
||||
}
|
||||
}
|
||||
|
||||
void _computeTrackTweens(List<Track> tracks) {
|
||||
for (var track in tracks) {
|
||||
final trackDuration = track.items
|
||||
.map((item) => item.duration.inMilliseconds)
|
||||
.reduce((sum, item) => sum + item);
|
||||
|
||||
final sequenceItems = track.items
|
||||
.map((item) => TweenSequenceItem(
|
||||
tween: item.tween,
|
||||
weight: item.duration.inMilliseconds / _maxDuration))
|
||||
.toList();
|
||||
|
||||
if (trackDuration < _maxDuration) {
|
||||
sequenceItems.add(TweenSequenceItem(
|
||||
tween: ConstantTween(null),
|
||||
weight: (_maxDuration - trackDuration) / _maxDuration));
|
||||
}
|
||||
|
||||
final sequence = TweenSequence(sequenceItems);
|
||||
|
||||
_tracksToTween[track.name] =
|
||||
_TweenData(tween: sequence, maxTime: trackDuration / _maxDuration);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the highest duration specified by [Track]s.
|
||||
/// ---
|
||||
/// Use it to pass it into an [ControlledAnimation].
|
||||
///
|
||||
/// You can also scale it by multiplying a double value.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final tween = MultiTrackTween(listOfTracks);
|
||||
///
|
||||
/// return ControlledAnimation(
|
||||
/// duration: tween.duration * 1.25, // stretch animation by 25%
|
||||
/// tween: tween,
|
||||
/// builder: (context, values) {
|
||||
/// ...
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
Duration get duration {
|
||||
return Duration(milliseconds: _maxDuration);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> transform(double t) {
|
||||
final Map<String, dynamic> result = {};
|
||||
_tracksToTween.forEach((name, tweenData) {
|
||||
final double tTween = max(0, min(t, tweenData.maxTime! - 0.0001));
|
||||
result[name] = tweenData.tween!.transform(tTween);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Single property to tween. Used by [MultiTrackTween].
|
||||
class Track<T> {
|
||||
final String name;
|
||||
final List<TrackItem> items = [];
|
||||
|
||||
Track(this.name);
|
||||
|
||||
/// Adds a "piece of animation" to a [Track].
|
||||
///
|
||||
/// You need to pass a [duration] and a [tween]. It will return the track, so
|
||||
/// you can specify a track in a builder's style.
|
||||
///
|
||||
/// Optionally you can set a named parameter [curve] that applies an easing
|
||||
/// curve to the tween.
|
||||
Track<T> add(Duration duration, Animatable<T> tween, {Curve? curve}) {
|
||||
items.add(TrackItem(duration, tween, curve: curve));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class TrackItem<T> {
|
||||
final Duration duration;
|
||||
late Animatable<T> tween;
|
||||
|
||||
TrackItem(this.duration, Animatable<T> tween, {Curve? curve}) {
|
||||
if (curve != null) {
|
||||
this.tween = tween.chain(CurveTween(curve: curve));
|
||||
} else {
|
||||
this.tween = tween;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _TweenData<T> {
|
||||
final Animatable<T>? tween;
|
||||
final double? maxTime;
|
||||
|
||||
_TweenData({this.tween, this.maxTime});
|
||||
}
|
||||
|
||||
/// Playback tell the controller of the animation what to do.
|
||||
enum Playback {
|
||||
/// Animation stands still.
|
||||
PAUSE,
|
||||
|
||||
/// Animation plays forwards and stops at the end.
|
||||
PLAY_FORWARD,
|
||||
|
||||
/// Animation plays backwards and stops at the beginning.
|
||||
PLAY_REVERSE,
|
||||
|
||||
/// Animation will reset to the beginning and start playing forward.
|
||||
START_OVER_FORWARD,
|
||||
|
||||
/// Animation will reset to the end and start play backward.
|
||||
START_OVER_REVERSE,
|
||||
|
||||
/// Animation will play forwards and start over at the beginning when it
|
||||
/// reaches the end.
|
||||
LOOP,
|
||||
|
||||
/// Animation will play forward until the end and will reverse playing until
|
||||
/// it reaches the beginning. Then it starts over playing forward. And so on.
|
||||
MIRROR
|
||||
}
|
||||
|
||||
/// Widget to create custom, managed, tween-based animations in a very simple way.
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// An internal [AnimationController] will do everything you tell him by
|
||||
/// dynamically assigning the one [Playback] to [playback] property.
|
||||
/// By default the animation will start playing forward and stops at the end.
|
||||
///
|
||||
/// A minimum set of properties are [duration] (time span of the animation),
|
||||
/// [tween] (values to interpolate among the animation) and a [builder] function
|
||||
/// (defines the animated scene).
|
||||
///
|
||||
/// Instead of using [builder] as building function you can use for performance
|
||||
/// critical scenarios [builderWithChild] along with a prebuild [child].
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// The following properties are optional:
|
||||
///
|
||||
/// - You can apply a [delay] that forces the animation to pause a
|
||||
/// specified time before the animation will perform the defined [playback]
|
||||
/// instruction.
|
||||
///
|
||||
/// - You can specify a [curve] that modifies the [tween] by applying a
|
||||
/// non-linear animation function. You can find curves in [Curves], for
|
||||
/// example [Curves.easeOut] or [Curves.easeIn].
|
||||
///
|
||||
/// - You can track the animation by setting an [AnimationStatusListener] to
|
||||
/// the property [animationControllerStatusListener]. The internal [AnimationController] then
|
||||
/// will route out any events that occur. [ControlledAnimation] doesn't filter
|
||||
/// or modifies these events. These events are currently only reliable for the
|
||||
/// [playback]-types [Playback.PLAY_FORWARD] and [Playback.PLAY_REVERSE].
|
||||
///
|
||||
/// - You can set the start position of animation by specifying [startPosition]
|
||||
/// with a value between *0.0* and *1.0*. The [startPosition] is only
|
||||
/// evaluated once during the initialization of the widget.
|
||||
///
|
||||
class ControlledAnimation<T> extends StatefulWidget {
|
||||
final Playback playback;
|
||||
final Animatable<T> tween;
|
||||
final Curve curve;
|
||||
final Duration duration;
|
||||
final Duration? delay;
|
||||
final Widget Function(BuildContext buildContext, T animatedValue)? builder;
|
||||
final Widget Function(BuildContext, Widget? child, T animatedValue)?
|
||||
builderWithChild;
|
||||
final Widget? child;
|
||||
final AnimationStatusListener? animationControllerStatusListener;
|
||||
final double startPosition;
|
||||
|
||||
const ControlledAnimation(
|
||||
{this.playback = Playback.PLAY_FORWARD,
|
||||
required this.tween,
|
||||
this.curve = Curves.linear,
|
||||
required this.duration,
|
||||
this.delay,
|
||||
this.builder,
|
||||
this.builderWithChild,
|
||||
this.child,
|
||||
this.animationControllerStatusListener,
|
||||
this.startPosition = 0.0,
|
||||
super.key})
|
||||
: assert(
|
||||
(builderWithChild != null && child != null && builder == null) ||
|
||||
(builder != null && builderWithChild == null && child == null),
|
||||
"Either use just builder and keep buildWithChild and child null. "
|
||||
"Or keep builder null and set a builderWithChild and a child."),
|
||||
assert(
|
||||
startPosition >= 0 && startPosition <= 1,
|
||||
"The property startPosition "
|
||||
"must have a value between 0.0 and 1.0.");
|
||||
|
||||
@override
|
||||
State<ControlledAnimation> createState() => _ControlledAnimationState<T>();
|
||||
}
|
||||
|
||||
class _ControlledAnimationState<T> extends State<ControlledAnimation>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<T?> _animation;
|
||||
bool _isDisposed = false;
|
||||
bool _waitForDelay = true;
|
||||
bool _isCurrentlyMirroring = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller = AnimationController(vsync: this, duration: widget.duration)
|
||||
..addListener(() {
|
||||
setState(() {});
|
||||
})
|
||||
..value = widget.startPosition;
|
||||
|
||||
_animation = widget.tween
|
||||
.chain(CurveTween(curve: widget.curve))
|
||||
.animate(_controller) as Animation<T?>;
|
||||
|
||||
if (widget.animationControllerStatusListener != null) {
|
||||
_controller.addStatusListener(widget.animationControllerStatusListener!);
|
||||
}
|
||||
|
||||
initialize();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> initialize() async {
|
||||
if (widget.delay != null) {
|
||||
await Future.delayed(widget.delay!);
|
||||
}
|
||||
_waitForDelay = false;
|
||||
await executeInstruction();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(ControlledAnimation oldWidget) {
|
||||
_controller.duration = widget.duration;
|
||||
executeInstruction();
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
Future<void> executeInstruction() async {
|
||||
if (_isDisposed || _waitForDelay) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.playback == Playback.PAUSE) {
|
||||
_controller.stop();
|
||||
}
|
||||
if (widget.playback == Playback.PLAY_FORWARD) {
|
||||
await _controller.forward();
|
||||
}
|
||||
if (widget.playback == Playback.PLAY_REVERSE) {
|
||||
await _controller.reverse();
|
||||
}
|
||||
if (widget.playback == Playback.START_OVER_FORWARD) {
|
||||
await _controller.forward(from: 0.0);
|
||||
}
|
||||
if (widget.playback == Playback.START_OVER_REVERSE) {
|
||||
await _controller.reverse(from: 1.0);
|
||||
}
|
||||
if (widget.playback == Playback.LOOP) {
|
||||
await _controller.repeat();
|
||||
}
|
||||
if (widget.playback == Playback.MIRROR && !_isCurrentlyMirroring) {
|
||||
_isCurrentlyMirroring = true;
|
||||
await _controller.repeat(reverse: true);
|
||||
}
|
||||
|
||||
if (widget.playback != Playback.MIRROR) {
|
||||
_isCurrentlyMirroring = false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.builder != null) {
|
||||
return widget.builder!(context, _animation.value);
|
||||
} else if (widget.builderWithChild != null && widget.child != null) {
|
||||
return widget.builderWithChild!(context, widget.child, _animation.value);
|
||||
}
|
||||
_controller.stop(canceled: true);
|
||||
throw FlutterError(
|
||||
"I don't know how to build the animation. Make sure to either specify "
|
||||
"a builder or a builderWithChild (along with a child).");
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isDisposed = true;
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility class to compute an animation progress between two points in time.
|
||||
///
|
||||
/// On creation you specify a [startTime] and a [duration].
|
||||
///
|
||||
/// You can query the progress value - a value between `0.0` and `11.0` - by
|
||||
/// calling [progress] and passing the current time.
|
||||
class AnimationProgress {
|
||||
final Duration duration;
|
||||
final Duration startTime;
|
||||
|
||||
/// Creates an [AnimationProgress].
|
||||
AnimationProgress({required this.duration, required this.startTime});
|
||||
|
||||
/// Queries the current progress value based on the specified [startTime] and
|
||||
/// [duration] as a value between `0.0` and `1.0`. It will automatically
|
||||
/// clamp values this interval to fit in.
|
||||
double progress(Duration time) => max(0.0,
|
||||
min((time - startTime).inMilliseconds / duration.inMilliseconds, 1.0));
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
name: particle_background
|
||||
description: Example for the simple_animations package
|
||||
homepage: https://github.com/felixblaschke/simple_animations
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0-0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.1
|
||||
flutter:
|
||||
assets:
|
||||
- preview.png
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title></title>
|
||||
<script defer src="main.dart.js" type="application/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 878 KiB |
Loading…
Reference in new issue