Initializing non-mutable fields in Stateful Widgets in Flutter - flutter

When creating a stateful widget in flutter, you may want some fields of the widget to not be mutated. In that case, I'm having trouble trying to figure out if it is better to always reference those fields from the state's widget reference, or if it's better to declare those fields in the state still, and get any initial values from the widget. For example:
class MyStatefulWidget extends StatefulWidget {
final bool? mutateMe; // allows the user to provide an initial value of a mutable field
final bool? doNotMutateMe; // allows the user to provide the value of a field that is not intended to be mutated
MyStatefulWidget({ super.key, this.mutateMe, this.doNotMutuateMe });
#override State<MyStatefulWidget> createState() => MyStatefulWidgetState();
}
class MyStatefulWidgetState extends State<MyStatefulWidget> {
late bool mutateMe;
late bool doNotMutateMe; // <-- HERE: is it better to include this field here?
#override void initState() {
mutateMe = widget.mutateMe ?? true;
doNotMutateMe = widget.doNotMutateMe ?? false;
}
// ...
}
For a field like doNotMutateMe, that is not intended to be modified, does it make sense to re-create the field in the state object, or not, and always just refer to widget.doNotMutateMe instead?
I've read that the state object outlives the widget, so I'm curious what implications that might have here?

As you've included , I will prefer using widget.variableName on state class
class MyStatefulWidget extends StatefulWidget {
final bool? mutateMe;
final bool? doNotMutateMe;
const MyStatefulWidget({
super.key,
this.mutateMe = true,
this.doNotMutateMe = false,
});
#override
State<MyStatefulWidget> createState() => MyStatefulWidgetState();
}

Related

Initializing Flutter Stateful Widget: Why is it discouraged to pass initial values to `State` constructor?

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;

Accessing state in widget and making class immutable

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

Initialize StatefulWidget state null safe from widget constructor in Flutter

I want to have a StatefulWidget where I can pass the initial value for a non-nullable member of the widgets State from the widgets constructor.
My current solution (see below) seems to be not ideal, I see two problems with it:
The initial value has to be saved in the widget itself before passing it to the state.
The member in the sate has to be marked as late since it can only be set after initialization.
Is there a better way to initialize a StatefulWidget's state non-nullable member from a value passed to the widget constructor?
My current implementation:
class MyWidget extends StatefulWidget {
final String text;
const MyWidget({Key? key, required this.text}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late String text;
#override
void initState() {
text = widget.text;
super.initState();
}
#override
Widget build(BuildContext context) {
return Text(text);
}
}
(Not shown here, but later the text member should be changeable, that's why it is in the State)
hey there your code seems good.
but the better way is using bloc to pass and receive data.
any way . its not necessary to pass and fill data in initstate and _MyWidgetState .
you can receive your data directly in build widget As you wrote (widget.text)
here is some good things for avoid nullable
https://codewithandrea.com/videos/dart-null-safety-ultimate-guide-non-nullable-types/
You could use the constructor of State like this: _MyWidgetState(){ text=widget.text; }. The constructor will certainly be executed before initState and build methods.

Flutter: Undefined name Try correcting the name to one that is defined, or defining the name

I am facing a few strange issues with Flutter. I do have very little knowledge about Flutter. I am learning it.
class ViewOtherProfile extends StatefulWidget {
final String userName;
final int points;
const ViewOtherProfile({
#required this.userName,
#required this.points,
});
You can see i am getting userName and Points data as argument.
I want to print this argument in the page. Like this
class _ViewOtherProfileState extends State<ViewOtherProfile> {
..........
void initState(){
print(points);
deviceInfo();
super.initState();
print(userName);
]);
}
............
Now problem is i am getting error.
Undefined name 'userName'.
Try correcting the name to one that is defined, or defining the name.
Any reason why i am getting this error and how i can resolve it.
Thanks to #jamesdlin
I tried to put it like this
print(ViewOtherProfile.userName);
but now i am getting another error.
Instance member 'userName' can't be accessed using static access.
There are two main types of widgets in Flutter. StatelessWidget and StatefullWidget. A StatelessWidget is built only one when the UI builds and is never rebuilt. Meanwhile, a StatefulWidget can be rebuilt every time if a call for setState is made.
Hence, for a StatefulWiget, there is a need to track the state of the class by extending the main class with a State class.
You have to note that the scope of variables in those two types of widgets can vary. For example...
class ExampleStateless extends StatelessWidget {
final String userName;
final int points;
const ExampleStateless({
#required this.userName,
#required this.points,
});
#override
Widget build(BuildContext context){
print(userName); print(points);
return Something();
}
}
Note that for the stateful widget, there are two classes and each class has its scope. The superclass ExampleStateful can share its scope to its subclass _ExampleStatefulState via the widget
class ExampleStateful extends StatefulWidget {
final String userName;
final int points;
Static final string id = "exampleState";
const ExampleStatefull({
#required this.userName,
#required this.points,
});
// scope 1
#override
_ExampleStatefulState createState() => _ExampleStatefulState();
}
class _ExampleStatefulState extends State<ExampleStateful>{
// scope 2
final String userName2 = null;
#override
void initState(){
super.initState();
print(widget.userName);
print(userName2);
print(ExampleStateful.id); // you can do this only if variable is static.
}
}
What is in scope 1 can be accessed in scope 2 via the widget properties. eg print(widget.userName); instead of print(userName);

GlobalKey in InheritedWidget causing re-parent

Is passing a GlobalKey down the tree using an InheritedWidget an antipattern? The stateful widget using that key is re-created (i.e. a new state this initState/disposed) every time its subtree is re-built.
My InheritedWidget looks like:
import 'package:flutter/material.dart';
import '../widgets/carousel.dart';
import '../widgets/panel/panel.dart';
class _CarouselKey extends GlobalObjectKey<CarouselState> {
const _CarouselKey(Object value) : super(value);
}
class _ProgressiveChatHeaderKey extends GlobalObjectKey<PanelScaffoldState> {
const _ProgressiveChatHeaderKey(Object value) : super(value);
}
class DimensionScopedKeyProvider extends InheritedWidget {
final _CarouselKey parallelBubbleCarouselKey;
final _ProgressiveChatHeaderKey progressiveChatHeaderKey;
final String keyString;
DimensionScopedKeyProvider({
Key key,
#required this.keyString,
#required Widget child,
}) : parallelBubbleCarouselKey = _CarouselKey(keyString),
progressiveChatHeaderKey = _ProgressiveChatHeaderKey(keyString),
super(key: key, child: child);
static DimensionScopedKeyProvider of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(DimensionScopedKeyProvider)
as DimensionScopedKeyProvider);
}
#override
bool updateShouldNotify(DimensionScopedKeyProvider oldWidget) => oldWidget.keyString != keyString;
}
And this InheritedWidget is rendered with a constant keyString, meaning that 1) updateShouldNotify always returns false and 2) the hashCode of the GlobalKeys passed to my build methods via DimensionScopedKeyProvider.of() are always identical.
The stateful widget builds something like
GlobalKey<PanelScaffoldState> get _headerKey => //
DimensionScopedKeyProvider.of(context).progressiveChatHeaderKey;
// ...
PanelScaffold(
key: _headerKey,
// ...
)
When I change a property that affects the subtree that the PanelScaffold lives in, though, a new PanelScaffoldState is created and the old one is disposed, even though the widget tree hasn't changed structure and the _headerKey hasn't changed either.
I also able to solve this problem, but I have no idea why it works.
The solution is to cache the access to the GlobalKey in didChangeDependencies
#override
void didChangeDependencies() {
super.didChangeDependencies();
_headerKey ??= DimensionScopedKeyProvider.of(context).progressiveChatHeaderKey;
}
.... and now everything is working as expected again—the rebuilds re-parent the existing state.
Does anyone know why caching the getter to the GlobalKey is the key here?