My app structure is a little bit mess, but I have to add this patch first and then I'll restructure the entire logic. The thing is I first check if there's a firebase user, then if there is one I use StreamBuilder to get the current user profile from Firestore, then I have the _firebaseMessaging.configure method because onLaunch and onResume I use this callback:
void _navigateToGestorResevas(Map<String, dynamic> message, User currentUser) {
Navigator.push(context,
MaterialPageRoute(builder: (context) =>
GestorScreen(user: currentUser)));
}
Because I need to send the User to this screen where he fetch the message from firebase.
onResume this works fine, but onLaunch it goes to the screen and fetch the data but there are like 20 seconds where there are some kind of glitch. It switch like 20-30 times between two states where I have and no have snapshot data in this _initState func:
final snapshot = await _dbRef.child('mensajes').child(widget.user.id).once();
if (snapshot.value != null) {
setState(() {
hayMensajes = true;
});
final data = snapshot.value;
for (var entry in data.entries) {
Message message = Message.fromJson(entry.value);
setState(() {
message.add(message);
});
}
} else {
setState(() {
hayMensajes = false;
});
}
Anyone have an idea what am I doing wrong?
If I am not mistaken, there are some active issues about FCM onLaunch callback with flutter. Some of them are still not fixed. One of the problems most people had to face was that onLaunch callback being called multiple times. I don't know why it happened, but as in your case, you can possibly get rid of the issue by some temporary fixes.
If the same screen is getting pushed over and over again, and glitching, you can pop the stack until it reaches the one you meant to open and set a condition to push navigator only if the new route is different from the old one. Using the named routes,
Navigator.popUntil(context, ModalRoute.withName(routeName));
if (ModalRoute.of(context).settings.name != routeName) {
Navigator.pushNamed(context, routeName);
}
I am not sure if that was the problem you asked, but I hope at least my answer helps somehow.
Related
I am developing an app by combining riverpod and stream. However, I receive the same event twice. To avoid duplicate reception, the stream is listened to in initState. However, a duplicate event occurred.
I checked it by taking breakpoints in debug mode, and I saw that two identical events were raised in streamController almost at the same time.
//This is the code that listens to the stream.
#override
void initState() {
super.initState();
ToiletListViewModel toiletListViewModel =
ref.read(toiletListViewModelProvider.notifier);
TextViewModel textViewModel = ref.read(textViewModelProvider.notifier);
textViewModel.setTexts();
toiletListViewModel.uiEventStream.listen((event) {
event.when(
onLoading: _onLoading,
onError: _onError,
onSuccess: _onSuccess,
);
});
toiletListViewModel.getToiletListLocal();
toiletListViewModel.getToiletListFromRemote();
}
//This is the code that sends an event to the stream
Future getToiletListFromRemote() async {
_uiEventController.add(const ToiletListUiEvent.onLoading());//This event occurs twice at a time.
try {
List<Toilet> results =
await getToiletListFromRemoteUseCase(toiletListPage);
state = [...state, ...results];
_uiEventController.add(const ToiletListUiEvent.onSuccess());
saveToiletList(state);
} catch (e) {
e as DioError;
_uiEventController.add(ToiletListUiEvent.onError(e.message));
}
return state;
}
If I make a mistake and fire the event twice, shouldn't success or fail occur twice as well as loading? In the code above, only the loading event is fired twice with no difference of 1ms.
What could be the cause? I don't know at all. Thank you for your help.
sorry. This was a stupid mistake. toiletListViewModel.getToiletListLocal();
I was calling the same event here.
I want to ask if there is a future function who gives data from api and i want to get that data when i pressed the button and i want some particular data like id of user i am using function like
onPressed: () async {
var data = await getLogin()
if(data.id == 0){
Navigator.push(context,
MaterialPageRoute(builder: ((context) => Login())));
else {
Navigator.push(context,
MaterialPageRoute(builder: ((context) => Order())));
}
}
Can you tell me why it is taking time when i am pressing button to open new screen and can any one tell me the solution
it is taking time because it await for data. It is a async function and this await keyword wait until you didn't get the data back..
Problem is that if you not use await keyword then data.id throw a null safety error....
So solution is Future Provider... Call the api using the any state management solution like provider and navigate without checking the condition... Then update Ui after getting data from Provider..
I want to create user journey's using AWS Pinpoint. My mobile app is made with Flutter and using Amplify package to create events)
pubspec.yaml
amplify_flutter: ^0.2.4
amplify_analytics_pinpoint: ^0.2.4
I am calling this method to add the event
Future<void> logLogin(String userId) async {
await addEvent('_app.login', {PinPointAnalyticsKeys.userIdProperty: userId});
}
Future<void> addEvent(String eventName, Map<String, String> properties) async {
AnalyticsEvent event = AnalyticsEvent(eventName);
properties.forEach((key, value) {
event.properties.addStringProperty(key, value);
});
Amplify.Analytics.recordEvent(event: event);
await Amplify.Analytics.flushEvents();
}
Method handling the login:
Future<bool> performLogin(String username, String password) async {
//Code that handle login using email/password => userId
await _pinPoint.logLogin(userId);
}
My test journey: a user from a specific segment who does login should get an email right away.
I am having 2 issues:
Events get added to Pinpoint but after a delay (between 10 and 20min approximatively). Shouldn't it get added right away?
When event gets added (after the delay) no journey does get triggered, so no email sent, and the Pinpoint Journey Metrics doesn't change. What could be the reason?
Thank you all for your assistance, if I missed some details please let me know.
For ex.
I am on Screen-1 , push screen and goto Screen-2.
and then I replace Screen-2 with Screen-3
and now I pop that Screen-3 with pass some data
and come back to Screen-1
and I want refresh data in Screen-1
You can use something like this (but you must go from 3 to 2 and finally 1) to get some value then refresh it:
var result = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Screen2()));
if (result != null) {
setState(() {
valueYouChangeIt = result;
});
}
And then send this value back from Screen2 with:
_sendDataBack(BuildContext context) {
Navigator.pop(context, valueToSendBack);
}
Or if you want to refresh everything you can write _initState function where you initialize your data and then call _initState on result.
Said that, all this can be super tedious process, so if it's a small app with 2 to 5 screens it can be ok, but with more screens, like someone else commented, I'd prefer to use Provider.
I have an API that I'm checking, and when the response changes I need to send a notification to the user. I would like to know how to do this without FCM Push Notifications.
I'm using flutter-local-notifications and background fetch (https://github.com/transistorsoft/flutter_background_fetch) to do it. On the background fetch docs it says that background fetch will do your function once every 15 minutes, which is good enough for me.
This is my initPlatformState():
Future<void> initPlatformState() async {
// Load persisted fetch events from SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
String json = prefs.getString(EVENTS_KEY);
if (json != null) {
setState(() {
_events = jsonDecode(json).cast<String>();
});
}
// Configure BackgroundFetch.
BackgroundFetch.configure(
BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
forceReload: true,
startOnBoot: true,
),
_onBackgroundFetch)
.then((int status) {
print('[BackgroundFetch] SUCCESS: $status');
setState(() {
_status = status;
});
}).catchError((e) {
print('[BackgroundFetch] ERROR: $e');
setState(() {
_status = e;
});
});
// Optionally query the current BackgroundFetch status.
int status = await BackgroundFetch.status;
setState(() {
_status = status;
});
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
}
I'm assuming that what's in the function that gets called in the fetch isn't needed for the question.
I tried this on my phone and simulator, and I used Xcode -> Simulate Background Fetch and it ran properly. It also ran properly when I opened the app. Unfortunately, it didn't run after 15 minutes. Is there something I'm missing?
How would I change my code to make the background fetch to happen every 15 minutes?
Yes, I spent lot of time trying to solve this but was not successful so I switched, I guessed what you are trying to do is to handle your own notification without Firebase or from an API like me, well this is it, after my search I was able to do this with the help of work manager package. So easy to use and implement, try it.
https://pub.dev/packages/workmanager