How do I call a function within my main page from another file? - flutter

I have the following problem:
My Stateful Widget "HomePage" has an Function:
void refresh() {
//not important for now
}
But, my whole save and load functions are located in an extra file (save_load.dart)
It's not a class or whatever, just pure functions that I can call from every other file.
void loadList(String key) {
//not important as well
//The following line is what I basically want to achieve
HomePage.refresh();
}
My Problem now is: I want to call the function refresh() after/within the loadList() function. Do you have an idea on how I can achieve this?
Thank you so much!

Summary
The easiest way to achieve what you're looking to do is to use a GlobalKey<HomePageState> to reference the currentState of your HomePage widget and then call the refresh method.
Solution
First: Create your Global Key
Create your global key and pass it into your HomePage when you create it.
final GlobalKey<HomePageState> homeKey = GlobalKey<HomePageState>();
...
// In one of your build methods where you're creating your `HomePage` widget.
HomePage(key: homeKey, ...)
Second: Get The Current State
Next, you'll need to get access to the current state of your HomePage widget.
final homePageState = homeKey.currentState;
Last: Call Refresh
Finally, you'll want to call the refresh method using the state class you retrieved from the GlobalKey.
// This is how you access it safely
homePageState?.refresh(); // will only call refresh if the homePageState is not null
// OR
homePageState!.refresh(); // will throw if homePageState is null

Related

What's the correct way to use methods from a sibling widget?

I am trying to clean my code by putting different widgets in different files/classes so it's not all inside the same main widget.
So right now I have this:
MainWidget {
Scaffold {
body: MyWebView(),
bottomNavigationBar: MyNavBar(),
}
}
Both are stateful widgets.
So now I'm wondering: how can I use methods from MyWebView from inside MyNavBar?
I've tried MyWebView.of(context).method() and even do a middle method inside MainWidget to try and call the methods from the parent widget always, but it's always "null". I've seen people mentioning PrivateKeys but many say this is an old way of doing things and I shouldn't rely on that.
So how can I do this?
I would need more details to provide an exact solution but based on your question this is the answer:
You have two options to do what you need
Lift Up The state:
This means In some situations, there are some states that are needed and shared among two or more sibling widgets, what we can do about it is to move those states to the parent widget, and provide it to the children, either by passing the variables and functions or using inherited-widget-based methods like using provider framework. so in your case, you can keep the function and state in the main widget, and pass it to both children.
Use a Key to access the MyWebView widget's state: as you mentioned in the comments, this is exactly done as you said :
you'd have to do it on the MainWidget, pass it onto the MyWebView(key: key), and then pass it to MyNavBar(webviewKey: key) to use it there
if you want to pass variable instead use typedef,
first declare outside MyWebview class :
let me guess, you need some int value from webview to pass it into navbar class.
typedef OndataProcessed = void Function(int valueIneed);
class MyWebView {
--------
final OndataProcessed ondataprocess;
and in methods in my webview
int someMethod (){
int result = blabla;
ondataprocess(result);
}
and finally :
MainWidget {
int resultfromwebview;
Scaffold {
body: MyWebView(ondataprocess: (int result){
setstate((){resultfromwebview = result})
}),
bottomNavigationBar: MyNavBar(index: result),
}
}

How to attend best practice for not using UI code in the Controller with GetX flutter when I need to show a Dialog if my task complete.?

For a simple Email login with OTP code I have a structure as follows.
View
await _signUpCntrl.signUp(email, password);
Controller
_showOtpDialog(email);
_showOtpDialog func
return Get.dialog(
AlertDialog(
So the thing is _showOtpDialog function is inside a controller file. ie. /Controllers/controller_file.dart
I want do something like a blocListener, call the _showOtpDialog from a screen(view) file on signup success. (also relocate the _showOtpDialog to a view file)
Using GetX I have to use one of the builders either obs or getbuilder. Which is I think not a good approach to show a dialog box.
On internet it says Workers are the alternative to BlocListener. However Workers function resides on Controller file and with that the dialog is still being called on the controller file.
As OTP dialog will have its own state and a controller I wanted to put it inside a /view/viewfile.dart
How do I obtain this?
I tried using StateMixin but when I call Get.dialog() it throw an error.
visitChildElements() called during build
Unlike BLoC there's no BlocListener or BlocConsumer in GetX.
Instead GetX has RxWorkers. You can store your response object in a Rx variable:
class SomeController extends GetxController{
final response= Rxn<SomeResponse>();
Future<void> someMethod()async{
response.value = await someApiCall();
}
}
And then right before the return of your widget's build method:
class SomeWidget extends StatelessWidget{
final controller = Get.put(SomeController());
#override
Widget build(BuildContext context){
ever(controller.response, (SomeResponse res){
if(res.success){
return Get.dialog(SuccessDialog()); //Or snackbar, or navigate to another page
}
....
});
return UI();
}
First thing, you will need to enhance the quality of your question by making things more clearly. Add the code block and the number list, highlight those and making emphasize texts are bold. Use the code block instead of quote.
Seconds things, Depends on the state management you are using, we will have different approaches:
Bloc (As you already added to the question tag). By using this state management, you controller ( business logic handler) will act like the view model in the MVVM architecture. In terms of that, You will need to emit a state (e.g: Sent success event). Afterward, the UI will listen to the changes and update it value according to the event you have emitted. See this Bloc example
GetX (As your code and question pointed out): GetX will acts a little bit different. you have multiple ways to implement this:
Using callbacks (passed at the start when calling the send otp function)
Declare a general dialog for your application ( this is the most used when it comes to realization) and calling show Dialog from Bloc
Using Rx. You will define a Reactive Variable for e.g final success = RxBool(true). Then the view will listen and update whenever the success changes.
controller.dart
class MyController extends GetxController {
final success = RxBool(false);
void sendOtp() async {
final result = await repository.sendOTP();
success.update((val) => {true});
}
}
view.dart
class MyUI extends GetView<MyController> {
#override
Widget build(BuildContext context) {
ever(controller.success, (bool success) {
// This will update things whenever success is updated
if (success) {
Get.dialog(AlertDialog());
}
});
return Container();
}
}

flutter refresh obs list of classes after changing data in it

I created a list and made it observable by using using getx obs
var msgChat = List.filled(1, UserMessagesModel(), growable: true).obs;
and if i append or add a new to it, it automatically updates in the UI but my problem is that if i using a single value inside the classes of the list it doesn't get updated until i do it for the second time before it updates
socket.on("msg_delivered", (del) {
OfflineDatabaseManager.instance.updateMsgReadData(del).then((value) {
uController.msgChat.forEach((el) {
if (el.msgId == del) {
print(el.msgId);
el.isRead = 'true';
}
});
});
});
my listview is wrap using an Obx widget which makes it work but it doesn't updated if a single value in the class of the list is changed please is there a way to like refresh the list or listview and also i can't use setState(() {}); because an not inside a Statefulbuilder but in a class which extends to getx
class SocketHandler extends GetxController {}
please how do i fix this issue. Thank. Also if you require more explanation then tell me to explain
you can use refresh method like this on your observable, after changing the value:
msgChat.refresh()
if this didn't work, it's probably because you didn't use Obs correctly
please share the Obs part if the problem remains so i can help

Load data asynchronously into ChangeNotifier model in Flutter

in my Flutter application I have a widget
class HomeScreen extends StatelessWidget
that uses a model
class HomeScreenModel extends ChangeNotifier
These two objects are tied together using a ChangeNotifierProvider.
When the application loads the HomeScreen widget I would like to call a custom init() function of HomeScreenModel to load asynchronously some data from the disk into the model and then notify the listener with notifyListeners() function. This should be done once.
What's the right place to call this init() function?
As far as I know, for a stateless widget there are no lifecycle functions called only once. I'm pretty sure, though, that the constructor of HomeScreenModel is called only once.
Is it safe to call the async HomeScreenModel.init() function from its own constructor?
Is there any best practice on how to asynchronously load data into a model implemented as a ChangeNotifier?
Thanks to all!
After a bit of searching and tests I choose to call the async init function from the HomeScreenModel constructor. So I have
HomeScreenModel(BuildContext context) {
var initFuture = init(context);
initFuture.then((voidValue) {
_log.d('init finished');
state = HomeScreenModelState.initialized;
notifyListeners();
});
}
and the init function prototype is
Future<void> init(BuildContext context) async
I have found that another way to do this is to use a StatefulWidget and call the async init from the
initState()
function. This function is called only once so is like the ChangeNotifier constructor.
As of now I'm not using StatefulWidgets because it seems to me that they create a sort of strong coupling between ui and business logic. So as of now the above solution seems fine to me.
I hope it can help someone
Another way to call this function would be to put the init() call into the create function of the provider.
runApp(ChangeNotifierProvider(
create: (context) {
var model = TestModel();
model.init();
return model;
},
child: TestApp()));

How to trigger setState() of any other Stateful Class?

I have two stateful states, ABCState and XYZState, I want to call setState() on ABCState from XYZState so that I can trigger a redraw of ABCState
class ABCState extends State<ABC> {
}
&
Class XYZState extends State<XYZ> {
// Call .setState() of ABCState
}
I tried to play around with keys, but somehow not able to do so. Appreciate your help
This is a bad idea.
But if you want to do it, you can :
Use GlobalKey, which allows to get state/context/widget
Use BuildContext of the children of your desired stateful widget. Using context.ancestorStateOfType(const TypeMatcher<MyStatefulWidget>());