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();
}
Related
I am implementing the 'Firebase Cloud Messaging(FCM)' for app based notifications and getting the following error while trying to call sendLocalNotification function in _firebaseMessagingBackgroundHandler while app is in background.
An error occurred in your background messaging handler: No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
And below is the sample code,
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging().onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
void sendLocalNotification(RemoteMessage message) {
}
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
var myAppState = _MyAppState();
myAppState.sendLocalNotification(message);
}
As per the code you provided, the _MyAppState class is being created inside the _firebaseMessagingBackgroundHandler method. So, that means it does not have the context to use/access the Firebase instance.
Just create a MyApp instance using the .createState() function and then you can use that state to call the sendLocalNotification() method.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message)
async {
var myAppState = MyApp().createState();
myAppState._MyAppState().sendLocalNotification(message);
}
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
I have a function called control in the StateFull Widget. I want to run this function with WorkManager every 15 minutes.
How can I call the control function from the callbackDispatcher function?
I added a Stream statically to the Statefull widget and then listened to it but it didn't work.
HomeScreen.dart file
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
const taskKontrol = "control";
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Container();
}
#override
void initState() {
super.initState();
setupWorkManager();
}
void control() async
{
//... my code control is here
}
}
void setupWorkManager() async {
await Workmanager.initialize(callbackDispatcher, isInDebugMode: true);
Workmanager.registerPeriodicTask(taskKontrol, taskKontrol,
frequency: Duration(seconds: 10),
existingWorkPolicy: ExistingWorkPolicy.append
);
}
void callbackDispatcher() {
Workmanager.executeTask((taskName, inputData) async {
switch(taskName)
{
case taskKontrol:
// How can I call the control function from here?
print("control from workmanager");
break;
}
return Future.value(true);
});
}
For those who still looking for an answer:
From the official docs:
The callbackDispatcher needs to be either a static function or a top level function to be accessible as a Flutter entry point.
I had this same problem and I solved it by moving the function callbackDispatcher to the file: main.dart
Also, the code that initializes callbackDispatcher must be in main() before the App() widget loads.
To call your control code, create a class with static function control()
Note: You cannot call the widget's method from callbackDispatcher!
Reason: Widgets are UI bound. As long as the screen remains active, the widget that is visible remains active. Once you close the app or move on to next screen, the widgets' memory gets recycled. But this callbackDispatcher gets executed even when your app is closed. So, it has to be isolated from UI code.
Here's the code:
main.dart:
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
runApp(App());
}
void callbackDispatcher() {
Workmanager.executeTask((taskName, inputData) async {
switch(taskName)
{
case ScheduledTask.taskName:
ScheduledTask.control(); // calls your control code
break;
}
return Future.value(true);
});
}
class ScheduledTask {
const static String taskName = "control";
static void control() {
// add your control here
}
}
All you can do from HomeScreen widget is to call setupWorkManager() that schedules the task
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Container();
}
#override
void initState() {
super.initState();
setupWorkManager();
}
}
void setupWorkManager() async {
Workmanager.registerPeriodicTask(taskKontrol, taskKontrol,
frequency: Duration(minutes: 15),
existingWorkPolicy: ExistingWorkPolicy.append
);
}
Note: The minimum frequency for the recurring task is 15 minutes
I've started using flutter_bloc package instead of redux to try it out, but I'm not entirely sure how I'm going to call flutter bloc events when receiving things from native (Android/iOS). It was easier with redux because in my parent MyApp widget of my main.dart file, I passed in the redux store to a custom class I created, and dispatched methods from the said class (called MethodChannelHandler).
main.dart:
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final Store<AppState> store = Store<AppState>(
// ... redux stuff ...
);
#override
void initState() {
// sauce
MethodChannelHandler(store);
super.initState();
}
}
methodChannelHandler.dart:
class MethodChannelHandler {
Store<AppState> store;
MethodChannelHandler(this.store) {
methodChannel.setMethodCallHandler(_handleMethod);
}
// Handle method calls from native
Future _handleMethod(MethodCall call) async {
if (call.method == A_METHOD) {
store.dispatch("something from native")
}
}
}
NOTE: I'm inept when it comes to programming vocabulary so please, if possible, please give me a small snippet of example code like I have or link me to some GitHub repo I can refer to instead of giving me a block of text I'm probably not going to understand.
In very simple way it's look like this:
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider<SomeBloc>(
create: (_) {
final bloc = SomeBloc(); //Create bloc
MethodChannelHandler(bloc); //Add method handler
return bloc;
},
lazy: false,
child: Text("Content"),
);
}
}
class SomeBloc extends Bloc {
SomeBloc() : super(SomeInitState());
#override
Stream mapEventToState(event) async* {
if (event is SomeEvent) {
//Handle SomeEvent
}
}
}
class MethodChannelHandler {
final SomeBloc someBloc;
MethodChannelHandler(this.someBloc) {
methodChannel.setMethodCallHandler(_handleMethod);
}
// Handle method calls from native
Future _handleMethod(MethodCall call) async {
if (call.method == A_METHOD) {
someBloc.add(SomeEvent("something from native"));
}
}
}
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(() {});