How to execute function when a variable is not null Flutter - flutter

I have this code in _MyAppState in my main.dart :
var _p3provider;
#override
void initState() {
super.initState();
P3provider p3provider = new P3provider();
authentification(p3provider);
_p3provider = p3provider;
}
I pass the variable _p3provider as p3provider to MyHomePage statefulWidget of my main.dart,
Here is some code of _MyHomePageState :
void initState() {
super.initState();
if (widget.p3provider.jsession_id == null) {
Future.delayed(Duration(seconds: 2), (){
getTasks();
});
} else {
getTasks();
}
}
To execute my getTasks function I need the jsession_id variable from p3provider object to be initialized. This variable is initialized in the initState of _MyAppState at this line : authentification(p3provider); .
The problem is that both the initState of _MyAppState and _MyHomePageState are executed at the same time so if I directly call getTasks() in the initState of _MyHomePageState it will return an error because p3provider.jsession_id will not have been initialized yet.
To avoid this error I used a Future.delayed after a checking if the variable is null, but I don't think this solution is optimal, I would like the function getTasks() to be executed directly when jsession_id become not null.
If anyone has an idea on how to do this, you are more than welcome ! :)

Found a solution :
I define this class :
class HomePageController{
var getTasks;
}
Which I instanciate in _MyAppState :
final HomePageController myController = HomePageController();
I pass it down to MyHomePage widget :
home: MyHomePage(controller: myController),
And in the initState of _MyHomePageState I assign my function getTasks() to the controller variable getTasks :
#override
void initState() {
super.initState();
widget.controller.getTasks = getTasks;
}
Now I can execute getTasks via the controller in _MyAppState :
#override
void initState(){
super.initState();
authentification().then((value){
myController.getTasks();
}
Here is the topic where I got it : Flutter calling child class function from parent class

Related

Access function defined in MyApp() class - Flutter

I would like to access a function defined in my top level class from another class. How can i do that ?
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp>
{
void startListeningNotifications()
{
//start listening to fcm messages
}
void initState()
{
super.initState();
startListeningNotifications();
}
}
I want to call this function startListeningNotifications() from another class. Is that possible ?
I am already calling this function in initState() but there are some cases in which i need to call it from some other class. For example, if a user isn't already registered with your Firebase-app, then after the registration process, i need to access this method in order to start listening to fcm notifications.
You can define startListeningNotifications() in a different file, import it into any page you need and call it there.
// create lib/_utils/fcm_utils.dart
void startListeningNotifications() {
// your function
}
...
// main.dart or any page you want to call your functions from
// TODO: replace yourAppName below with your app name
import 'package:yourAppName/_utils/fcm_utils.dart';
...
void initState() {
super.initState();
startListeningNotifications();
}

Flutter force screen update with initState

I have a StatefulWidget in which I load data from the server using the initState(). It is somethig like this:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
initState() {
super.initState();
// ... Load the data
}
// build
}
When I use the Navigator.pop() or Navigator.pushNamed() it does not reinit the state. Is there any away to prevent this?
After you call 'pushNamed' in MyApp, wait until 'pushNamed()' will be returned which is occurred when 'pop()' is called.
After that, refresh data and call build() by calling 'setState()'.
await Navigator.pushNamed(context, "/somePage");
...
[call reloadData sentence]
...
setState(() {});

How to use the same BLoC for different widgets in Flutter?

I have 2 custom widgets, and I want to use the same Bloc file.
My Bloc file gets data from the internet in the constructor.
class MyBloc {
// StreamControllers, StreamSinks, Streams ...
MyBloc() {
getDataFromInternet();
}
}
class MyWidget1 extends StatefulWidget {
MyWidget1({Key key}) : super(key: key);
#override
_MyWidget1State createState() => _MyWidget1State();
}
class _MyWidget1State extends State<MyWidget1> {
MyBloc _bloc;
#override
void initState() {
_bloc = MyBloc();
super.initState();
}
}
class MyWidget2 extends StatefulWidget {
MyWidget2({Key key}) : super(key: key);
#override
_MyWidget2State createState() => _MyWidget2State();
}
class _MyWidget2State extends State<MyWidget2> {
MyBloc _bloc;
#override
void initState() {
_bloc = MyBloc();
super.initState();
}
}
My problem is, that it downloads the data every time the screen changes (any of the two widgets appear on the screen).
Should I pass the initialized bloc object to the widgets in the constructors, and not create a new Bloc in the widgets constructor? I don't want to save the data and write logic to check if I already downloaded it or not.
Use this bloc implementation https://bloclibrary.dev/
Your bloc will have single instance with it's single state at the moment. Invoke new state depending on previous and you will never has problems with unneeded queries or something like this.

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;
}
}
}

is there any difference between assigning value to the variable inside of initState or not in Flutter StatefulWidget?

I saw two ways to declare variables with StatefulWidget in many sample codes.
initialize variable with value (firstCase)
initialize variable without value and assign to value inside of initState (secondCase)
Is there any difference between these?
Or which one would be better code in practice?
class Sample extends StatefulWidget {
Sample({Key key}) : super(key: key);
#override
_SampleState createState() => _SampleState();
}
class _SampleState extends State<Sample> {
bool firstCase = false;
bool secondCase;
#override
void initState() {
secondCase = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: child,
);
}
}
If you can create initialise your variable directly in the property, do so. It's better for readability (a single place to look for).
The only reason you'll want to use initState is for when you cannot initialise the variable directly from its declaration.
These situations are for the most part:
your variable depends on widget or context
it depends on this
For example, if you want to create an AnimationController you'll need to pass it vsync: this. But the following won't compile:
class MyState extends State with SingleTickerProviderStateMixin {
final myController = AnimationController(
vsync: this, // compile error, cannot use `this` on initialisers
);
}
And you'd have to instead write:
class MyState extends State with SingleTickerProviderStateMixin {
AnimationController myController;
#override
void initState() {
super.initState();
myController = AnimationController(
vsync: this, // OK
);
}
}
Although note that this specific example will soon change as a future version of Dart will introduce the late keyword, which then allows:
class MyState extends State with SingleTickerProviderStateMixin {
late final myController = AnimationController(
vsync: this, // OK, not a compile error this time
);
}
You may still need initState for variables that depends on widget/context though.