How to make a linear progress indicator with swing effect? - flutter

I want to make a progress bar that looks like this. If you look closely, the length of the indicator varies when it swings.
I tried to reuse the linear progress bar that flutter provides.
import 'package:flutter/material.dart';
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
late AnimationController controller;
#override
void initState() {
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 5),
)..addListener(() {
setState(() {});
});
controller.repeat(reverse: true);
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
LinearProgressIndicator(
value: controller.value,
),
],
),
),
);
}
}
And it looks like this.

You can follow this snippet. Run on dartPad
/// create ---- ProgressIndicator
///
/// ````
/// child: SizedBox(
/// height: 12,
/// child: CustomLinearProgressIndicator(
/// backgroundColor: Colors.white,
/// color: Colors.blue,
/// maxProgressWidth: 100,
/// ),
/// ),
/// ````
class CustomLinearProgressIndicator extends StatefulWidget {
const CustomLinearProgressIndicator({
Key? key,
this.color = Colors.blue,
this.backgroundColor = Colors.white,
this.maxProgressWidth = 100,
}) : super(key: key);
/// max width in center progress
final double maxProgressWidth;
final Color color;
final Color backgroundColor;
#override
State<CustomLinearProgressIndicator> createState() =>
_CustomLinearProgressIndicatorState();
}
class _CustomLinearProgressIndicatorState
extends State<CustomLinearProgressIndicator>
with SingleTickerProviderStateMixin {
late AnimationController controller =
AnimationController(vsync: this, duration: const Duration(seconds: 1))
..addListener(() {
setState(() {});
})
..repeat(reverse: true);
late Animation animation =
Tween<double>(begin: -1, end: 1).animate(controller);
#override
Widget build(BuildContext context) {
return ColoredBox(
color: widget.backgroundColor,
child: Align(
alignment: Alignment(animation.value, 0),
child: Container(
decoration: ShapeDecoration(
// play with BoxDecoration if you feel it is needed
color: widget.color,
shape: const StadiumBorder(),
),
// you can use animatedContainer, seems not needed
width: widget.maxProgressWidth -
widget.maxProgressWidth * (animation.value as double).abs(),
height: double.infinity,
),
),
);
}
}

Related

How to center the anchor point of a shape in Flutter?

I have a continer whose length and width are 0, but with the animation, I change its length and width to 200. The problem is that when it starts to grow, it grows from the bottom and does not grow from the middle. I want to know how to place its anchor point in the middle. Working with after effects, they will understand by seeing this image
For example, in the code below, the circle starts growing from the bottom, while I want it to start growing from the middle point of the circle:
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin {
late Animation<double> width;
late AnimationController controller;
#override
void initState() {
super.initState();
controller =
AnimationController(duration: Duration(seconds: 2), vsync: this);
width = Tween<double>(begin: 0, end: 200).animate(controller);
width.addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(50),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('a')),
Container(
width: width.value,
height: width.value,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.black),
),
],
),
),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin {
late Animation<double> width;
late AnimationController controller;
Tween<double> animation = Tween<double>(begin: 0, end: 200);
#override
void initState() {
super.initState();
controller =
AnimationController(duration: Duration(seconds: 2), vsync: this);
width = animation.animate(controller);
width.addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(50),
child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('a'),
),
SizedBox(height: animation.end!-width.value/2),
Container(
width: width.value,
height: width.value,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.black),
),
],
),
),
);
}
}
I believe you have a positioning issue. I moved the circle a little bit and now it seems that it starts to grow from the center. Please let me know if this code fixes your issue:
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
late Animation<double> width;
late AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(duration: Duration(seconds: 2), vsync: this);
width = Tween<double>(begin: 0, end: 200).animate(controller);
width.addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('a')),
AnimatedContainer(
width: width.value,
height: width.value,
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.black),
duration: controller.duration!,
),
],
),
),
);
}
}

How to create the following circular animation in flutter

Dark portion is continuously circulating.
Size of dark portion is proportional to amount of data loaded.
Example:- When 40% data loaded then the dark circulating part is 40% of circumference. When 60% data loaded then the dark circulating part is 60% of circumference. So on.
https://youtu.be/a4czRLK6ouE
Here is my attempt to create this animation:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late final AnimationController controller;
double percent = 0.0;
// ** Addition in #mmcdon20 code - Start
changePercent() {
// 20 frame is enough to beat eye, that's why I used
// 50 refresh/second to keep animation smooth
Future.delayed(
const Duration(milliseconds: 20), // Adjust accordingly.
() {
setState(() {
percent += 0.005; // Adjust accordingly.
});
print('........................');
if (percent < 1) {
changePercent();
}
},
);
}
// ** Addition in #mmcdon20 code - End
#override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)
..animateTo(1)
..repeat();
// ** Addition in #mmcdon20 code - Start
changePercent();
// ** Addition in #mmcdon20 code - End
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomProgressIndicator(
value: percent,
color: Colors.orange,
controller: controller,
width: 200,
height: 200,
strokeWidth: 10,
// curve: Curves.slowMiddle, // ** Eliminationated from #mmcdon20 code
),
// ** Eliminationated from #mmcdon20 code - Start
// Slider(
// value: percent,
// onChanged: (v) {
// setState(() {
// percent = v;
// });
// },
// ),
// ** Eliminationated from #mmcdon20 code - End
],
),
);
}
}
class CustomProgressIndicator extends StatelessWidget {
const CustomProgressIndicator({
Key? key,
required this.color,
required this.value,
required this.controller,
required this.width,
required this.height,
required this.strokeWidth,
this.curve = Curves.linear,
}) : super(key: key);
final Color color;
final double value;
final AnimationController controller;
final double width;
final double height;
final double strokeWidth;
final Curve curve;
#override
Widget build(BuildContext context) {
return RotationTransition(
turns: CurvedAnimation(
parent: controller,
curve: curve,
),
child: SizedBox(
width: width,
height: height,
child: CircularProgressIndicator(
strokeWidth: strokeWidth,
color: color,
backgroundColor: color.withOpacity(.2),
value: value,
),
),
);
}
}
1st Add this:
dependencies:
percent_indicator: ^3.4.0
Then Example:
import 'package:flutter/material.dart';
import 'package:percent_indicator_example/sample_circular_page.dart';
import 'package:percent_indicator_example/sample_linear_page.dart';
void main() {
runApp(MaterialApp(home: Scaffold(body: SamplePage())));
}
class SamplePage extends StatefulWidget {
#override
_SamplePageState createState() => _SamplePageState();
}
class _SamplePageState extends State<SamplePage> {
void _openPage(Widget page) {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => page,
),
);
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MaterialButton(
color: Colors.blueAccent,
child: Text("Circular Library"),
onPressed: () => _openPage(SampleCircularPage()),
),
Padding(
padding: EdgeInsets.all(20.0),
),
MaterialButton(
color: Colors.blueAccent,
child: Text("Linear Library"),
onPressed: () => _openPage(SampleLinearPage()),
),
],
),
),
);
}
}

Flutter Reworked question: Issue sharing states between widget with button and widget with countdown timer

I am trying since some days to connect an animated stateful child widget with a countdown timer to the parent stateful widget with the user interaction. I found this answer from Andrey on a similar question (using Tween which I do not) that already helped a lot, but I still don't get it to work. My assumption is, the child's initState could be the reason. The timer's code comes from here.
I have removed quite some code including some referenced functions/classes. This should provide a clearer picture on the logic:
In MainPageState I declare and init the _controller of the animation
In MainPageState I call the stateless widget CreateKeypad hosting among others the "go" key
When go is clicked, this event is returned to MainPageState and _controller.reverse(from: 1.0); executed
In MainPageState I call the stateful widget CountDownTimer to render the timer
In _CountDownTimerState I am not sure if my initState is correct
In _CountDownTimerState I build the animation with CustomTimerPainter from the timer code source
The animation shall render a white donut and a red, diminishing arc on top. However, I only see the white donut, not the red timer's arc. Any hint is highly appreciated.
class MainPage extends StatefulWidget {
MainPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> with TickerProviderStateMixin {
AnimationController _controller;
var answer="0", correctAnswer = true, result = 0;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(seconds: 7));
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
),
child: SafeArea(
child: Container(
child: Column(
children: <Widget>[
CreateKeypad( // creates a keypad with a go button. when go is clicked, countdown shall start
prevInput: int.parse((answer != null ? answer : "0")),
updtedInput: (int val) {
setState(() => answer = val.toString());
},
goSelected: () {
setState(() {
if (answer == result.toString()) {
correctAnswer = true;
}
final problem = createProblem();
result = problem.result;
});
_controller.reverse(from: 1.0); // start the countdown animation
Future.delayed(const Duration(milliseconds: 300,),
() => setState(() => correctAnswer = true));
},
),
CountDownTimer(_controller), // show countdown timer
]
),
),
)
);
}
}
// CREATE KEYPAD - all keys but "1! and "go" removed
class CreateKeypad extends StatelessWidget {
final int prevInput;
final VoidCallback goSelected;
final Function(int) updtedInput;
CreateKeypad({#required this.prevInput, #required this.updtedInput, this.goSelected});
#override
Widget build(BuildContext context) {
return Row(
children: <Widget> [
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(2.0),
child: SizedBox(
width: 80.0, height: 80.0,
child: CupertinoButton(
child: Text("1", style: TextStyle(color: CupertinoColors.black)),
onPressed: () {
updtedInput(1);
},
),
),
),
Padding(
padding: const EdgeInsets.all(2.0),
child: SizedBox(
width: 80.0, height: 80.0,
child: CupertinoButton(
child: Text("Go!", style: TextStyle(color: CupertinoColors.black)),
onPressed: () => goSelected(),
),
),
),
],
),
]
);
}
}
// CREATE COUNTDOWN https://medium.com/flutterdevs/creating-a-countdown-timer-using-animation-in-flutter-2d56d4f3f5f1
class CountDownTimer extends StatefulWidget {
CountDownTimer(this._controller);
final AnimationController _controller;
#override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer> with TickerProviderStateMixin {
#override
void initState() {
super.initState(); // here I have some difference to Andrey's answer because I do not use Tween
}
String get timerString {
Duration duration = widget._controller.duration * widget._controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60)
.toString()
.padLeft(2, '0')}';
}
#override
Widget build(BuildContext context) {
return Container(
child: AnimatedBuilder(
animation: widget._controller,
builder:
(BuildContext context, Widget child) {
return CustomPaint(
painter: CustomTimerPainter( // this draws a white donut and a red diminishing arc on top
animation: widget._controller,
backgroundColor: Colors.white,
color: Colors.red,
));
},
),
);
}
}
You can copy paste run full code below
Step 1: You can put controller inside CountDownTimerState
Step 2: Use GlobalKey
CountDownTimer(key: _key)
Step 3: Call function start() inside _CountDownTimerState with _key.currentState
goSelected: () {
setState(() {
...
_controller.reverse(from: 10.0); // start the countdown animation
final _CountDownTimerState _state = _key.currentState;
_state.start();
...
class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 7));
super
.initState(); // here I have some difference to Andrey's answer because I do not use Tween
}
...
void start() {
setState(() {
_controller.reverse(from: 1.0);
});
}
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
class MainPage extends StatefulWidget {
MainPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> with TickerProviderStateMixin {
AnimationController _controller;
var answer = "0", correctAnswer = true, result = 0;
GlobalKey _key = GlobalKey();
#override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 7));
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
//navigationBar: CupertinoNavigationBar(),
child: SafeArea(
child: Container(
color: Colors.blue,
child: Column(children: <Widget>[
CreateKeypad(
// creates a keypad with a go button. when go is clicked, countdown shall start
prevInput: int.parse((answer != null ? answer : "0")),
updtedInput: (int val) {
setState(() => answer = val.toString());
},
goSelected: () {
setState(() {
if (answer == result.toString()) {
correctAnswer = true;
}
/*final problem = createProblem();
result = problem.result;*/
});
print("go");
_controller.reverse(from: 10.0); // start the countdown animation
final _CountDownTimerState _state = _key.currentState;
_state.start();
/* Future.delayed(
const Duration(
milliseconds: 300,
),
() => setState(() => correctAnswer = true));*/
},
),
Container(
height: 400,
width: 400,
child: CountDownTimer(key: _key)), // show countdown timer
]),
),
));
}
}
// CREATE KEYPAD - all keys but "1! and "go" removed
class CreateKeypad extends StatelessWidget {
final int prevInput;
final VoidCallback goSelected;
final Function(int) updtedInput;
CreateKeypad(
{#required this.prevInput, #required this.updtedInput, this.goSelected});
#override
Widget build(BuildContext context) {
return Row(children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(2.0),
child: SizedBox(
width: 80.0,
height: 80.0,
child: CupertinoButton(
child:
Text("1", style: TextStyle(color: CupertinoColors.black)),
onPressed: () {
updtedInput(1);
},
),
),
),
Padding(
padding: const EdgeInsets.all(2.0),
child: SizedBox(
width: 80.0,
height: 80.0,
child: CupertinoButton(
child:
Text("Go!", style: TextStyle(color: CupertinoColors.black)),
onPressed: () => goSelected(),
),
),
),
],
),
]);
}
}
// CREATE COUNTDOWN https://medium.com/flutterdevs/creating-a-countdown-timer-using-animation-in-flutter-2d56d4f3f5f1
class CountDownTimer extends StatefulWidget {
CountDownTimer({Key key}) : super(key: key);
//final AnimationController _controller;
#override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 7));
super
.initState(); // here I have some difference to Andrey's answer because I do not use Tween
}
String get timerString {
Duration duration = _controller.duration * _controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
}
void start() {
setState(() {
_controller.reverse(from: 1.0);
});
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return CustomPaint(
painter: CustomTimerPainter(
// this draws a white donut and a red diminishing arc on top
animation: _controller,
backgroundColor: Colors.green,
color: Colors.red,
));
},
),
);
}
}
class CustomTimerPainter extends CustomPainter {
CustomTimerPainter({
this.animation,
this.backgroundColor,
this.color,
}) : super(repaint: animation);
final Animation<double> animation;
final Color backgroundColor, color;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = backgroundColor
..strokeWidth = 10.0
..strokeCap = StrokeCap.butt
..style = PaintingStyle.stroke;
canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);
paint.color = color;
double progress = (1.0 - animation.value) * 2 * math.pi;
//print("progress ${progress}");
canvas.drawArc(Offset.zero & size, math.pi * 1.5, -progress, false, paint);
}
#override
bool shouldRepaint(CustomTimerPainter old) {
//print(animation.value);
return true;
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MainPage(),
);
}
}

Rotating color in containers animation in flutter

I have extracted the code below on my original code which is a bit long.
import 'package:flutter/material.dart';
import 'cubeRotate.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Rotating boxes',
home: MyHomePage(title: 'Rotating boxes'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> animals = [
'dog',
'cat',
'mouse',
'snake',
'rabbit',
'pig'
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
width: 500.0,
height: 500.00,
child: Wrap(
children: [
CubeRotate(name:animals[0], start: 0, end: 0.2),
CubeRotate(name:animals[1], start:0.2, end:0.4),
CubeRotate(name:animals[2], start:0.4, end:0.6),
CubeRotate(name:animals[3], start:0.6, end:0.8),
CubeRotate(name:animals[4], start:0.8, end:0.9),
CubeRotate(name:animals[5], start:0.9, end:1.0),
],
),
),
);
}
}
and another dart file
import 'package:flutter/material.dart';
class CubeRotate extends StatefulWidget {
#override
CubeRotate({Key key, #required this.name,
#required this.start,
#required this.end}): super(key: key);
String name;
double start;
double end;
_CubeRotateState createState() => _CubeRotateState();
}
class _CubeRotateState extends State<CubeRotate> with TickerProviderStateMixin{
AnimationController controller;
Animation animation;
#override
initState() {
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 10),
);
Tween _colorTween1 = new ColorTween(
begin: Colors.white,
end: Colors.blue,
);
Tween _colorTween2 = new ColorTween(
begin: Colors.blue,
end: Colors.white,
);
animation = TweenSequence(
<TweenSequenceItem> [
TweenSequenceItem(
tween: _colorTween1.chain(CurveTween(curve: Interval(widget.start, widget.end))),
weight: 20,
),
TweenSequenceItem(
tween: _colorTween2.chain(CurveTween(curve: Interval(widget.start, widget.end))),
weight: 20,
),
]
).animate(controller);
controller.forward();
controller.addListener(() {
setState(() {});
});
controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
print(widget.name + ' controller end.');
controller.reverse();
controller.reset();
controller.forward();
}
});
super.initState();
}
#override
dispose() {
controller.dispose();
super.dispose();
}
double getSize() {
if (MediaQuery.of(context).orientation == Orientation.portrait) {
return MediaQuery.of(context).size.width - 120;
} else {
return MediaQuery.of(context).size.height - 120;
}
}
#override
Widget build(BuildContext context) {
double _width = getSize() / 2;
double _height = getSize() / 3;
return Container(
width: _width,
height: _height,
child: Card(
color: animation.value,
child: Center(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text(
widget.name,
style: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
),
),
);
}
}
that gives me this animation
The animation I would like to happen is a rotation colour to each containers like dog is blue and others are white, cat is blue and the rest are white and so on. I did a lot of revisions on my code like sequence tween and reverse and reset but still won't do what I would like to happen. I am sure this is just a short solution for most of you. I would like to know what am I doing wrong here? Thank you.

How to have a custom animation as a Flutter progress / loading indicator?

I am trying to replace the default CircularProgressIndicator with my own animation. I created a spinning widget based on the example here How to rotate an image using Flutter AnimationController and Transform? , but when replacing CircularProgressIndicator with "MyIconSpinner", for some reason it is not appearing. Any tips please?
Here are the contents of MyIconSpinner
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';
class MyIconSpinner extends StatefulWidget {
MyIconSpinner({Key key, this.title}) : super(key: key);
final String title;
#override
_MyIconSpinnerState createState() => _MyIconSpinnerState();
}
class _MyIconSpinnerState extends State<MyIconSpinner>
with TickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(
duration: const Duration(milliseconds: 5000),
vsync: this,
);
super.initState();
}
#override
Widget build(BuildContext context) {
_controller.forward();
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RotationTransition(
turns: Tween(begin: 0.0, end: 1.0).animate(_controller),
child: Icon(
Icons.star,
size: 40,
),
),
],
),
),
);
}
}
I am placing it in a widget like this
return Scaffold(
appBar: AppBar(
title: Text("Appbar"),
backgroundColor: Colors.black,
automaticallyImplyLeading: false,
),
body: Center(
child: Column(children: <Widget>[
StreamBuilder(
stream: doSomething(withSomeData),
builder: (BuildContext context,
AsyncSnapshot<List<DocumentSnapshot>> asyncSnapshot) {
if (!asyncSnapshot.hasData) return MyIconSpinner();
I think you shouldn't wrap MyIconSpinner in Scaffold. You should give MyIconSpinner color parameter and also repeat animation after it is completed. Here is the edited version of MyIconSpinner.
class MyIconSpinner extends StatefulWidget {
MyIconSpinner({Key key, this.title, this.color = Colors.blue}) : super(key: key);
final String title;
final Color color;
#override
_MyIconSpinnerState createState() => _MyIconSpinnerState();
}
class _MyIconSpinnerState extends State<MyIconSpinner>
with TickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(
duration: const Duration(milliseconds: 5000),
vsync: this,
);
_controller.addListener((){
if(_controller.isCompleted){
_controller.repeat();
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
_controller.forward();
return RotationTransition(
turns: Tween(begin: 0.0, end: 1.0).animate(_controller),
child: Icon(
Icons.star,
size: 40,
color: widget.color,
),
);
}
}