How can I test a future function using bloc test - flutter

I'm new to bloc and currently learning bloc testing. The below code is
working fine in debug mode. However, I can't find anything to await the
function to test the current position/error.
Here is my bloc
class GoogleMapBloc extends Bloc<GoogleMapEvent, GoogleMapState> {
GoogleMapBloc() : super(const GoogleMapState()) {
on<CurrentLocationEvent>(_showCurrentPosition);
}
//This is the bloc logic
void _showCurrentPosition(
CurrentLocationEvent event, Emitter<GoogleMapState> emit) async {
try {
emit(state.copyWith(loadStatus: LoadStatus.loading));
Position position = await _getCurrentLocation();
final Marker marker = Marker(
markerId: const MarkerId('current_position'),
position: LatLng(position.latitude, position.longitude),
);
Set<Marker> markers = {};
markers.add(marker);
emit(state.copyWith(
loadStatus: LoadStatus.success,
currentPosition: position,
markers: markers));
} catch (e) {
//debugPrint("Exception ===> ${e.toString()}");
emit(state.copyWith(
loadStatus: LoadStatus.fail,
markers: {},
error: e.toString().substring(11)));
}
}
//This will fetch current user or error
Future<Position> _getCurrentLocation() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
throw Exception('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
throw Exception('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
throw Exception(
'Location permissions are permanently denied, we cannot request permissions.');
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
return await Geolocator.getCurrentPosition();
}
}
Here, I'm confused, as to how can I await the function without using
the wait as this is an async call.
void googleMapBloc() {
group("Google Map Bloc Testing", () {
blocTest<GoogleMapBloc, GoogleMapState>(
'Check location enabled',
build: () => GoogleMapBloc(),
act: (bloc) => bloc.add(const CurrentLocationEvent()),
wait: const Duration(milliseconds: 10000),
expect: () => <GoogleMapState>[
const GoogleMapState(loadStatus: LoadStatus.loading),
const GoogleMapState(
markers: {}, loadStatus: LoadStatus.fail, error: ""),
],
);
});
}
Also, how can I mock this for the api request

Related

Track user location globally in flutter (at background too)

I need to track user location at background in every screen of my app, i already implemented a method to take location with geolocator package, but it only works if i put the code in every app screen. Is it possible in flutter?
ps.: i tried to use workmanager, but the minimum interval is 15 minutes, and i need to receive the location in an interval of 3 seconds.
my actually code bellow:
getPosicaoAtual() async {
try {
Position posicao = await _posicaoAtual();
DbUtil.insert('local', {
'latitude': posicao.latitude,
'longitude': posicao.longitude,
});
_local = {
'latitude': posicao.latitude,
'longitude': posicao.longitude,
};
} catch (e) {
clearLocal();
_local['erro'] = e.toString();
}
notifyListeners();
}
Future<Position> _posicaoAtual() async {
LocationPermission permissao;
// Location location = new Location();
bool ativado = await Geolocator.isLocationServiceEnabled();
if (!ativado) {
// await location.requestService();
if (!await Geolocator.isLocationServiceEnabled()) {
return Future.error('Por favor, habilite a localização no smartphone');
}
}
permissao = await Geolocator.checkPermission();
if (permissao == LocationPermission.denied) {
permissao = await Geolocator.requestPermission();
if (permissao == LocationPermission.denied) {
return Future.error('Você precisa autorizar o acesso à localização');
}
}
if (permissao == LocationPermission.deniedForever) {
return Future.error('-1');
}
return await Geolocator.getCurrentPosition();
}
You should use https://pub.dev/packages/flutter_background_service
To run flutter code in background even app is terminated

Flutter - Get user GPS location and use throughout screens globally

How can I get the user's GPS (current address) on launch and then use that throughout the app where needed? I'd like to be able to use both options of the coordinates and written address being output. I'd like to have it be string data instead of text widget, so that then I can use it in other widgets where appropriate. Here's what I have so far, but I can't figure out how to incorporate this into my app properly and extract and implement that data.
Oh, also how to make it update periodically for when then user changes locations?
Any ideas?
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
class FindMe extends StatefulWidget {
initState() {
FindMe();
}
#override
_FindMeState createState() => _FindMeState();
}
class _FindMeState extends State<FindMe> {
String currentAddress = '';
late Position currentposition;
Future<String> _determinePosition() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
Fluttertoast.showToast(msg: 'Please enable Your Location Service');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
Fluttertoast.showToast(msg: 'Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
Fluttertoast.showToast(msg:'Location permissions are permanently denied, we cannot request permissions.');
}
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.bestForNavigation);
try {
List<Placemark> placemarks = await placemarkFromCoordinates(position.latitude, position.longitude);
Placemark place = placemarks[0];
setState(() {
currentposition = position;
currentAddress = "${place.locality}, ${place.postalCode}, ${place.country}";
});
} catch (e) {
print(e);
}
return currentAddress;
}
#override
Widget build(BuildContext context) {
_determinePosition();
return Text(
currentAddress,
style: const TextStyle(color: Colors.black),
);
}
}
You could use state-management solutions like blocor riverpod to do this.
With Riverpod one solution could look like this:
class LocationService {
Future<String> determinePosition(){...}
}
...
final locationServiceProvider = Provider((ref) => LocationService());
final positionProvider = FutureProvider((ref) => ref.watch(locationServiceProvider).determinePosition);
...
// in a consumer widget
build(BuildContext context, WidgetRef ref){
final position = ref.watch(positionProvider);
// do something with the position
}
You can then access the value in your position provider from different parts of your app.

How to fix Non-nullable instance when trying to get phones location

I'm trying to get the location of my phone with geolocation however the variable that stores location when you a press the button to get location has to be store as null at the beginning as I do not have the location of the phone yet however when I do that I get an error 'Non-nullable instance field 'currentposition' must be initialized' how to I fix this
this is my code
class _HomeState extends State<Home> {
String currentAddress = 'My Address';
Position currentposition;
void initState() {}
Future<Position> _determinePosition() async {
bool serviceEnabled; //check location service is enabled
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
Fluttertoast.showToast(msg: 'Please enable Your Location Service');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
Fluttertoast.showToast(msg: 'Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
Fluttertoast.showToast(
msg:
'Location permissions are permanently denied, we cannot request permissions.');
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
try {
List<Placemark> placemarks =
await placemarkFromCoordinates(position.latitude, position.longitude);
Placemark place = placemarks[0];
setState(() {
currentposition = position;
currentAddress =
"${place.locality}, ${place.postalCode}, ${place.country}";
print(currentposition);
});
} catch (e) {
print(e);
}
throw "";
}
If you declare a variable then you also have to initialize it by passing some value to it. But in any case you want to assign value later then you can define it as late making this change to your variable you can also make it nullable by using ?
late Position currentposition; //late initialize but not null
and nullable with late
late Position? currentposition; //late initialize nullable

Not able to get the Latitude and Longitude while using 'GeoLocator' package

I am trying to use 'GeoLocator' to retrieve Latitude & Longitude. However, not able to get any output?
Also will be helpful if someone can help me organize the 'geoLocator' package as a service in a separate dart file.
As suggested I tried changing the code, but still not able to get the geolocation. Also when I call the '_determinePosition()' getting "Instance of 'Future'
".
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//
Future<Position> _determinePosition() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
if (permission == LocationPermission.denied) {
return Future.error('Location permissions are denied');
}
}
return await Geolocator.getCurrentPosition();
}
#override
Widget build(BuildContext context) {
// TESTIST IF PRINT CAN WORK HERE
print(_determinePosition());
return Scaffold(
appBar: AppBar(
title: Text("Location"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
child: Text("Get location"),
onPressed: () {
// PRINTING IN THE CONSOLE
_determinePosition();
},
),
],
),
),
);
}
}
Try to add the Internet permission in addition to the location permissions per the documentation (https://pub.dev/packages/geolocator):
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Regarding getting "intsantce of a Future" instead of the result you need to await the call to _determinePosition() just like this:
onPressed: () async{
// PRINTING IN THE CONSOLE
await _determinePosition();
},
For using Geolocator as a service I recommend to use a dependency injection solution like Provider(https://pub.dev/packages/provider) or GetIt(https://pub.dev/packages/get_it)
Use below function which checks the permission and returns current position, so before getting location it's important to check whether we had required permission or not.
import 'package:geolocator/geolocator.dart';
/// Determine the current position of the device.
///
/// When the location services are not enabled or permissions
/// are denied the `Future` will return an error.
Future<Position> _determinePosition() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
return Future.error(
'Location permissions are denied');
}
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
return await Geolocator.getCurrentPosition();
}

How do I handle a future not returning?

I'm using a location plugin to get the current location of the device. However, on certain devices, await getLocation() never returns (there are also no errors in the debug console). How do I handle such an issue?
this is my code for getCurrentLocation()
import 'package:geolocator/geolocator.dart';
import 'location.dart';
/// Determine the current position of the device.
///
/// When the location services are not enabled or permissions
/// are denied the `Future` will return an error.
Future<Position> getCurrentLocation() async {
bool serviceEnabled;
LocationPermission permission;
Position position;
await reqLocation(); // requests turn on location
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.deniedForever) {
return Future.error(
'Location permissions are permantly denied, we cannot request permissions.');
}
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission != LocationPermission.whileInUse &&
permission != LocationPermission.always) {
return Future.error(
'Location permissions are denied (actual value: $permission).');
}
}
print('LOGIC');
position = await Geolocator.getCurrentPosition();
if (position == null) {
print('null');
} else {
print('LOCATION');
print(position);
}
return position;
}
Use a timeout for your future to handle this case: Flutter - Future Timeout
Use your future like this:
var result = await getCurrentLocation().timeout(const Duration(seconds: 5, onTimeout: () => null));
Now, your future runs for 5 seconds and if the operation is not complete yet, the future completes with null (because onTimeout returned null. You can use a different value as you like [Refer to the link above]).
Now check result. if null, the operation did not complete within specified time limit else you get your position value in result as usual if it managed to complete within the specified duration.