I keep primitive states inside State. But for objects, the following works:
class _Like extends StatefulWidget {
final Post _post; <-- mutable object is here
_Like(this._post);
#override
State<StatefulWidget> createState() => _LikeState();
}
class _LikeState extends State<_Like> {
...
_like() {
setState(() {
widget._post.liked = !widget._post.liked; <-- mutated here
});
}
...
}
What would the be reason not to use this approach? (As opposed to moving the state inside State, preferably as primitive bool)
Perhaps because initState() is in the State class, which your StatefulWidget can't call. So your entire state should be in your State class to permit that.
Related
I have a bloc which is responsible for switching indexes in the Navogation Bottom Bar.It is implemented in such a way that it copies the old state and changes it. I need to replace copyWith and make it not copy but create a new state. How can this be implemented and rewritten given bloc?
class BottomNavyBloc extends Bloc<BottomNavyEvent, BottomNavyState> {
BottomNavyBloc() : super(const BottomNavyState()) {
on<ChangePageEvent>(
(event, emit) => emit(
state.copyWith(index: event.index),
),
);
}
}
abstract class BottomNavyEvent extends Equatable {
const BottomNavyEvent();
#override
List<Object> get props => [];
}
class ChangePageEvent extends BottomNavyEvent {
final int index;
const ChangePageEvent({
required this.index,
});
#override
List<Object> get props => [index];
}
My state:
class BottomNavyState extends Equatable {
const BottomNavyState({
this.index = 0,
});
final int index;
#override
List<Object> get props => [index];
}
class ChangePageState extends BottomNavyState {
}
We use
emit(state.copyWith(index: event.index))
to say that we are copying all the elements from the previous state by changing index.
Your state BottomNavyState has only one variable as of now. So, the above copyWith acts similar to using emitting new state.
We should not try to change or override the method copyWith because it beats the method's actual purpose.
Instead, you could use
emit(BottomNavyState(index: event.index))
to use a new state constructor instead of copying from previous state.
I have a situation where I want to create multiple flutter StatefulWidgets based on the same state class. The problem I run into seems clear, the state widget needs to know the properties of the parent that calls createState. There has to be a way to accomplish this without having to copy paste large amounts of code.
What I have so far is a state class that looks like this:
abstract class XxxState<T extends StatefulWidget> extends State<T>
{ TextEditingController textController; // instantiated by initState().
}
to accomplish what I want I create several abstract methods that the subclass will override to implement it's function. The problem is when I create the class I want to instantiate I don't know how to connect the new StatelessWidget to the state class that overrides the XxxState class.
class YyyWidget extends StatefulWidget {
#override
_YyyState createState() => _YyyState();
}
class _YyyState<YyyWidget> extends XxxState {
/// In this class widget.xxx looks to StatefulWidget and not YyyWidget
}
Any reference for how this should be done would be appreciated.
All one has to do is learn how to subclass a StatefulWidget correctly and all works as expected.
abstract class YyyWidget extends StatefulWidget { ... }
// Do not do the createState here
// Someone edited the question and removed the abstract from the code,
// thereby changing the question to one they thought they could answer.
Then create the abstract state
abstract class YyyState<Page extends XxxWidget>
extends State<Page> {
{
TextEditingController textController;
...
}
Then the subclass calls
class ZzzWidget extends XxxWidget {
// Do the create state here
#override
_AaaState createState() => _AaaState();
}
and the subclass state is
class _AaaState extends YyyState<AaaWidget> {
//inside this class widget.field gets state from AaaWidget
}
I don't even remember what the original post was, So maybe deleting the question is the better option here.
I am trying to get a list from class to the other. But I want it to only be transferred after it has got a value assigned from a Future. Is there a way to do so (something like a setState method that acts across classes) My code is here:
class Design extends StatefulWidget {
#override
_DesignState createState() => _DesignState();
}
class _DesignState extends State<Design>{
var Data;
#override
void initState() {
super.initState();
comparer().then((List returnedV){
setState(() {
Data = returnedV;
});
});
}
Future<List> compare() async {
...
return dataDevice
}
}
class AboutSheet extends StatefulWidget {
final List Data;
AboutSheet({#required this.Data});
#override
_AboutSheetState createState() => _AboutSheetState();
}
class _AboutSheetState extends State<AboutSheet> {
}
Every time I use the variable Data in the second class it has the value null. I think it's because I have defined it before with the value null and it's pulling that and is not waiting for the future to assign a value to it. I can't think of a workaround. I would really appreciate your help!
What you are referring to is a state management solution. There is a lot of them, with each their pros and cons. I (and the Flutter team) would suggest Provider.
Take a look at this : List of state management approaches
(i18n) I use setState from outside of MyApp class to change language, I got this warning, and don't know how to solve it.
info: The member 'setState' can only be used within instance members of subclasses of 'package:flutter/src/widgets/framework.dart'. (invalid_use_of_protected_member at [flutter_firebase_authen] lib\app.dart:22)
class MyApp extends StatefulWidget {
final FirebaseAnalyticsObserver observer;
const MyApp({
Key key,
#required this.observer,
}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
static void setLocale(BuildContext context, Locale newLocale) {
final _MyAppState state = context.ancestorStateOfType(const TypeMatcher<_MyAppState>());
state.setState(() {
state.locale = newLocale;
});
}
}
The warning message is quite clear: the function setState should only be called from within the class, not from another class.
The workaround is simple, write a helper function inside your State class, that calls setState for you. For example:
refresh() => setState(() {});
Now from outside this class, you can call state.refresh().
(But really, if you are calling setState from another class, maybe you should look into ValueNotifier, or StreamBuilder, etc.)
If I instantiate a BLoC in MyWidget, I'll usually use a StatefulWidget and close the stream in the dispose method like so:
class _MyWidgetState extends State<MyWidget> {
...
#override
void initState() {
// ...
bloc = MyBloc()
}
#override
void dispose() {
bloc.stream.close();
// ...
}
...
}
However, when I inject a provided instance of MyBloc into MyWidget (maybe via MyWidget's constructor), I close the stream higher up the widget hierarchy since MyWidget does not get a new instance of MyBloc with a new Stream when the user navigates away from and back to MyWidget.
class MyWidget extends StatefulWidget {
final MyBloc bloc;
MyWidget(this.bloc) ... ;
..
}
class _MyWidgetState extends State<MyWidget> {
...
#override
void dispose() {
// Not closing stream here anymore. Closing higher up because
// I don't get a fresh instance when user comes back here;
// ...
}
}
Does this cause a memory leak?
That's not a problem, as long that the widget that created your object correctly dispose it when removed from the widget tree.
In fact, descendants should not call dispose on their parameters.
Doing so is an anti-pattern, as a descendant don't have the ownership of their parameter and therefore should not do any modifications on it.