I'm new to Dart/Flutter and would like to build a simple app where a LinearProgressBar gets updated every second.
Without getting too much into the actual code, I have the following setup working.
A function that calculates the progress, based on passed time.
A LinearProgressBar showing the progress.
A periodic Timer recalculating the progress and updating the progress bar every second.
I debugprint 'tick' every time, the recalculation is done.
Everything is working as expected with one exception. The 'tick' keeps getting printed when I move the app in the background on my Android device.
On native Android, I would cancel my periodic Timer when the 'onPause' event is triggered.
Is there something similar in Flutter? All I could find was 'initState' and 'dispose'. Dispose, however, does not get called when moving the app to background.
I don't want the timer to keep ticking in the background.
On my research, I found this Stack Overflow question onresume-and-onpause-for-widgets-on-flutter. It's answer suggests using TickerProviderStateMixin.
I used it the following way.
class _BarItemState extends State<BarItem> with SingleTickerProviderStateMixin {
Ticker ticker;
num progress = 1.0;
#override
void initState() {
super.initState();
ticker = createTicker((duration) => setState(() {
debugPrint('tick');
progress = duration.inSeconds / 30;
}))
..start();
}
// other stuff omitted
}
It is working, but I'm still not satisfied.
The reason is, that the ticker callback is getting now called every few milliseconds instead of once a second. This seems to me like a waste of resources (I don't need a smooth animation), ... am I overcomplicating things?
Even if it seems that I don't need it for my use case, I still would like to know:
How to handle the onPause/onResume events on my own?
You can override the didChangeAppLifecycleState of the WidgetBindingObserver interface to receive notifications for app lifecycle changes.
There's sample code in this page
You can use lifecycle channel from SystemChannels.
Example:
SystemChannels.lifecycle.setMessageHandler((msg){
debugPrint('SystemChannels> $msg');
});
Output:
I/flutter ( 3672): SystemChannels> AppLifecycleState.paused
I/flutter ( 3672): SystemChannels> AppLifecycleState.resumed
Related
I want to know whether the call is disconnected or continued, and based on that I want to perform an action in the application.
Can anyone tell me how to check if the phone call is disconnected or not?
along with that I also want to know if it is received by the end-user or not
any kind of help will be appreciated.
thank you
I think you should take the steps I list below:
One line of code can make a phone call
Wait for any in-flight phone
Calls Watch everything that happens on the phone during a single Call or all
calls.
Keep track of the length of calls, errors, and
call drops.
Now let’s start
Install the plugin
Flutter_phone_state: ^0.5.8
Initiate a call
It is best to make calls from your app whenever you can. This is the best way to find where the call came from.
final phoneCall = FlutterPhoneState.makePhoneCall("480-555-1234");
The truth about a call comes from a PhoneCall object.
showCallInfo(PhoneCall phoneCall) {
print(phoneCall.status);
print(phoneCall.isComplete);
print(phoneCall.events);
}
PhoneCall.events can be read as a stream, and when the call is over, the plugin will gracefully close the stream. The plugin keeps an eye on all calls in progress and will eventually force any call to time out.
watchEvents(PhoneCall phoneCall) {
phoneCall.eventStream.forEach((PhoneCallEvent event) {
print("Event $event");
});
print("Call is complete");
}
You could also just wait until the call is over.
waitForCompletion(PhoneCall phoneCall) async {
await phoneCall.done;
print("Call is completed");
}
Accessing in-flight calls
In-flight calls can be accessed like this:
final `activeCalls = FutterPhoneState.activeCalls;`
Note that activeCalls is a copy of the calls at the time you called it. This copy cannot be changed. It won't be updated on its own.
Watching all events
You can watch all the events instead of just focusing on one call. We recommend using “FlutterPhoneState.phoneCallEventStream” because it includes our own tracking logic, call timeouts, failures, etc.
watchAllPhoneCallEvents() {
FlutterPhoneState.phoneCallEvents.forEach((PhoneCallEvent event) {
final phoneCall = event.call;
print("Got an event $event");
});
print("That loop ^^ won't end");
}
You can sign up to get the raw events if you want to. Keep in mind that there are only so many of these events.
watchAllRawEvents() {
FlutterPhoneState.rawPhoneEvent.forEach((RawPhoneEvent event) {
final phoneCall = event.call;
print("Got an event $event");
});
print("That loop ^^ won't end");
This is really common that i want to know an action is done and then do sth after that. for this we usually use events but i don't know how to use it in my case.
my scenario: There is an SplashScene which shows some animations for a constant time, after that time i navigate to my HomeScene. there are some other initializations which i don't want to be done unless SplashScene is gone and we r in HomeScene.
those initializations are in App component. and what im doing is that because the SplashScene animation time is constant i use a timeout to init things.
// Constants.ts
export const GlobalStaticData = {
initialDuration: 5000 // ms
}
// App
public componentDidMount() {
setTimeout(() => {
// initialize things
}, GlobalStaticData.initialDuration) // show dialogs after splash loading time
}
// SplashScene
private onAnimationEnd = () => {
NavigationActions.navigate(HomeScene)
}
but i know this is not good at all, i already experience sometimes which timing doesn't work as expected and things get initialized when app is still in SplashScene.
i was thinking for way to use events but i dont know how do that. what i want to listen to a value e.g called isSplashLoadCompleted in App component and in splash change that value when its works are done. then in App its event listener is called and initialing get started.
You can write a basic event system to subscribe to an event and then emit the event when certain thing happened on the other page:
const subscribers = {}
// Subscribe to loading your target page here Ex subsciber('home_load',()=>{ Your Logic })
const subscribe = (event,callback)=>{
if(subscribers[event] == null){ subscribers[event] = {}}
subscribers[event].push(callback)
}
const unSubscribe = (event,callback)=>{
....
}
// Call this inside your target page componentDidMount Ex: emitEvent('home_load',Some data or null)
const emitEvent = (event,data)=>{
if(subscribers[event]!=null && subscibers[event].length > 0){
for(const cb of subscribers[event]){
if(cb != null){cb()}
}
}
}
Sounds like the you want to trigger specific code when different scenes are loaded. I would recommend moving your animation/initialization logic into the compomentDidMount() for the respective scenes - SplashScene and HomeScene.
You're running into issues because your animation/initialization code is completely decoupled from your scenes. Couple the logic to the componentDidMount for these scenes and you won't have to worry about timing issues.
I'm trying to build a video calling app with Agora, I need to show acceptance screen like WhatsApp when user calling, if the app is exited i need to show calling screen when user is calling, I tried lot of thing but nothing works, I trying to do i flutter and but there is nothing much information on this,Please help me
First things first. You need to learn about some concepts before delving into your solution. Actually there isn't an out of the box solution.
You need to use a couple of things together:
Use push notifications to "wake up" your app:
https://pub.dev/packages/firebase_messaging
To start your app using push notifications refers to this post:
https://stackoverflow.com/a/48405551/4335775
Use CallKit (IOS) or ConnectionServices (Android) to show the upcoming call screen. By the day of this answer there are only a few packages to handle these things, here is one that can handle both platforms:
https://pub.dev/packages/flutter_callkeep
If you want a completely different thing and need to run some background process, there are bunch whole of things you should know first. I suggest beginning here: https://flutter.dev/docs/development/packages-and-plugins/background-processes
Here is a usefull package to work with background processes that should be constantly running:
https://pub.dev/packages/background_fetch
Currently there are two packages which provides integration for agora.io:
https://pub.dev/packages/agora_rtc_engine (for Agora Real Time Communication, vídeo call included)
https://pub.dev/packages/agora_rtm for real-time messaging
I hope this can help you.
You can try WorkManager plugin.
You can register an call back function to the os when the app is closed.
const myTask = "syncWithTheBackEnd";
void main() {
Workmanager.initialize(callbackDispatcher);
Workmanager.registerOneOffTask(
"1",
myTask, //This is the value that will be returned in the callbackDispatcher
initialDelay: Duration(minutes: 5),
constraints: WorkManagerConstraintConfig(
requiresCharging: true,
networkType: NetworkType.connected,
),
);
runApp(MyApp());
}
void callbackDispatcher() {
Workmanager.executeTask((task) {
switch (task) {
case myTask:
print("this method was called from native!");
break;
case Workmanager.iOSBackgroundTask:
print("iOS background fetch delegate ran");
break;
}
//Return true when the task executed successfully or not
return Future.value(true);
});
}
Maybe this can help you.
The complete article medium article
After reading the documentation on Spotify's Android Media Notifications API, https://beta.developer.spotify.com/documentation/android-sdk/guides/android-media-notifications/, I successfully managed to receive the notifications metadata and it is displayed properly on my app.
However, the notifications metadata is only updated when the queue changes, when the track changes, and when playback is changed, so unless one of these three actions happens, the "positionInMs" intent extra isn't sent.
As of right now as a workaround I am simply starting a timer using the time the intent was sent, the last known playback position, and the track duration to track current playback position.
This seemed to work at first, but after further testing I've realized that the timer I set can go out of sync, if the track the user is listening to freezes because of a slow internet connection.
Any ideas to properly track the playback position, while accounting for a slow internet connection? Or are there any alternatives I should look into?
I understand that this question is rather old, but I am going to answer anyway if anyone else comes across it.
I recommend constantly querying Spotify to get the playback position. One way you can do this is by using a timer and querying Spotify every given time frame. The below example queries Spotify every 100ms. If you want to reduce/increase the numbers of queries, you can simply use stopwatch.setClockDelay() and provide your required time
For instance, you can use this timer library
implementation 'com.yashovardhan99.timeit:timeit:1.2.0'
Then use the following code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify);
Stopwatch stopwatch = new Stopwatch();
stopwatch.setOnTickListener(this);
stopwatch.start();
}
#Override
public void onTick(Stopwatch stopwatch) {
Data.getAndroidSpotifyAppRemote().getPlayerApi().getPlayerState().setResultCallback(new CallResult.ResultCallback<PlayerState>() {
#Override
public void onResult(PlayerState playerState) {
Log.d("TAG", playerState.playbackPosition);
}
});
}
Don't forget to add the following code at the top of your class:
implements Stopwatch.OnTickListener
I have being upgrading an application to use the new Mobile Android GNSK but I have noticed that using the new MusicID-Stream is a little bit tricky. If the "identifyAlbumAsync" method get executed before the "audioProcessStart" method(since this need to be executed in a different thread), the application just crashes. In the Gracenote Demo application, the "audioProcessStart" method is continuously running so there is no need to synchronize its execution with the "identifyAlbumAsync" method call. Is it the way it is supposed to be used? It will be convenient if the application didn't crashed at least when the methods are not executed in order. Also in our application, we don't want to have the "audioProcessStart" method continuously like it is done in the demo application. We only want to run the "audioProcessStart" method when the user request identification and when the song playing gets identified , we want to stop the audio processing by calling "audioProcessStop". Is there an easy way to do this? Right now, we are getting the Thread where "identifyAlbumAsync" is running to sleep for 2 seconds in order to make sure that the Thread where the "audioProcessStart" method is supposed to run has time to get executed. Thank you in advance for your prompt response
In the upcoming 1.2 release, IGnMusicIdStreamEvents includes a callback that signals audio-processing has started, and an ID can be synced with this, e.g.:
#Override
public void musicIdStreamProcessingStatusEvent( GnMusicIdStreamProcessingStatus status, IGnCancellable canceller ) {
if (GnMusicIdStreamProcessingStatus.kStatusProcessingAudioStarted.compareTo(status) == 0) {
try {
gnMusicIdStream.identifyAlbumAsync();
} catch (GnException e) { }
}
}
Thanks for the feedback, you're right about this issue. Unfortunately right now sleeping is the best solution. But we are adding support for an explicit sync event in an upcoming release, please stay tuned.