Countdown in text flutter - dart

We need to create a 5 minute countdown text object in Flutter, once the countdown reaches zero then we need to launch a new screen. here is the current code:
Container(
margin: EdgeInsets.only(left: 0.0,top: 60.0, bottom: 50.0, right:0.0),
child: Text(
'5:00', style: new TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 100.0 ))
,
),

I usually don't post just sample code, but I was in the mood of coding a bit in flutter. So here's my quick suggestion on how you can implement this:
class _MyHomePageState extends State<MyHomePage> {
TickViewModel _viewModel;
int _seconds;
StreamSubscription<int> _tickSubscription;
#override
void initState() {
super.initState();
_viewModel = TickViewModel();
_tickSubscription = _viewModel._tickStream.listen((newTime) {
if (newTime == 0) {
//TODO: DO THE SCREEN CHANGE HERE;
}
setState(() {
_seconds = newTime;
});
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("My Flutter App"),
backgroundColor: Colors.red,
),
body: Text(_computeTime()));
}
String _computeTime() {
if (_seconds == null) {
return "-";
}
String minutes = "${_seconds ~/ 60}".padLeft(2, "0");
String seconds = "${_seconds % 60}".padLeft(2, "0");
return "$minutes:$seconds";
}
#override
void dispose() {
//We cleanup the stuffs here
_tickSubscription.cancel();
_viewModel.destroy();
super.dispose();
}
}
class TickViewModel {
static const int _fiveMinutes = 5 * 60;
StreamController<int> _ticker;
Stream<int> get _tickStream => _ticker.stream;
Timer _timer;
final int seconds;
int _internalSeconds;
TickViewModel({this.seconds = _fiveMinutes}) : _internalSeconds = seconds {
_ticker = StreamController(onListen: () {
//start timer only when we have a subscriber
_startTimer();
}, onCancel: () {
_timer.cancel();
});
}
void _startTimer() {
//start timer to tick every 1 second
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
_internalSeconds -= 1;
if (_internalSeconds < 0) {
_timer.cancel();
} else {
_ticker.add(_internalSeconds);
}
});
}
void destroy() {
_ticker.close();
_timer.cancel();
}
}

Related

FlutterSoundRecorder.onProgress never hasData in StreamBuilder

I am following this 5 minutes video to set up an audio recorder in Flutter.
When I click the ElevatedButton to start recording the audio, it does change between play and stop, and an audio file is created, but the snapshot.hasData is always false, so the Text stays 00:00 during recording. The only information I found is about setSubscriptionDuration, which I did set. I also tried flutter clean, etc. What else can it be?
I'm using Flutter 3.3.8, on macOS, flutter_sound: ^9.1.9. I'm running the app on a real iPhone XR with flutter run
I am new to flutter. I really appreciate any help you can provide!
I have
StreamBuilder
StreamBuilder<RecordingDisposition>(
stream: recorder.onProgress,
builder: (context, snapshot) {
print('snapshot.hasData :${snapshot.hasData}');
final duration =
snapshot.hasData ? snapshot.data!.duration : Duration.zero;
print('duration :$duration');
String twoDigits(int n) => n.toString().padLeft(2, '0');
final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text(
'$twoDigitMinutes:$twoDigitSeconds',
style: const TextStyle(
fontSize: 20,
),
);
},
),
ElevatedButton
ElevatedButton(
child: Icon(
recorder.isRecording ? Icons.stop : Icons.mic,
size: 20,
),
onPressed: () async {
if (recorder.isRecording) {
await stop();
} else {
await record();
}
setState(() {});
},
)
Initialize the recorder properly
final recorder = FlutterSoundRecorder();
Future<void> initRecorder() async {
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw 'Microphone permission not granted';
}
await recorder.openRecorder();
isRecorderReady = true;
recorder.setSubscriptionDuration(
const Duration(
microseconds: 100,
),
);
}
#override
void initState() {
super.initState();
initRecorder();
}
This is what it looks like so far:
So, I found a solution, but the StreamBuilder question is not answered. Instead of using StreamBuilder, create a stateful TimerWidget that's initialized by a ValueNotifier.
import 'dart:async';
import 'package:flutter/material.dart';
enum Time { start, pause, reset }
class TimerController extends ValueNotifier<Time> {
TimerController({Time time = Time.reset}) : super(time);
void startTimer() => value = Time.start;
void pauseTimer() => value = Time.pause;
void resetTimer() => value = Time.reset;
}
class TimerWidget extends StatefulWidget {
final TimerController controller;
const TimerWidget({
Key? key,
required this.controller,
}) : super(key: key);
#override
_TimerWidgetState createState() => _TimerWidgetState();
}
class _TimerWidgetState extends State<TimerWidget> {
Duration duration = const Duration();
Timer? timer;
#override
void initState() {
super.initState();
widget.controller.addListener(() {
switch (widget.controller.value) {
case Time.start:
startTimer();
break;
case Time.pause:
stopTimer();
break;
case Time.reset:
reset();
stopTimer();
break;
}
});
}
void reset() => setState(() => duration = const Duration());
void addTime() {
const addSeconds = 1;
setState(() {
final seconds = duration.inSeconds + addSeconds;
if (seconds < 0) {
timer?.cancel();
} else {
duration = Duration(seconds: seconds);
}
});
}
void startTimer({bool resets = true}) {
if (!mounted) return;
timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
}
void stopTimer() {
if (!mounted) return;
setState(() => timer?.cancel());
}
#override
Widget build(BuildContext context) => Center(child: buildTime());
Widget buildTime() {
String twoDigits(int n) => n.toString().padLeft(2, "0");
final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text(
'$twoDigitMinutes:$twoDigitSeconds',
style: const TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
),
);
}
}
change the microseconds: 100, to millisecond:100 in recorder.setSubscriptionDuration
recorder.setSubscriptionDuration(
const Duration(milliseconds: 100),
);
like this

Pause Flutter Countdown Timer

I have been playing with Dart Timer Class that I got it to work in its very basic form, however I am stuck trying to add a pause function to it. I have looked into their documentations, but their is not much about their Timer class...
Is there any way I can pause and resume the timer/countdown on click? This is what I have achieved so far:
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Timer _timer;
int _start = 10;
void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(() {
if (_start < 1) {
timer.cancel();
} else {
_start = _start - 1;
}
}));
}
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: Text("Timer test")),
body: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
startTimer();
},
child: Text("start"),
),
Text("$_start")
],
));
}
}
I just uploaded this package to implement exactly this: https://pub.dev/packages/pausable_timer
// It starts paused
final timer = PausableTimer(Duration(seconds: 1), () => print('Fired!'));
timer.start();
timer.pause();
timer.start();
There is also an issue requesting the feature in Dart itself, but it doesn't look like it will be added soon.
There is no built-in pause function, since the Timer class is mainly intended for scheduling blocks of code for later.
What is your use case? The Stopwatch class has pause and resume funtionality.
Working flutter example with pause and vibrations is below (source)
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:vibration/vibration.dart';
void main() => runApp(MaterialApp(home: CountdownCard()));
class CountdownCard extends StatefulWidget {
// This widget is the root of your application.
#override
_CountdownCardState createState() => _CountdownCardState();
}
class _CountdownCardState extends State<CountdownCard> {
Timer _timer;
int _start = 0;
bool _vibrationActive = false;
void startTimer(int timerDuration) {
if (_timer != null) {
_timer.cancel();
cancelVibrate();
}
setState(() {
_start = timerDuration;
});
const oneSec = const Duration(seconds: 1);
print('test');
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(
() {
if (_start < 1) {
timer.cancel();
print('alarm');
vibrate();
} else {
_start = _start - 1;
}
},
),
);
}
void cancelVibrate() {
_vibrationActive = false;
Vibration.cancel();
}
void vibrate() async {
_vibrationActive = true;
if (await Vibration.hasVibrator()) {
while (_vibrationActive) {
Vibration.vibrate(duration: 1000);
await Future.delayed(Duration(seconds: 2));
}
}
}
void pauseTimer() {
if (_timer != null) _timer.cancel();
}
void unpauseTimer() => startTimer(_start);
#override
void dispose() {
_timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Countdown'),
),
body: Wrap(children: <Widget>[
Column(
children: <Widget>[
RaisedButton(
onPressed: () {
startTimer(10);
},
child: Text("start"),
),
Text("$_start"),
RaisedButton(
onPressed: () {
pauseTimer();
},
child: Text("pause"),
),
RaisedButton(
onPressed: () {
unpauseTimer();
},
child: Text("unpause"),
),
RaisedButton(
onPressed: () {
cancelVibrate();
},
child: Text("stop alarm"),
),
],
),
]));
}
}
variables we define
start timer on page opening.
timer off when page is closed.
if the screen is touched, the variable duration becomes true and the timer stops counting down
Timer? _timer;
int _start = 200;
bool duration = false;
Duration oneSec = const Duration(milliseconds: 10);
#override
void initState() {
// TODO: implement initState
super.initState();
startTimer();
}
#override
void dispose() {
_timer!.cancel();
super.dispose();
}
```
GestureDetector(
onTap: () {},
onTapDown: (e) {
setState(() {
duration = true;
});
},
onHorizontalDragEnd: (e) {
Navigator.pop(context);
},
onTapUp: (e) {
setState(() {
duration = false;
});
},
child:Text("demo page")}
void startTimer() {
_timer = new Timer.periodic(
oneSec,
(Timer timer) {
if (_start == 0) {
setState(() {
timer.cancel();
Navigator.pop(context);
});
} else {
setState(() {
duration == false ? _start-- : null;
});
}
},
);
}
}

Flutter change state from StatefulWidget object

Like title states, how can one access the state of a StatefulWidget from the StatefulWidget.
Background:
I have a star rating widget that consists of 5 "StarWidget"s in a row. The StarWidget class is just an Icon with a detector wrapped around it (not using IconButton because it has a very large size). The StarWidget stores whether it is selected or not in a corresponding State object and accordingly displays a solid or outline icon.
In my main widget, I have access to the StatefulWidget objects, and would like to configure their states.
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class StarRatingWidget extends StatefulWidget {
#override
_StarRatingWidgetState createState() {
return _StarRatingWidgetState();
}
}
class _StarRatingWidgetState extends State<StarRatingWidget>
implements StarSelectionInterface {
//Properties
int _currentRating = 0;
List<RatingStarWidget> starWidgets = [];
//Methods
#override
void initState() {
super.initState();
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 0,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 1,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 2,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 3,
),
);
starWidgets.add(
RatingStarWidget(
starSelectionInterface: this,
starPosition: 4,
),
);
}
#override
Widget build(BuildContext buildContext) {
return Row(
children: starWidgets,
);
}
//Star Selection Interface Methods
void onStarSelected(_RatingStarWidgetState starWidgetState) {
print("listener: star selected ${starWidgetState._starPosition}");
//a new, rating has been selected, update rating
if (_currentRating != starWidgetState._starPosition) {
_currentRating = (starWidgetState._starPosition + 1);
}
//same star as rating has been selected, set rating to 0
else {
_currentRating = 0;
}
//update stars according to rating
for(int i = 1; i <= 5; i++) {
//what should I do here?!
}
}
}
class RatingStarWidget extends StatefulWidget {
//Properties
final int starPosition;
final StarSelectionInterface starSelectionInterface;
//Constructors
RatingStarWidget({this.starSelectionInterface, this.starPosition});
//Methods
#override
_RatingStarWidgetState createState() {
return _RatingStarWidgetState(starSelectionInterface, starPosition);
}
}
class _RatingStarWidgetState extends State<RatingStarWidget> {
//Properties
int _starPosition;
bool _isSelected = false;
StarSelectionInterface selectionListener;
//Constructors
_RatingStarWidgetState(this.selectionListener, this._starPosition);
//Methods
#override
Widget build(BuildContext buildContext) {
return AnimatedCrossFade(
firstChild: GestureDetector(
child: Icon(
FontAwesomeIcons.star,
size: 14,
),
onTap: () {
print("star: selected");
selectionListener.onStarSelected(this);
},
),
secondChild: GestureDetector(
child: Icon(
FontAwesomeIcons.solidStar,
size: 14,
),
onTap: () {
selectionListener.onStarSelected(this);
},
),
duration: Duration(milliseconds: 300),
crossFadeState:
_isSelected ? CrossFadeState.showSecond : CrossFadeState.showFirst,
);
}
}
class StarSelectionInterface {
void onStarSelected(_RatingStarWidgetState starWidgetState) {}
}
The Flutter way is to rebuild widgets whenever it is necessary. Don't be afraid to build widgets, they are cheap for the SDK, specially in this case for simple stars.
Accessing another widget state requires more work than just rebuilding it. To access the state you should use keys or you should add special methods in the widget itself.
In this case, where the star is rebuilt no matter what, it is even better and simpler to use plain stateless widgets because the selected state can be provided by the parent in the moment of rebuilding.
And since the state is stored in the parent widget, I think it is better no to store it as wall in each one of the individual stars.
Next is a very simple solution that follows that idea. But yes, it still rebuilds the stars.
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body: Center(child: StarRatingWidget())),
);
}
}
class StarRatingWidget extends StatefulWidget {
#override
_StarRatingWidgetState createState() {
return _StarRatingWidgetState();
}
}
class _StarRatingWidgetState extends State<StarRatingWidget> {
int _currentRating = 0;
List<Widget> buildStars() {
List<RatingStarWidget> starWidgets = [];
for (int i = 0; i < 5; i++) {
starWidgets.add(
RatingStarWidget(
clickCallback: () => setState(() {
_currentRating = i + 1;
}),
highlighted: _currentRating > i,
),
);
}
return starWidgets;
}
#override
Widget build(BuildContext buildContext) {
return Row(
children: buildStars(),
);
}
}
class RatingStarWidget extends StatelessWidget {
//Properties
final VoidCallback clickCallback;
final bool highlighted;
//Constructors
RatingStarWidget({this.clickCallback, this.highlighted});
#override
StatelessElement createElement() {
print("Element created");
return super.createElement();
}
//Methods
#override
Widget build(BuildContext buildContext) {
return GestureDetector(
onTap: () {
clickCallback();
},
child: AnimatedCrossFade(
firstChild: Icon(
FontAwesomeIcons.star,
size: 14,
),
secondChild: Icon(
FontAwesomeIcons.solidStar,
size: 14,
),
duration: Duration(milliseconds: 300),
crossFadeState:
highlighted ? CrossFadeState.showSecond : CrossFadeState.showFirst,
),
);
}
}
I wrote my own example similar to yours. What I do here is:
Initial star rate is -1 because arrays start from 0 ;) and I create stars with position, current star rate and the callback function. We will use this callback function to update the value in the ScreenOne.
In Star widget, we have a local bool selected with default value false and we assign it a value inside the build function based on the position of the star and current rate. And we have setSelected() function which runs the callback function and updates currentRate with the value of star position.
Check the video example here.
class ScreenOne extends StatefulWidget {
#override
_ScreenOneState createState() => _ScreenOneState();
}
class _ScreenOneState extends State<ScreenOne> {
int currentRate = -1; //since array starts from 0, set non-selected as -1
List<Star> starList = []; //empty list
#override
void initState() {
super.initState();
buildStars(); //build starts here on initial load
}
Widget buildStars() {
starList = [];
for (var i = 0; i < 5; i++) {
starList.add(Star(
position: i,
current: currentRate,
updateParent: refresh, //this is callback
));
}
}
refresh(int index) {
setState(() {
currentRate = index; //update the currentRate
});
buildStars(); //build stars again
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Test page 1"),
),
body: Container(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: starList,
),
),
),
);
}
}
class Star extends StatefulWidget {
final Function(int index) updateParent; //callback
final int position; //position of star
final int current; //current selected star from parent
const Star({Key key, this.position, this.updateParent, this.current})
: super(key: key);
#override
_StarState createState() => _StarState();
}
class _StarState extends State<Star> {
bool selected = false;
void setSelected() {
widget.updateParent(widget.position);
}
#override
Widget build(BuildContext context) {
if (widget.current >= widget.position) {
selected = true;
} else {
selected = false;
}
return GestureDetector(
child: AnimatedCrossFade(
firstChild: Icon(Icons.star_border),
secondChild: Icon(Icons.star),
crossFadeState:
selected ? CrossFadeState.showSecond : CrossFadeState.showFirst,
duration: Duration(milliseconds: 300),
),
onTap: () {
setSelected();
},
);
}
}

Flutter: Scratch card

Here is the example of a scratch card in react-native, I want to implement in Flutter, But I am not getting any way to do this. I tried with blendMode but it is not working, and even there is no clear functionality are given in CustomPaint in the flutter. https://github.com/aleksik/react-scratchcard.
Future<Null> main() async {
runApp(MaterialApp(home: Scaffold(body: new Roller())));
}
class Roller extends StatefulWidget {
#override
_ScratchCardState createState() => new _ScratchCardState();
}
class _ScratchCardState extends State<Roller> {
ui.Image _image;
String _urlImage = 'assets/BedRoom.png';
List<Offset> _points = <Offset>[];
#override
void initState() {
// TODO: implement initState
super.initState();
super.initState();
load(_urlImage).then((j) {
_image = j;
print('image:${_image}');
});
}
Future<ui.Image> load(String asset) async {
ByteData data = await rootBundle.load(asset);
ui.Codec codec = await ui.instantiateImageCodec(
data.buffer.asUint8List(),
);
ui.FrameInfo fi = await codec.getNextFrame();
return fi.image;
}
void _onPanStart(DragStartDetails dt) {
print('drag start ');
setState(() {
});
}
Offset _localPosition;
void _onPanUpdate(DragUpdateDetails details) {
setState(() {
RenderBox object = context.findRenderObject();
_localPosition = object.globalToLocal(details.globalPosition);
_points = new List.from(_points)..add(_localPosition);
});
}
_onPanEnd(DragEndDetails dt) {
_points.add(null);
}
#override
void dispose() {
super.dispose();
}
Widget _scale(BuildContext context) {
return GestureDetector(
onPanStart: _onPanStart,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
onDoubleTap: () {
setState(() {
_points.clear();
});
},
child: Container(
child: CustomPaint(
painter: ScratchCard(
imagePath: _image, points: _points,),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _scale(context),
);
}
}
class ScratchCard extends CustomPainter {
final ui.Image imagePath;
List<Offset> points;
ScratchCard({Key key, this.imagePath, this.points}) : super();
Paint _paint = Paint();
Rect rect, inputSubrect, outputSubrect;
Path path = new Path();
Paint paint1 = new Paint();
#override
void paint(Canvas canvas, Size size) {
_paint.blendMode = BlendMode.src;
if (imagePath != null)
canvas.drawImage(imagePath, Offset(10.0, 100.0), _paint);
Paint paint = new Paint()
..color = Colors.white
..strokeCap = StrokeCap.round
..strokeWidth = 30.0;
_paint.blendMode = BlendMode.clear;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null) {
canvas.drawLine(points[i], points[i + 1], paint);
path.reset();
}
}
}
#override
bool shouldRepaint(ScratchCard oldDelegate) {
return true;
}
}
I've created a library for scratch cards in Flutter which supports both - color and image scratchable overlays. You can get it from https://pub.dev/packages/scratcher Feedback is more than welcome!
Example:
Scratcher(
brushSize: 30,
threshold: 50,
color: Colors.red,
onChange: (value) { print("Scratch progress: $value%"); },
onThreshold: () { print("Threshold reached, you won!"); },
child: Container(
height: 300,
width: 300,
child: Text("Hidden text"),
),
)

how to display animations one after the other in flutter

I have mapped an array to a class and I am displaying the animation accordingly but the animation works for the entire thing at once. I want to have a delay between the subsequent animations.
The screenshot of the game is here. The stars should appear with animation one after the other The code that i am using is :
for (var i = 0; i < 4; i++) {
myScore > (10*i) ? stars.add("true") : stars.add("false");
}
Widget _buildItem(int index, String text) {
return new MyButton(
key: new ValueKey<int>(index),
text: text,
keys: keys++,
onPress: () {
}
);
}
class MyButton extends StatefulWidget {
MyButton(
{Key key,
this.text,
this.keys,
this.onPress})
: super(key: key);
final String text;
final VoidCallback onPress;
int keys;
#override
_MyButtonState createState() => new _MyButtonState();
}
class _MyButtonState extends State<MyButton> with TickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
String _displayText;
initState() {
super.initState();
print("_MyButtonState.initState: ${widget.text}");
_displayText = widget.text;
controller = new AnimationController(
duration: new Duration(milliseconds: 600), vsync: this);
animation = new CurvedAnimation(parent: controller, curve: Curves.easeIn)
..addStatusListener((state) {
// print("$state:${animation.value}");
if (state == AnimationStatus.dismissed) {
print('dismissed');
if (widget.text != null) {
setState(() => _displayText = widget.text);
controller.forward();
}
}
});
}
#override
void didUpdateWidget(MyButton oldWidget) {
super.didUpdateWidget(oldWidget);
// sleep(const Duration(microseconds: 10)); //I tried adding a delay here but instead it delays the entire thing.
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Size media = MediaQuery.of(context).size;
double ht = media.height;
double wd = media.width;
widget.keys++;
print("_MyButtonState.build");
return new Shake(
animation: animation,
child: new GestureDetector(
child: new Container(
child: new FlatButton(
onPressed: () => widget.onPress(),
color: Colors.transparent,
child: new Icon(
_displayText == "true" ? Icons.star : Icons.star_border,
key: new Key("${widget.keys}"),
size: ht > wd ? ht * 0.05 : wd * 0.05,
color: Colors.black,
)
),)
) );
}
}
Use Staggered Animations in Flutter for series of operations , rather than all at once .Follow the link Staggered Animations to build Staggered Animations using Flutter .

Resources