coming from Kotlin(Android) I wanted to know which of the below code is the recommended way to initialize a simple boolean state property in flutter
...
class _LogInPagePageState extends State<LogInPage> {
bool obscurePasswords = true; //Is it better to set the initial value of obscurePasswords here?
#override
void initState() {
super.initState();
obscurePasswords = true; //Or is it more preferred here?
}
....
Which is the best place to create the initial state of something as simple as the obscurePassword property
Related
I have a global bloc that wraps the materiapApp widget and I am able to get the bloc inside my deep nested widget tree. However, I would like to access the current state's property.
#override
void initState() {
super.initState();
_internetConnectionBloc = BlocProvider.of<InternetConnectionBloc>(context);
//I am able to get the bloc but I would like to access it's current state's property
}
States:
abstract class InternetConnectionState extends Equatable {
const InternetConnectionState();
#override
List<Object> get props => [];
}
class InternetConnectionInitial extends InternetConnectionState {}
class InternetConnectionStatusUpdated extends InternetConnectionState {
final InternetConnectionType connectionType;
const InternetConnectionStatusUpdated(this.connectionType);
#override
List<Object> get props => [connectionType];
}
I would like to access the InternetConnectionStatusUpdated state's connectionType property in the initState instead of blocListenerin the build method.
try this in initState
var connectionType = BlocProvider.of<InternetConnectionBloc>(context).state.connectionType;
I fixed it like this:
#override
void initState() {
super.initState();
_internetConnectionBloc = BlocProvider.of<InternetConnectionBloc>(context);
final state = _internetConnectionBloc!.state;
if(state is InternetConnectionStatusUpdated){ // --> this way dart
//analyze was able to understand the state type
print(state.connectionType);
}
}
I have a state that looks like this where I call the method _getData inside the initState
class _DataPageState extends State<DataPage> {
#override
void initState() {
super.initState();
_selectedEvents = ValueNotifier(_getData(value!));
}
#override
void dispose() {
_selectedEvents.dispose();
super.dispose();
}
List<dynamic> _getData(DateTime value) {
//trying to access 'events' variable here
return events[day] ?? [];
}
And my build method looks likes this
#override
Widget build(BuildContext context) {
final _source = toResults(data);
//'events' variable I'm trying to pass
final events = LinkedHashMap<DateTime, List<dynamic>>(
equals: isSameDay,
hashCode: getHashCode,
)..addAll(_source);
}
I'm trying to pass the events variable to my _getData method inside the state to be able to run it inside the initState, is there an appropriate way of doing that?
And if not, is there a way of moving the final variable up without having the issue of "Instance member can't be accessed in an initializer" ?
Your build method should be "pure", meaning it should not have side effects, since Flutter can call it whenever it needs to rebuild the widget tree (e.g. if a keyboard appears, if changing to landscape mode, because a parent rebuilds, etc).
In Dart, if you have a variable that will be initialized before it is first used, but cannot be immediately initialized, you can use the late keyword. Perhaps something like this would work:
class MyState extends State<MyWidget> {
late LinkedHashMap<DateTime, List<dynamic>> _events; // late means no initializer needed
#override
void initState() {
super.initState();
final _source = toResults(data);
_events = LinkedHashMap<DateTime, List<dynamic>>(
equals: isSameDay,
hashCode: getHashCode,
)..addAll(_source);
}
// other stuff
}
It's not entirely clear where data is coming from, but if it can be passed in from higher up in the tree, you could make it a property on the Widget class and use widget.data to read it safely inside initState()
For the snippet of the code below:
// snippet of the main class
class MainState extends State<Main>{
MusicMaterial musicObj = MusicMaterial();
SoundsMaterial soundObj = SoundsMaterial();
#override
Widget build(BuildContext context) {
return Container(
child: something.value == 0
? musicObj
: soundObj
);
}
}
// snippet of the MusicMaterial class
class MusicMaterialState extends State<MusicMaterial>{
#override
Widget build(BuildContext context) {
return Row(
AnotherClass obj1 = AnotherClass(0, 'test'),
AnotherClass obj2 = AnotherClass(1, 'test'),
);
}
}
// snippet of the AnotherClass class
class AnotherClassState extends State<AnotherClass>{
import '../globals.dart' as globals;
#override
void initState() {
globals.globalCounter++; // this variable is just a global variable from the globals.dart page
}
}
// snippet of the global.dart
library my_prj.globals;
globalCounter = 0;
It keeps creating a new instance every time the "if" state is updated in the Main State class. So for instance, the value of the global counter keeps going up from 0 to 2 to 4...8... How do we ensure that the object does not get re-initialized every single time, so for instance void initState() from AnotherClassState is called only once? i.e the value remains 2 and only 2.
I have tried using "AutomaticKeepAliveClientMixin and #override bool get wantKeepAlive => true" - i.e keeping it alive so when it is invoked next time, it does not call initState() again, however it did not work.
Hopefully I'm understanding correctly what you need. It seems that you want the counter to be increased only one time per class type. I'm sure there are different ways to do it but It comes to my mind to make globalCounter a little more complex
class GlobalCounter {
List<String> _keys = List<String>();
int _counter = 0;
int get counter => _counter;
void increaseCounter(String key) {
// increase only if the key passed as parameter didn't increase already
if (!_keys.contains(key)) {
_counter++;
_keys.add(key);
}
}
}
globalCounter = GlobalCounter();
Then you can use it like this
#override
void initState() {
// pass the type of the instance trying to increase the counter
globals.globalCounter.increaseCounter(this.runtimeType.toString());
}
I'm currently trying Provider as a state management solution, and I understand that it can't be used inside the initState function.
All examples that I've seen call a method inside a derived ChangeNotifier class upon user action (user clicks a button, for example), but what if I need to call a method when initialising my state?
Motivation:
Creating a screen which loads assets (async) and shows progress
An example for the ChangeNotifier class (can't call add from initState):
import 'package:flutter/foundation.dart';
class ProgressData extends ChangeNotifier {
double _progress = 0;
double get progress => _progress;
void add(double dProgress) {
_progress += dProgress;
notifyListeners();
}
}
You can call such methods from the constructor of your ChangeNotifier:
class MyNotifier with ChangeNotifier {
MyNotifier() {
someMethod();
}
void someMethod() {
// TODO: do something
}
}
Change your code to this
class ProgressData extends ChangeNotifier {
double _progress = 0;
double get progress => _progress;
void add(double dProgress) async {
// Loading Assets maybe async process with its network call, etc.
_progress += dProgress;
notifyListeners();
}
ProgressData() {
add();
}
}
In initState all the of(context) things don't work correctly, because the widget is not fully wired up with every thing in initState.
You can use this code:
Provider.of<ProgressData>(context, listen: false).add(progress)
Or this code:
Future.delayed(Duration.zero).then(_){
Provider.of<ProgressData>(context).add(progress)
}):
So an AssetLoader class which reports on its progress will look something like this, I guess:
import 'package:flutter/foundation.dart';
class ProgressData extends ChangeNotifier {
double _progress = 0;
ProgressData() {
_loadFake();
}
Future<void> _loadFake() async {
await _delayed(true, Duration(seconds: 1));
_add(1.0);
await _delayed(true, Duration(seconds: 2));
_add(2.0);
await _delayed(true, Duration(seconds: 3));
_add(3.0);
}
// progress
double get progress => _progress;
// add
void _add(double dProgress) {
_progress += dProgress;
notifyListeners();
}
// _delayed
Future<dynamic> _delayed(dynamic returnVal, Duration duration) {
return Future.delayed(duration, () => returnVal);
}
}
As Fateme said:
the widget is not fully wired up with everything in initState
Also, you can use something like this in your initState
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
Provider.of<ProgressData>(context, listen: false).add(5);
});
I think it's more standard!
Be aware that you should use the correct context! I mean the context of the Builder!
The problem here lies with the fact that context does not exist yet in initState as extensively explained by the other answers. It doesn't exist because it hasn't yet been made a part of the widget tree.
Calling a method
If you're not assigning any state and only calling a method then initState would be the best place to get this done.
// The key here is the listen: false
Provider.of<MyProvider>(context, listen: false).mymethod();
The code above is allowed by Flutter because it doesn't have to listen for anything. In short, it's a one off. Use it where you only want to do something instead of read/listen to something.
Listening to changes
Alternatively, if you need to listen to changes from Provider then the use of didChangeDependencies would be the best place to do so as context would exist here as in the docs.
This method is also called immediately after initState.
int? myState;
#override
void didChangeDependencies() {
// No listen: false
myState = Provider.of<MyProvider>(context).data;
super.didChangeDependencies();
}
If you've never used didChangeDependencies before, what it does is get called whenever updateShouldNotify() returns true. This in turn lets any widgets that requested an inherited widget in build() respond as needed.
I'd usually use this method in a FutureBuilder to prevent reloading data when data already exists in Provider after switching screens. This way I can just check Provider for myState and skip the preloader (if any) entirely.
Hope this helps.
I am using Provider. I have got class. And I need to set value to Provider from field. But I do not have access to context here.
class _DateAndTimePickerDemoState extends State<DateAndTimePickerDemo>
{
DateTime _fromDate = DateTime.now();
Provider.of<AppState>(context).selected_period = date; // here
}
How to set Provider value from class field?
Option No 1
You can use override method initState()
#override
void initState() {
super.initState();
Future.delayed(Duration.zero,(){
Provider.of<AppState>(context).selected_period = /* your value */;
});
}
Option No 2
You can set value in build() method also
#override
Widget build(BuildContext context) {
Provider.of<AppState>(context).selected_period = /* your value */;
// your rest of code is write here
}
You can get context in two ways:
Inside initState by calling Future.delayed(Duration.zero,() {... context ...})
After build method has been called Widget build(BuildContext context) {...}
In your case, I would call Provider.of<AppState>(context).selected_period = date; after build method, because most of the functions I define inside build method anyways so I can easily access context.
Use Provider.of<AppState>(context, listen: false).selected_period = /* your value */;
You need to set listen to false because Provider is used outside the build method.