LateInitializationError: Field 'controller' has not been initialized - flutter

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();
}

Related

flutter child props is changing parent variable

I send a variable from a parent widget to its child. by parameters. I'm using that variable on the parent and child widget. for example, I'm using variable.name on the parent widget. When I change the variable on the child widget widget.variable.name = 'all' it's also updating the parent widget but I just wanna change that on the child widget.
You can make a temporary variable inside child state.
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int parentVal = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
setState(() {
parentVal++;
});
},
child: Text("parent val: $parentVal"),
),
ChildW(
childval: parentVal,
)
],
));
}
}
class ChildW extends StatefulWidget {
final int childval;
const ChildW({
Key? key,
required this.childval,
}) : super(key: key);
#override
State<ChildW> createState() => _ChildWState();
}
class _ChildWState extends State<ChildW> {
late int tempChildVal;
#override
void initState() {
super.initState();
tempChildVal = widget.childval;
}
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
setState(() {
tempChildVal++;
});
},
child: Text("child val: $tempChildVal"),
);
}
}

Flutter: How to set state of parent widget

I'd like to change the color of a parent container with a button from a child widget.
Let's say I have a parentClass widget and a childClass widget. The container with dynamic color is in parentClass, and the button is in childClass. At first the container is blue and I want to make it red on button tap.
Color dynamicColor;
class ParentClass extends StatefulWidget {
ParentClass({Key key}) : super(key: key);
#override
_ParentClassState createState() => _ParentClassState();
}
class _ParentClassState extends State<ParentClass> {
#override
void initState() {
super.initState();
setState(() {
dynamicColor = Colors.blue;
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
ChildClass(),
Container(
color: dynamicColor,
child: ...
)
],
);
}
}
class ChildClass extends StatefulWidget {
ChildClass({Key key}) : super(key: key);
#override
_ChildClassState createState() => _ChildClassState();
}
class _ChildClassState extends State<ChildClass> {
#override
Widget build(BuildContext context) {
return Container(
child: TextButton(
onPressed: () {
setState(() {
dynamicColor = Colors.red; // What I want to do
});
},
child: Text('Change parent container color'),
),
);
}
}
You can create a function on parent widget and pass to child with parameter.
Like:
void delete() async {
setState(() {
dynamicColor = Colors.blue;
});
}
on your Widget Tree
CustomChild(function: () => delete()),
Your custom Widget
class CustomChild extends StatelessWidget {
Function function;
CustomChild({this.function})
#override
Widget build(BuildContext context) {
return Container();
}
}

How to call function in a StatefulWidget from a button somewhere within another widget?

How do I call the movePage(page) function in Widget1 from MaterialButton that placed deeply nested down below within the widget tree?
Please refer to example code below:
class Widget1 extends StatefulWidget {
#override
_Widget1State createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
int _selectedIndex = 0;
void movePage(page) {
setState(() {
_selectedIndex = page;
});
}
#override
Widget build(BuildContext context) {
return Container();
}
}
///Somewhere nested down below within another widget in the widget tree
class Widget12 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialButton(onPressed: () => {});
}
}
You could just pass it to the constructor. Try this on DartPad.
class Widget1 extends StatefulWidget {
#override
_Widget1State createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
int _selectedIndex = 0;
void movePage(int page) => setState(() => _selectedIndex += page);
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('$_selectedIndex'),
Widget2(func: movePage),
],
);
}
}
class Widget2 extends StatelessWidget {
final void Function(int) func;
const Widget2({Key key, #required this.func}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialButton(
/// Try with any number.
onPressed: () => func(2),
child: Text('button'),
);
}
}
I finally find it working using InheritedWidget.
Reference:
Call method of a widget from another widget
The codes are in his blog:
http://www.hellomonk.com/2018/03/communication-between-widgets-using.html
I will just leave it here for who might need it as well.

Font Awesome spinners icons not spinning in 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

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);
},
);
}
}