Your provided examples are excellent, thanks to them i managed to complete 90% of my project. I chose to follow your implementation because of your brilliant examples and documentation.
Can you possibly add an example or guide me in the right direction of how to listen for location updates when using the "location" plugin. Reading through your existing examples i found it easy to get the current location, but i'm lost when it comes to listening for location changes.
So far this is the location helper i'm using:
import 'dart:async';
import 'package:location/location.dart';
class LocationStore {
Future<LocationData> getCurrentLocation() async {
var location = new Location();
LocationData currentLocation;
try {
currentLocation = await location.getLocation();
print(currentLocation.latitude);
print(currentLocation.longitude);
return currentLocation;
} catch (e) {
throw Exception(e);
}
}
Future<StreamSubscription> listenForUserLocation() async {
var location = new Location();
return location.onLocationChanged().listen((LocationData currentLocation) {
print(currentLocation.latitude);
print(currentLocation.longitude);
return currentLocation;
});
}
}
With bloc files:
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:location/location.dart';
import 'package:meta/meta.dart';
abstract class LocationState extends Equatable {
const LocationState();
@override
List<Object> get props => [];
}
class LocationEmptyState extends LocationState {}
class LocationLoadingState extends LocationState {}
class LocationLoadedState extends LocationState {
final LocationData location;
LocationLoadedState(this.location);
@override
List<Object> get props => [location];
}
class LocationListeningState extends LocationState {
final StreamSubscription<LocationData> locationSubscription;
LocationListeningState(this.locationSubscription);
@override
List<Object> get props => [locationSubscription];
}
class LocationErrorState extends LocationState {
final dynamic error;
const LocationErrorState({@required this.error});
@override
List<Object> get props => [error];
@override
String toString() => 'location error { error: $error }';
}
import 'package:equatable/equatable.dart';
abstract class LocationEvent extends Equatable {
const LocationEvent();
@override
List<Object> get props => [];
}
class GetCurrentLocation extends LocationEvent {}
class StartListeningForCurrentLocation extends LocationEvent {}
class UpdatedCurrentLocation extends LocationEvent {}
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:location/location.dart';
import 'package:sasos/stores/location_store.dart';
import './bloc.dart';
class LocationBloc extends Bloc<LocationEvent, LocationState> {
@override
LocationState get initialState => LocationEmptyState();
@override
Stream<LocationState> mapEventToState(
LocationEvent event,
) async* {
if (event is GetCurrentLocation) {
yield* _mapToGetCurrentLocationState();
} else if (event is StartListeningForCurrentLocation) {
yield* _mapToStartListeningLocationState();
} else if (event is UpdatedCurrentLocation) {
}
}
Stream<LocationState> _mapToGetCurrentLocationState() async* {
yield LocationLoadingState();
try {
final LocationData locationData = await LocationStore().getCurrentLocation();
yield LocationLoadedState(locationData);
} catch (error) {
yield LocationErrorState(error: error);
}
}
Stream<LocationState> _mapToStartListeningLocationState() async* {
yield LocationLoadingState();
try {
final StreamSubscription subscription = await LocationStore().listenForUserLocation();
yield LocationListeningState(subscription);
} catch (error) {
yield LocationErrorState(error: error);
}
}
}
With UI file
import 'package:flutter/material.dart';
import 'location_form.dart';
class LocationScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Locaiton')),
body: LocationForm(),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sasos/blocs/location/bloc.dart';
class LocationForm extends StatefulWidget {
@override
State<LocationForm> createState() => _LocationFormState();
}
class _LocationFormState extends State<LocationForm> {
@override
Widget build(BuildContext context) {
_onGetLocationPressed() {
BlocProvider.of<LocationBloc>(context).add(GetCurrentLocation());
}
_onGetLocationListenPressed() {
BlocProvider.of<LocationBloc>(context).add(StartListeningForCurrentLocation());
}
return BlocListener<LocationBloc, LocationState>(
listener: (context, state) {
if (state is LocationErrorState) {
Scaffold.of(context).showSnackBar(SnackBar(content: Text('${state.error}'), backgroundColor: Colors.red));
}
},
child: BlocBuilder<LocationBloc, LocationState>(
builder: (context, state) {
return Form(
child: Column(
children: [
RaisedButton(
onPressed: state is! LocationLoadingState ? _onGetLocationPressed : null,
child: Text('Get Location'),
),
RaisedButton(
onPressed: state is! LocationLoadingState ? _onGetLocationListenPressed : null,
child: Text('Listen for Location'),
),
Container(child: state is LocationLoadingState ? CircularProgressIndicator() : null),
Container(child: state is LocationLoadedState ? Text(state.location.toString() ?? '') : null),
],
),
);
},
),
);
}
}
Hi @Theunodb 馃憢
Thanks for opening an issue as well as for the positive feedback!
I'll try to have an example created either later today or tomorrow 馃憤 In the meantime, you should refactor your bloc to add new events in response to location data from the subscription:
locationSubscription?.cancel();
locationSubscription = LocationStore().userLocation().listen((location) => add(LocationChanged(location)));
Then in mapEventToState you can just handle a LocationChangedEvent by yielding a new state with the latest location (passed via the event).
One thing I would like to add is that I would recommend injecting the LocationProvider/LocationService into the bloc as a dependency instead of creating new instances of it within the bloc. The way you currently have it set up makes it really tough to unit test the bloc code because you can't easily inject a Mock/Fake LocationProvider.
Hope that helps and I'll create a complete example in the next few days 馃憤
Thank you for the feedback! Yes your suggestions makes sense, I'm looking forward to your example!
@Theunodb I've put together a gist. Closing for now but feel free to comment if you have any questions/feedback. Thanks!
Most helpful comment
Hi @Theunodb 馃憢
Thanks for opening an issue as well as for the positive feedback!
I'll try to have an example created either later today or tomorrow 馃憤 In the meantime, you should refactor your bloc to add new events in response to location data from the subscription:
Then in
mapEventToStateyou can just handle aLocationChangedEventby yielding a new state with the latest location (passed via the event).One thing I would like to add is that I would recommend injecting the LocationProvider/LocationService into the bloc as a dependency instead of creating new instances of it within the bloc. The way you currently have it set up makes it really tough to unit test the bloc code because you can't easily inject a Mock/Fake LocationProvider.
Hope that helps and I'll create a complete example in the next few days 馃憤