Font Awesome spinners icons not spinning in flutter - flutter

I am using spinner icon as below but i do't know why it is not spinning. It just work like a normal icon.
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
return new IconButton(
// Use the FontAwesomeIcons class for the IconData
icon: new Icon(FontAwesomeIcons.spinner),
onPressed: () { print("Pressed"); }
);
}
}

At this moment, this package only provides the Icons, but those Icons do not spin or animate by themselves. Use flutter's animation capabilities to spin it. Here is example widget from Brian Egan.
class Spinner extends StatefulWidget {
final IconData icon;
final Duration duration;
const Spinner({
Key key,
#required this.icon,
this.duration = const Duration(milliseconds: 1800),
}) : super(key: key);
#override
_SpinnerState createState() => _SpinnerState();
}
class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
AnimationController _controller;
Widget _child;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 2000),
)..repeat();
_child = Icon(widget.icon);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return RotationTransition(
turns: _controller,
child: _child,
);
}
}
Usage
Spinner(
icon: FontAwesomeIcons.spinner,
)
related issue on github

At this moment font_awesome_flutter package only provides the Icons, and those Icons do not spin or animate by themselves.
That's why i used a material design circular progress indicator which shows progress along a circle
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
return new CircularProgressIndicator();
}
}
https://docs.flutter.io/flutter/material/CircularProgressIndicator/CircularProgressIndicator.html

Related

LateInitializationError: Field 'controller' has not been initialized

Im using a SideBar() widget inside the SideBarWidget() and using it in my main screen SideBarScreen()
but i cant initialize the controller in the Sidebar() widget... How can i fix this
sidebar_Screen
class _SideBarScreenState extends State<SideBarScreen> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Stack(
children: [
ScaffoldScreen(
iconButton: IconButton(
onPressed: () {SideBarWidget().toggle();},
icon: kScaffoldScreenButtonIcon,
),
),
SideBarWidget(),
],
),
);
}
}
sideBarWidget
class SideBarWidget extends StatelessWidget {
SideBarWidget({Key? key}) : super(key: key);
void toggle() {
SideBarState().toggle();
}
late final SideBar sideBarWidget = SideBar();
#override
Widget build(BuildContext context) {
return sideBarWidget;
}
}
SideBar
class SideBar extends StatefulWidget {}
class SideBarState extends State<SideBar> with SingleTickerProviderStateMixin{
late AnimationController controller;
late Animation<Offset> _offsetAnimation;
#override
void initState() {
super.initState();
controller = AnimationController(vsync: this, duration: const Duration(seconds: 2));
_offsetAnimation = Tween<Offset>(
begin: const Offset(-1.0, 0.0),
end: const Offset(0.0, 0.0),
).animate(CurvedAnimation(parent: controller, curve: Curves.easeOut));
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void toggle() {
if (controller.isCompleted) {
controller.reverse();
}
else {controller.forward();}
}
#override
Widget build(BuildContext context) {
return SlideTransition();
}
}
This gives the error LateInitializationError: Field 'controller' has not been initialized.
I tried passing the controller as a parameter to the SideBar() but it gives off an exception:
This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
The problem is that you're creating a new instance of SideBar every time you call SideBarWidget().toggle(). Instead, you should be using the same instance of SideBar so that the state is retained.
class SideBarWidget extends StatelessWidget {
SideBarWidget({Key? key}) : super(key: key);
final sideBar = SideBar();
void toggle() {
sideBar.state.toggle();
}
#override
Widget build(BuildContext context) {
return sideBar;
}
}
So now you are using the same instance of SideBar every time and you don't need to pass any parameters.
Note:
To use like this, you also need to change the SideBar class to a StatefulWidget
class SideBar extends StatefulWidget {
#override
_SideBarState createState() => _SideBarState();
}

Custom Widget revising animation in gridview

I have a custom widget that changes color when tapped inside a gridview. When I scroll to the bottom and scroll back up to the top selected widget its animation is reversed.
I'm pretty sure that it has something to do with the widget being disposed of when out of view but I don't have a solution to overcome it. See my code below:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Thirty Seconds',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
// Page with the gridview
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
crossAxisCount: 2,
children: List.generate(20, (index) {
return MyCustomWidget(
key: GlobalKey(),
index: index + 1,
);
}),
),
);
}
}
// Custom Widget
class MyCustomWidget extends StatefulWidget {
const MyCustomWidget({
super.key,
required this.index,
});
final int index;
#override
State<MyCustomWidget> createState() => _MyCustomWidgetState();
}
class _MyCustomWidgetState extends State<MyCustomWidget>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<Color?> _colorAnimation;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_colorAnimation = ColorTween(begin: Colors.white, end: Colors.yellow)
.animate(_animationController)
..addListener(() => setState(() {}));
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _toggleAnimation() {
if (_animationController.isCompleted) {
_animationController.reverse();
} else {
_animationController.forward();
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
_toggleAnimation();
},
child: Container(
color: _colorAnimation.value,
child: Center(
child: Text("Custom Widget ${widget.index}"),
),
),
);
}
}
GridView dispose the widget that aren't visible on UI. You can use cacheExtent(not suitable for this case) or AutomaticKeepAliveClientMixin on _MyCustomWidgetState.
class _MyCustomWidgetState extends State<MyCustomWidget>
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
You may prefer handing it parent widget and passing a bool to check active state or state-management or project level depends on scenario.

Pass animation controller declared in the home page of the app to a button present in navigation drawer

This is the home page code:-
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
TabController _tabController;
GlobalKey<ScaffoldState> drawerKey = GlobalKey<ScaffoldState>();
AnimationController animationController;
Animation<double> animation;
bool cirAn = false;
#override
void initState() {
animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
reverseDuration: Duration(milliseconds: 300),
);
animation = CurvedAnimation(
parent: animationController,
curve: Curves.easeIn,
reverseCurve: Curves.easeOut
);
//animationController.forward();
_tabController = TabController(
initialIndex: 0,
length: 2,
vsync: this,
);
super.initState();
}
#override
void dispose() {
_tabController.dispose();
//animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final themeProvider = Provider.of<DarkThemeProvider>(context);
var size = MediaQuery.of(context).size;
return cirAn
? CircularRevealAnimation(
centerOffset: Offset(size.height / 15, size.width / 3.5),
animation: animation,
child: myHomeWidget(
themeProvider,
),
)
: myHomeWidget(themeProvider);
}
i want to use the circular reveal animation for the whole app when a button in navigation drawer is clicked. In the home page there is a class called MyDrawer inside which another class named ThemeButton is there. Now when i tap on the theme button the circular reveal should happen for the whole app.
class ThemeButton
class ThemeButton extends StatefulWidget {
#override
_ThemeButtonState createState() => _ThemeButtonState();
}
class _ThemeButtonState extends State<ThemeButton>
with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
final themeChange = Provider.of<DarkThemeProvider>(context);
return IconButton(
icon: Icon(
themeChange.darkTheme ? Icons.wb_sunny : Icons.brightness_3,
size: 25,
color: Colors.white,
),
onPressed: () {
themeChange.darkTheme = !themeChange.darkTheme;
},
);
}
}

Trigger animations on widget load flutter

So, I have a AnimatedPositioned widget in my widget tree, which contains a form. I want the AnimatedPositioned widget to slide up from bottom when user navigates to the screen. Now, there are many tutorials which show how to do this when user clicks a button using the setState method. But how do I trigger this automatically when this screen is loaded?
The "right" way to do that would be not to use an implicit animation widget like AnimatedPositioned and instead use an explicit animation. Where you start your AnimationController in the initState of a StatefulWidget.
But just technically you could set the "destination" values for your AnimatedPositioned widget in a Future so that the value changes on the next frame. You try remoing the Future to see that otherwise the widget renders at its end position.
This way is not recommended but possible:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double _dist = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
AnimatedPositioned(
child: Text('Hello Words'),
duration: Duration(seconds: 5),
left: _dist,
top: _dist,
),
]),
);
}
#override
initState() {
super.initState();
Future(() {
setState(() {
_dist = 250.0;
});
});
}
}

Controlling animation from other widget

I'm trying to control an animation on a widget (widget 2 in the pic) using gestures captured from another widget (widget 1 in the pic) and I'm having trouble finding the right way to do that.
The approach I currently think about is having the AnimationController in a common parent. widget 1 would, via callbacks, update the value of this controller in the parent. widget 2 would receive that controller a a parameter and therefore the animation in widget 2 would correspond to the values provided by the gestures in widget 1
Is that the right way to do it ? If not, what would be a better approach ?
Edit: After trying this method, I notice that an exception get thrown
Another exception was thrown: AnimationController.dispose() called more than once.
I suspect the AnimationController object to be reconstructed on rebuild causing the error. Any idea on how to do it to avoid that ?
Thanks ! :)
Here is a full sample of what I think you're trying to achieve, using AnimatedWidget. Might not be the most efficient way to do it, but it works.
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Test")),
body: SafeArea(child: Parent()),
),
);
}
}
class Parent extends StatefulWidget {
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _animation;
#override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this);
_animation =
Tween<double>(begin: 0, end: 100).animate(_animationController);
}
#override
void dispose() {
super.dispose();
_animationController.dispose();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(child: Widget1(animation: _animation)),
Widget2(onSliderChanged: (value) {
_animationController.value = value / 100;
}),
],
);
}
}
class Widget1 extends AnimatedWidget {
Widget1({Key key, #required Animation<double> animation})
: super(key: key, listenable: animation);
#override
Widget build(BuildContext context) {
Animation<double> animation = listenable;
return Opacity(
opacity: animation.value / 100,
child: Center(
child: Text(
"Hi",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 40.0),
),
),
);
}
}
class Widget2 extends StatefulWidget {
final ValueChanged<double> onSliderChanged;
const Widget2({Key key, this.onSliderChanged}) : super(key: key);
#override
State<StatefulWidget> createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
double _value = 0;
#override
Widget build(BuildContext context) {
return Slider(
value: _value,
min: 0,
max: 100,
onChanged: (value) {
setState(() {
_value = value;
});
widget.onSliderChanged(value);
},
);
}
}