How can I use GeoFire plugin to update my location in background - flutter

I am using this plugin to update my location in firebase. When the app is in foreground everything works perfectly but as soon as my app goes in background then the location update service stops,I tried using didChangeAppLifecycleState but I can't seem to get it to work,
This is my implementaion so far..
class HomeTabPage extends StatefulWidget {
#override
_HomeTabPageState createState() => _HomeTabPageState();
}
class _HomeTabPageState extends State < HomeTabPage >
with AutomaticKeepAliveClientMixin, WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.inactive ||
state == AppLifecycleState.detached) return;
final isBackground = state == AppLifecycleState.paused;
if (isBackground) {
print("### paused");
StreamSubscription < Position > backgroundStreamSubscription =
Geolocator.getPositionStream().listen((Position position) {
initPositionPoint = position;
Geofire.setLocation(
currentFirebaseUser.uid, position.latitude, position.longitude);
});
}
}

From Github issues #53 and #444 of the flutter-geolocator repo it seems that it doesn't support background location tracking. It seems that some folks have been using background_locator as an alternative, so you might want to look at that.

Related

Disposing Camera preview in CupertinoTabBar when switched in Flutter

I am building a QR code scanner app with a couple of tabs wrapped up within CupertinoTabBar in a CupertinoTabScaffold. I have a CupertinoTabController to take care of the switching between the tabs. One of this tabs has a CameraPreview widget from the Camera plugin of Flutter along with a proper dispose mechanism. However, whenever the tab are switched, the Camera stream still persists, causing the phone to heat up and also causes janky UX. Now I read that the BottomNavigationBar from Material widgets does not persist in this way. Any idea on how to achieve the same behaviour with CupertinoTabBar?
You can use the StatefulWidget for each a page of the tabs and then try to listening AppLifecycleState. Disponse controller if state inactive/paused.
In my case it's working fine.
class Example extends StatefulWidget {
#override
ExampleState createState() => ExampleState();
}
//Implement WidgetsBindingObserver to listen Lifecycle State
class ExampleState extends State<Example> with WidgetsBindingObserver {
late CameraController _controller;
...
...
#override
void initState() {
super.initState();
// Add Listener (Lifecycle State)
WidgetsBinding.instance!.addObserver(this);
}
Future<void> _setupController() async {
//todo setup/init controller
}
//Implements this method to listen Lifecycle State
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_controller.dispose();
_setupCameraAndControllerFuture = _setupController();
}
if (state == AppLifecycleState.inactive) {
_controller.dispose();
} else if (state == AppLifecycleState.paused) {
_controller.dispose();
}
}
#override
void dispose() {
// Remove Listener (Lifecycle State)
WidgetsBinding.instance!.removeObserver(this);
// dispose controller
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
...
...
);
}
}

"Multiple widgets used the same GlobalKey" error in flutter

I'm getting an error like the one in the picture. I'm confused because I'm not setting up GlobalKey on every page. I just made a GlobalKey on main.dart for this:
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
StreamController<bool> _showLockScreenStream = StreamController();
StreamSubscription _showLockScreenSubs;
GlobalKey<NavigatorState> _navigatorKey = GlobalKey();
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_showLockScreenSubs = _showLockScreenStream.stream.listen((bool show){
if (mounted && show) {
_showLockScreenDialog();
}
});
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_showLockScreenSubs?.cancel();
super.dispose();
}
// Listen for when the app enter in background or foreground state.
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// user returned to our app, we push an event to the stream
_showLockScreenStream.add(true);
} else if (state == AppLifecycleState.inactive) {
// app is inactive
} else if (state == AppLifecycleState.paused) {
// user is about quit our app temporally
} else if (state == AppLifecycleState.suspending) {
// app suspended (not used in iOS)
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
...
);
}
void _showLockScreenDialog() {
_navigatorKey.currentState.
.pushReplacement(new MaterialPageRoute(builder: (BuildContext context) {
return PassCodeScreen();
}));
}
}
I've tried to remove the GlobalKey _navigatorKey but the error still appears.
The error appears when switching pages. Is there anyone who can help me?
There are many kinds of Keys. But the GlobalKey allows access to the state of a widget (if it's a StatefulWigdet).
Then, if you use the same GlobalKey for many of them, there is a conflict with their States.
In addition, they must be of the same type due to its specification:
abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
// ...
void _register(Element element) {
assert(() {
if (_registry.containsKey(this)) {
assert(element.widget != null);
final Element oldElement = _registry[this]!;
assert(oldElement.widget != null);
assert(element.widget.runtimeType != oldElement.widget.runtimeType);
_debugIllFatedElements.add(oldElement);
}
return true;
}());
_registry[this] = element;
}
// ...
}
This fragment of code shows that in debug mode, there is an assertion for ensuring that there isn't any other GlobalState of the same type previously registered.

How to use didChangeAppLifecycleState with Flutter Hooks

I am using Flutter with hooks and I am trying to get the App Life Cycle State. I followed documentation and created new hook (code shown below) which works ok for all situations with one exception. When the application state becomes "paused", the hook does not return the value back to the widget. I am not clear what to do at this point. Someone suggested using Isolates but I don't see how that can help. Updating App Life Cycle is not compute expensive.
Please let me know what else I could do make this work.
Thanks
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
AppLifecycleState useAppLifecycleState() {
return use(const _LifeCycleState());
}
class _LifeCycleState extends Hook<AppLifecycleState> {
const _LifeCycleState();
#override
__LifeCycleState createState() => __LifeCycleState();
}
class __LifeCycleState extends HookState<AppLifecycleState, _LifeCycleState>
with WidgetsBindingObserver {
AppLifecycleState _state;
#override
void initHook() {
super.initHook();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
#override
AppLifecycleState build(BuildContext context) {
return _state;
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_state = state;
});
super.didChangeAppLifecycleState(state);
}
}
Thanks for your help.

Flutter | Stop music when minimised using audioplayers

In my app, I am playing music (local) in a loop, which plays continuously unless the user stops it. I am using audioplayers package.
Future playLoop(String filePath) async {
player.stop();
player = await cache.loop(filePath);
}
Currently, when app is minimised, the music is not getting stoped. The feature I want to implement is that when the app is minimised, it should stop playing music in the background.
Thanks in advance.
Solutions :
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
//stop your audio player
}else{
print(state.toString());
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
There are mainly 4 states for it:
resumed: The application is visible and responding to user input.
inactive: The application is in an inactive state and is not receiving
user input.
paused: The application is not currently visible to the user, not
responding user input, and running in the background.
detached: The application is still hosted on a flutter engine but is
detached from any host views.
The solution above is correct, but some steps are needed before to get it
1 add WidgetsBindingObserver to your class
class AnyClass extends StatefulWidgets {
_AnyClassState createState() => _AnyClassState();
}
class _AnyClassState extends State<AnyClass> with
WidgetsBindingObserver {
Widget build(BuildContext context) {
return ...
}
}
2 Now it will work, we can added the methods inside class
class _AnyClassState extends State<AnyClass> with
WidgetsBindingObserver {
// ADD THIS AppLifecycleState VARIABLE
late AppLifecycleState appLifecycle;
// ADD THIS FUNCTION WITH A AppLifecycleState PARAMETER
didChangeAppLifecycleState(AppLifecycleState state) {
appLifecycle = state;
setStae(() {});
if(state == AppLifecycle.paused) {
// IF YOUT APP IS IN BACKGROUND...
// YOU CAN ADDED THE ACTION HERE
print('My app is in background');
}
}
// CREATE INITSTATE AND DISPOSE METHODS
initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Widget build(BuildContext context) {
return ...
}
}
NOW IT WILL WORK FINE!

Flutter Capture event when app comes to Foreground from triggered Intent

I am trying to pull off some basic stuff here. Scenario: I am checking for GPS status on init() using isLocationServiceEnabled. If the GPS is off, I'm showing a popup that redirects to Location settings using AndroidIntent. If hit back without turning on the GPS, I want to capture the event when my app comes to foreground. I guessed it has to do with the lifecycle and tried like below, nothing gets print on the console
AppLifecycleState _notification;
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_notification = state;
print('onResumed called 1');
print(_notification);
});
if( state == AppLifecycleState.resumed){
print('onResumed called 2');
}
}
Am I missing something here?
Did you extend class with WidgetsBindingObserver like so:
class _WhateverWidget extends State<WhateverWidget> with WidgetsBindingObserver
and then initialize an instance like so:
#override void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
}
#override void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}