I need to expose a couple of functions of a Stateful Widget. Since these functions depend on the state of the widget, I created a variable to store the state.
However, I am getting a compile time warning:
This class (or a class that this class inherits from) is marked as '#immutable', but one or more of its instance fields aren't final.
My Code:
class ItemWidget extends StatefulWidget {
final Record record;
final Function additem;
final Function removeItem;
var state;
ItemWidget(this.record, this.additem, this.removeItem);
#override
_ItemWidgetState createState() {
return this.state = new _ItemWidgetState();
}
// These are public functions which I need to expose.
bool isValid() => state.validate();
void validate() => state.validate();
}
Is there a better way /correct way of achieving this?
Thanks.
You should write the function on state, and access it via GlobalKey.
import 'package:flutter/material.dart';
class ItemWidget extends StatefulWidget {
final Record record;
final Function additem;
final Function removeItem;
const ItemWidget(
Key? key,
this.record,
this.additem,
this.removeItem,
) : super(key: key);
#override
ItemWidgetState createState() => ItemWidgetState();
}
class ItemWidgetState extends State<ItemWidget> {
bool isValid() {
return true;
}
void validate() {}
#override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
}
https://api.flutter.dev/flutter/widgets/GlobalKey-class.html
Related
Currently, when I would like initial values of a stateful widget to be configurable, I follow a pattern that looks like
class MyWidget extends StatefulWidget {
final String? initialValue;
MyWidget({ this.initialValue });
#override State createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
String statefulValue = "default initial value";
#override
void initState() {
super.initState();
if (widget.initialValue != null) { statefulValue = widget.initialValue; }
}
// ...
}
This works, but seems a bit heavyweight to me to achieve something I have to think is a very common use case. First, it doesn't make sense to me that initialValue should have to be a field at all, since its use is only to initialize the state, and then is no longer needed. Second, I think it would avoid some boiler plate if the state class could have a constructor that the stateful widget could call, so the above could look like:
class MyWidget extends StatefulWidget {
final String? initialValue;
MyWidget({ this.initialValue });
#override State createState() => MyWidgetState(initialValue: initialValue);
}
class MyWidgetState extends State<MyWidget> {
String statefulValue;
MyWidgetState({ String? initialValue }) : statefulValue = initialValue ?? "default initial value";
// ...
}
That doesn't exactly solve the first problem, but I think reads more easily. This however triggers the "Don't put any logic in createState" linter error. So my questions are
a) is there a pattern where the initial value doesn't have to be held on to longer than necessary?
b) why is passing parameters to the State constructor frowned upon?
You can provide default value on constructor
class MyWidget extends StatefulWidget {
final String initialValue;
const MyWidget({this.initialValue = "default initial value"});
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late String statefulValue = widget.initialValue;
So I have 2 classes:
class A extends StatefulWidget {
A({Key? key}) : super(key: key);
#override
State<A> createState() => _AState();
}
class _AState extends State<A> {
#override
Widget build(BuildContext context) {
return Container();
}
void func(){}
}
And I want to override the _AState classes func() method. So I do this like this:
class B extends A{
final item = 10;
#override
State<A> createState() => _BState();
}
class _BState extends _AState{
#override
void func() {
widget.item //can't do this
}
}
I have no problem overriding the func() method, but now I also need to access my new variable item, that is declared in B class. And I know I can't do that because instance widget is provided by State<A> class.
So my question is: How to access the variable item from B class in _BState?
Cast the widget to B object
class _BState extends _AState{
#override
void func() {
// (widget as B).item
}
}
I have this model class which has a refresh method,
class Model{
...
void refresh(){}
}
I'm calling this method from Parent widget like this,
class Parent extends StatefulWidget {
#override
_Parent State createState() => _Parent State();
}
class _Parent State extends State<Parent >
with SingleTickerProviderStateMixin {
final Model model = Model();
#override
Widget build(BuildContext context) {
return Column(
children:[
Child(model: model),
IconButton(onPressed:model.refresh)
]
);
}
}
this is child widget,
class Child extends StatefulWidget {
final Model model;
const Child({
Key? key,
required this.model,
}) : super(key: key);
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child>
with SingleTickerProviderStateMixin {}
so what I want is to override that method in here and call that whenever it's called from parent.
In short what I need is a callback from parent to child which need to be inside the Model class. So How can I do this?
ok so I knew I can do it with state management but using another library was not a option and I totally forgot about the change notifier so I implemented like this,
class Model extends ChangeNotifier{
void refresh(){
notifyListeners();
}
}
class Child extends StatefulWidget{
final Model model
Child(this.model);
...
#override
initState(){
model.addListener((){});
}
}
class Parent extends StatelessWidget{
final Model model = Model();
...
Child(model),
SomeWidget(
onTap:(){
model.refresh();
}
),
}
I have a simple code snippet:
class CreateNewViewPage extends StatefulWidget {
final ComponentConfiguration currentComponentConfiguration = ComponentConfiguration(1, 1);
final Components components = Components(currentComponentConfiguration);
#override
State<StatefulWidget> createState() => _CreateNewViewPage();
}
Unfortunately, when I want to send currentComponentConfiguration to the Components class construct, I get the following information: The instance member 'currentComponentConfiguration' can't be accessed in an initializer.
How can I fix this problem?
You can't use a non-static variable for initialzing another non-static variable. Either make currentComponentConfiguration a static member or do components object creation in State class (_CreateNewViewPageState).
Using Static
class CreateNewViewPage extends StatefulWidget {
static final ComponentConfiguration currentComponentConfiguration = ComponentConfiguration(1, 1);
final Components components = Components(currentComponentConfiguration);
#override
_CreateNewViewPageState createState() => _CreateNewViewPageState();
}
Using State class
class CreateNewViewPage extends StatefulWidget {
final ComponentConfiguration currentComponentConfiguration = ComponentConfiguration(1, 1);
#override
_CreateNewViewPageState createState() => _CreateNewViewPageState();
}
class _CreateNewViewPageState extends State<CreateNewViewPage> {
Components components;
#override
void initState() {
super.initState();
components = Components(widget.currentComponentConfiguration);
}
#override
Widget build(BuildContext context) {
return Text('Hello, World!', style: Theme.of(context).textTheme.headline4);
}
}
I got a Visual Studio warning on my class (below) saying "This class (or a class which this class inherits from) is marked as '#immutable', but one or more of its instance fields are not final: UserSignIn._email", but I cannot mark this argument as final because I initialise it in the constructor
Without final :
class UserSignIn extends StatefulWidget {
TextEditingController _email;
UserSignIn({String emailInput}) {
this._email = TextEditingController(text: (emailInput ?? ""));
}
#override
_UserSignInState createState() => _UserSignInState();
}
class _UserSignInState extends State<UserSignIn> {
#override
Widget build(BuildContext context) {
...
}
}
How to do this ?
Thank you
You should put the TextEditingController in the state class and initialise it in the initState method, like this.
And keep in mind that the StatefulWidget can be different every time when the widget tree is changed, so don't put anything in there that is not immutable.
Keep everything dynamic in the State class
class UserSignIn extends StatefulWidget {
final String emailInput;
const UserSignIn({Key key, this.emailInput}) : super(key: key);
#override
_UserSignInState createState() => _UserSignInState();
}
class _UserSignInState extends State<UserSignIn> {
TextEditingController _controller;
#override
void initState() {
_controller = TextEditingController(text: widget.emailInput);
super.initState();
}
...
}