How can I use initState in a widget build to reference the value of riverpod? - flutter

I want to assign the value of currentUser.profilepictureURL to ListImage.
But if I put the initState outside of the widget build, it will be out of scope. Please tell me.
class _MyHomePageState extends ConsumerState<MyHomePage> {
var listImage = [];
#override
Widget build(BuildContext context) {
final currentUser = ref.watch(userModelProvider);
void initState() {
listImage.add(currentUser.profilePictureURL);
super.initState();
}
return Container();
}
}

The way your are using initState is wrong. InitState is an #override and it should not to be used inside a build method.
try this:
class _MyHomePageState extends ConsumerState<MyHomePage> {
var listImage = [];
#override
Widget build(BuildContext context) {
final currentUser = ref.watch(userModelProvider);
listImage.add(currentUser.profilePictureURL);
return Container();
}
}

Related

How to move a variable outside build?

How do I move a variable that uses a context outside of the build method so that it is created once?
class _EventListState extends State<EventList> {
#override
Widget build(BuildContext context) {
final eventNotifier = EventInherited.of(context).eventNotifier;
...
You can use the late modifier.
class _EventListState extends State<EventList> {
late final eventNotifier = EventInherited.of(context).eventNotifier;
#override
Widget build(BuildContext context) {
...
make
eventNotifier
class variable and connect it to the getter function.
class _EventListState extends State<EventList> {
var? eventNotifier;
#override
Widget build(BuildContext context) {
eventNotifier = EventInherited.of(context).eventNotifier;
}
Dynamic get getEventNotifier => this.eventNotifier;
You can use the didChangeDependencies() method. The context is available at that point.
#override
void didChangeDependencies(){
super.didChangeDependencies();
final eventNotifier = EventInherited.of(context).eventNotifier;
}
This method will be called again in certain scenarios, so be careful about what you are initializing.
This thread has more information on when it is recalled, but in general it is okay to use for initialing listeners.
Understanding Flutter didChangeDependencies mechanics

Flutter detect when value passed to the statefull widget changes

I have a statefull widget where am passing an integer to. I would like to execute a method when the value passed to the widget changes
class Test extends StatefulWidget {
final String item;
const Test({
Key? key,
required this.item,
}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
List<String> hh = [];
#override
void initState() {
super.initState();
print("init state value is ${widget.item}");
}
#override
Widget build(BuildContext context) {
return Container();
}
void getDataItems() {
print("value passed is ${widget.item}");
setState(() {
hh = [widget.item];
});
}
}
So on the above i would like to detect when the value of string item passed to the widget changes and call the method getDataItems which will later update the list hh.
How can i detect when the value passed statefull widget has changed?
You can check the useEffect, which is based on the React hook useEffect.
Or you can hardcode it.
Create other integer: late int lastCalledInteger;
And check this in the build:
#override
Widget build(BuildContext context) {
if(lastCalledInteger != integer) {
getDataItems();
lastCalledInteger = integer;
}
return Container();
}
You can simply override didUpdateWidget on State to be informed of every change of passed in arguments. If you have multiple arguments you of course need to change what changed. For this didUpdateWidget provides you with the oldWidget as only parameter.
From the documentation:
Called whenever the widget configuration changes.
[...]
Override this method to respond when the widget changes (e.g., to
start implicit animations).

super.initstate() not being called

Im a new flutter developer and have just started using the initState(). However, it seems like it never gets called, I tried all fixes I found on here, but none of them works! Here is a simplified example
import 'package:flutter/material.dart';
class example extends StatefulWidget {
#override
_State createState() => _State();
}
class _State extends State<example> {
#override
void Initstate() {
super.initState();
print('hello world');
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
The hello world never gets printed though!
initState is not supposed to be inside the build function. You can create the initState function by going to the line above #override before build(), type init, hit ctrl space and it will autocomplete. This would be the correct version:
class _State extends State<Example> {
#override
void initState() {
super.initState();
print('hello world');
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}

How to use stateful widget parameters in state class at construction without adding the widget to the tree?

I stumped into a problem where I need to use a StatefulWidget parameter in its state class when it's constructed, but I couldn't find a way to do it since using widget.[whatever variable name] in the state's class constructor returns an unexpected null value, and the initState function only runs when the widget is being drawn to the screen.
For example:
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
final String text;
Test(this.text);
final state = _TestState();
#override
_TestState createState() => state;
}
class _TestState extends State<Test> {
String? changingText;
void updateChangingText(String moreText){
changingText = changingText! + moreText;
}
#override
void initState() {
super.initState();
changingText = widget.text;
}
#override
Widget build(BuildContext context) {
return Text(changingText!);
}
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
var w = Test('test');
w.state.updateChangingText(' text');
return MaterialApp(home: Scaffold(body:
Test('test text')
));
}
}
void main() {
runApp(App());
}
This doesn't work since changingText is being updated before initState gives it its initial value since it only runs when Text is being drawn to the screen and this:
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
final String text;
Test(this.text);
final state = _TestState();
#override
_TestState createState() => state;
}
class _TestState extends State<Test> {
String? changingText;
void updateChangingText(String moreText){
changingText = changingText! + moreText;
}
_TestState(){
changingText = widget.text;
}
#override
Widget build(BuildContext context) {
return Text(changingText!);
}
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
var w = Test('test');
w.state.updateChangingText(' text');
return MaterialApp(home: Scaffold(body:
Test('test text')
));
}
}
void main() {
runApp(App());
}
doesn't work either since you can't use widget.[whatever] in state class constructors (for some reason).
So what can I do to use widget parameters in the state class before the widget is drawn to the screen?
Thanks in advance for the help
You should use the initState method present in the State for this instead of the constructor
#override
void initState() {
changingText = widget.text;
super.initState();
}

How to ensure code is run only once in a widget?

I do have a lot of code that looks like
this:
bool _somethingFromApiLoaded = false;
Something _somethingFromApi;
loadSomething() async {
final something = await ServiceProvider.of(context).apiService.getSomething();
setState(() => _somethingFromApi = something);
}
#override
Widget build(BuildContext context) {
if (!_somethingFromApiLoaded) {
loadSomething();
_somethingFromApiLoaded = true;
}
}
Note how I produce a lot of boilerplate code to ensure loadSomething is only called once.
I wonder if there isn't a lifecycle method to do so that I somehow misinterpret. I can't use initState because it does not have context.
I would try to a use a StatefulWidget and use initState() method.
That is the lifecycle you are referring to.
You should try to use a Future inside the initState()
#override
void initState() {
super.initState(); // make sure this is called in the beggining
// your code here runs only once
Future.delayed(Duration.zero,() {
_somethingFromApi = await ServiceProvider.of(context).apiService.getSomething();
});
}
As User gegobyte said, Context is available in the initState.
But apparently can't be used for everything.
You can use context in initState() by passing it to the widget:
class HomeScreen extends StatefulWidget {
final BuildContext context;
HomeScreen(this.context);
#override
State<StatefulWidget> createState() => new _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
bool _somethingFromApiLoaded = false;
Something _somethingFromApi;
loadSomething() async {
final something = await ServiceProvider.of(widget.context).apiService.getSomething();
setState(() => _somethingFromApi = something);
}
#override
void initState() {
super.initState();
if (!_somethingFromApiLoaded) {
loadSomething();
_somethingFromApiLoaded = true;
}
}
}