-1

I am writing an app in flutter and need to send the users location as stream to my backend.After using the singleton as suggested in comment the logout works and it stops the stream,but after loging back streaming is not working. I initialize my location stream by calling it in main:

Main:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  LocationService().locationStream;
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Semdatex Patient Portal',
      debugShowCheckedModeBanner: false,
      //theme for all screens
      theme: ThemeData(
        primarySwatch: Colors.blue,
        primaryColor: Colors.blue[100],
      ),
      //all possible routes within the app
      initialRoute: '/loginScreen',
      routes: {
        '/': (context) => const InitialSite(),
        '/chat': (context) => const Chat(),
        '/chatPage': (context) => const ChatPage(),
        '/multipleChoice': (context) => const MultipleChoice(),
        '/scale': (context) => const Scale(),
        '/endScreen': (context) => const EndScreen(),
        '/singleChoiceNew': (context) => const SelectionWindow(),
        '/loginScreen': (context) => const LoginScreen(),
        '/registerScreen': (context) => const RegisterScreen(),
      },
    );
  }
}

LocationService:

import 'dart:async';

import 'package:location/location.dart';
import 'package:sdx_patient_portal/Datamodel/user_location.dart';

class LocationService {
        static final LocationService _instance = LocationService._internal();
  factory LocationService() => _instance;

  LocationService._internal() {
    getLocationOnchange();
  }
  var location = Location();
  final StreamController<UserLocation> _locationController =
      StreamController<UserLocation>.broadcast();

  StreamSubscription<LocationData>? listen;

  Stream<UserLocation> get locationStream => _locationController.stream;
    
  void getLocationOnchange() async {
    location.requestService().then((value) => {
          location.requestPermission().then((permissionStatus) {
            if (permissionStatus == PermissionStatus.granted) {
              location.enableBackgroundMode(enable: true);
              location.changeSettings(
                  interval: 10000, accuracy: LocationAccuracy.high);
              listen = location.onLocationChanged.listen((locationData) {
                _locationController.add(UserLocation(
                    locationData.latitude,
                    locationData.longitude,
                    locationData.altitude,
                    locationData.time));
              });
            }
          })
        });
  }

  StreamSubscription<LocationData>? getListener(){
    return listen;
  }

  void dipose(){
    listener?.cancel();
    listener = null;
  }
}

Here is the log in part in which I call the post after user has been loged in successfully:

class LoginScreen extends StatefulWidget {
  const LoginScreen({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _State();
}

class _State extends State<LoginScreen> {
  TextEditingController nameController = TextEditingController();
  TextEditingController passwordController = TextEditingController();

  final _formKey = GlobalKey<FormState>();
  User user = User("", "");
  
  String url = "http://10.0.2.2:9173/mob/login";

  Future save() async {
    var res = await http.post(Uri.parse(url),
        headers: {'Content-Type': 'application/json'},
        body: json.encode({'email': user.email, 'password': user.password}));
    if (res.body.isEmpty) {
      print('login wasn\'t successfull');
      _showMyDialog();
    } else {
      var data = json.decode(res.body);
      if (data["patientNumber"] == null) {
        _showMyDialog();
      } else {
        await savePatientNumber(data["patientNumber"], data["lastAccess"]);
        final prefs = await SharedPreferences.getInstance();
        await prefs.setBool('loggedIn', true);
        //LocationService().getListener()?.resume();
        LocationService().locationStream;
        startLocationStream();
        Navigator.pushNamed(context, '/');
      }
    }
  }

I call the following POST after log in:

postLocation() async {
  var patient = await getPatient();
  var patNumber = patient[0]["PatNum"];
  var l = (LocationService().locationStream);
const String url = "http://10.0.2.2:9173/mob/location";

  UserLocation locationData;
  l.listen((event) {
    locationData = UserLocation(
        event.latitude, event.longitude, event.altitude, event.time);
    http.post(Uri.parse(url),
        headers: {'Content-Type': 'application/json'},
        body: json.encode({
          'latitude': locationData.latitude,
          'altitude': locationData.altitude,
          'longitude': locationData.longitude,
          'time': locationData.time,
          'patientNumber': patNumber
        }));
  });
}

Then I call cancle the listener by signing out:

void signOut(context) async {
    LocationService().dipose();
  Navigator.of(context).pushAndRemoveUntil(
      MaterialPageRoute(builder: (context) => const LoginScreen()),
      (route) => false);
}

Hoe can I get controll over the state of the app and rerun the services after re-login?

Iman
  • 769
  • 1
  • 13
  • 51
  • 1
    Stab in the dark here, but it don't look like LocationService() is a singleton so when you signout and call LocationService().getListener()?.cancel(); it actually creates a new one leaving the old running. Check https://stackoverflow.com/a/12649574/11582192 to make it a singleton – Per.J Jun 21 '22 at 11:06
  • thanks. It solves the logout problem, but after loging in back the location is not sending any more. I changed the code accordingly – Iman Jun 21 '22 at 12:40
  • The flow is not clear from the pasted code, did you use your debugger to confirm that the flow is the one you are expecting? For example, did you check if after signing out and back in you are re-calling `LocationService().locationStream; startLocationStream();` – cksrc Jun 30 '22 at 14:56

1 Answers1

0

It is hard to follow as part of the code missing, but something like this could work.

Call startListeningLocation when a user logs in and stopListeningLocation when logs out.

class LocationService {
 static final LocationService _instance = LocationService._internal();
  factory LocationService() => _instance;

  LocationService._internal();
  
  final _location = Location();
  final StreamController<UserLocation> _locationController =
      StreamController<UserLocation>.broadcast();

  StreamSubscription<LocationData>? _locationSubscription;

  Stream<UserLocation> get locationStream => _locationController.stream;
    
  void startListeningLocation() {
    _location.requestService().then((value) => {
          _location.requestPermission().then((permissionStatus) {
            if (permissionStatus == PermissionStatus.granted) {
              _location.enableBackgroundMode(enable: true);
              _location.changeSettings(
                  interval: 10000, accuracy: LocationAccuracy.high);
              _locationSubscription = _location.onLocationChanged.listen((locationData) {
                _locationController.add(UserLocation(
                    locationData.latitude,
                    locationData.longitude,
                    locationData.altitude,
                    locationData.time));
              });
            }
          })
        });
  }
  
  void stopListeningLocation() {
     _locationSubscription?.cancel();
  }
}
user18309290
  • 5,777
  • 2
  • 4
  • 22