I'm trying to detect whether Google Maps app is installed on iOS, and if so, launch it, if not, launch Apple Maps. Here is what I have so far, but on my phone with Google Maps installed, it isn't detecting it and launching appropriately.
Any ideas?
import 'package:url_launcher/url_launcher.dart';
_launchMaps() async {
String googleUrl =
'comgooglemaps://?center=${trip.origLocationObj.lat},${trip.origLocationObj.lon}';
String appleUrl =
'https://maps.apple.com/?sll=${trip.origLocationObj.lat},${trip.origLocationObj.lon}';
if (await canLaunch("comgooglemaps://")) {
print('launching com googleUrl');
await launch(googleUrl);
} else if (await canLaunch(appleUrl)) {
print('launching apple url');
await launch(appleUrl);
} else {
throw 'Could not launch url';
}
}
I pulled the url scheme from here: How would I be able to open google maps when I press a button in my app?
you can install the packege url_launcher and use the code down below:
import 'package:url_launcher/url_launcher.dart';
class MapUtils {
MapUtils._();
static Future<void> openMap(double latitude, double longitude) async {
String googleUrl = 'https://www.google.com/maps/search/?api=1&query=$latitude,$longitude';
if (await canLaunch(googleUrl)) {
await launch(googleUrl);
} else {
throw 'Could not open the map.';
}
}
}
Now you can open google maps in your app just call this method:
MapUtils.openMap(-3.823216,-38.481700);
I found my issue: this needs to be in the plist file. The code in the question above is fine. (The SO answer referenced in the question only mentioned the "comgooglemaps" string.)
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
</array>
Docs: https://developers.google.com/maps/documentation/ios-sdk/start#step_7_declare_the_url_schemes_used_by_the_api
Do it this way
Full code is given below
static void navigateTo(double lat, double lng) async {
var uri = Uri.parse("google.navigation:q=$lat,$lng&mode=d");
if (await canLaunch(uri.toString())) {
await launch(uri.toString());
} else {
throw 'Could not launch ${uri.toString()}';
}
}
1) in pubspec.yaml
dependencies:
flutter:
sdk: flutter
...
url_launcher: ^5.7.8
2) Import wherever you want to use
import 'package:url_launcher/url_launcher.dart';
3) final you call
onPressed: () {
navigateTo(location.lat, location.lng);
},
If you don't have the actual latlong, you can simply pass an address to Google Maps.
void launchMap(String address) async {
String query = Uri.encodeComponent(address);
String googleUrl = "https://www.google.com/maps/search/?api=1&query=$query";
if (await canLaunch(googleUrl)) {
await launch(googleUrl);
}
}
Of course, the more information you have in the address, the more accurate the search will be. Exactly the same as looking for something on the actual Google Maps page or app.
By the way, you need to url-encode the address before adding it to the URL, to support special characters like spaces. It's only needed for iOS, but hey, we want to develop for all environments out there.
using url launcher
in yaml file: url_launcher: ^5.0.2 last
then you can use this method to open google maps centered to the provided lat and long
more info to maps intent from docs [here][2]
launchMap({String lat = "47.6", String long = "-122.3"}) async{
var mapSchema = 'geo:$lat,$long';
if (await canLaunch(mapSchema)) {
await launch(mapSchema);
} else {
throw 'Could not launch $mapSchema';
}
}
If you want to navigate with directions you can just create a url with source and destination co-ordinates and other coordinates to add as stops.
Steps:
Install url_launcher plugin
write a code like below.
_launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
const url ='https://www.google.com/maps/dir/?api=1&origin=43.7967876,-79.5331616&destination=43.5184049,-79.8473993&waypoints=43.1941283,-79.59179|43.7991083,-79.5339667|43.8387033,-79.3453417|43.836424,-79.3024487&travelmode=driving&dir_action=navigate';
_launchURL(url);
static Future<void> openMap(BuildContext context, double lat, double lng) async {
String url = '';
String urlAppleMaps = '';
if (Platform.isAndroid) {
url = 'https://www.google.com/maps/search/?api=1&query=$lat,$lng';
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {
throw 'Could not launch $url';
}
} else {
urlAppleMaps = 'https://maps.apple.com/?q=$lat,$lng';
url = 'comgooglemaps://?saddr=&daddr=$lat,$lng&directionsmode=driving';
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else if (await canLaunchUrl(Uri.parse(urlAppleMaps))) {
await launchUrl(Uri.parse(urlAppleMaps));
} else {
throw 'Could not launch $url';
}
}
}
import 'package:url_launcher/url_launcher.dart';
static void launchMapsUrl(
sourceLatitude,
sourceLongitude,
destinationLatitude,
destinationLongitude) async {
String mapOptions = [
'saddr=$sourceLatitude,$sourceLongitude',
'daddr=$destinationLatitude,$destinationLongitude',
'dir_action=navigate'
].join('&');
final url = 'https://www.google.com/maps?$mapOptions';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
} }
Here you can use this function directly and pass the required parameters and also import this package https://pub.dev/packages/url_launcher/
As follow-up to Roc Boronat's post, the following code can be used for launching the platform specific map application.
Future<void> launchMapUrl(String address) async {
String encodedAddress = Uri.encodeComponent(address);
String googleMapUrl = "https://www.google.com/maps/search/?api=1&query=$encodedAddress";
String appleMapUrl = "http://maps.apple.com/?q=$encodedAddress";
if (Platform.isAndroid) {
try {
if (await canLaunch(googleMapUrl)) {
await launch(googleMapUrl);
}
} catch (error) {
throw("Cannot launch Google map");
}
}
if (Platform.isIOS) {
try {
if (await canLaunch(appleMapUrl)) {
await launch(appleMapUrl);
}
} catch (error) {
throw("Cannot launch Apple map");
}
}
For more information regarding the query parameters in Apple Maps URL, please visit this link.
Edit (7th Aug, 2022): This code will work upto version 6.0.20 of the url_launcher plugin. I could not get it to work after this version as I was getting an ERR_UNKNOWN_URL_SCHEME error when trying to launch Google Maps using canLaunchUrl and launchUrl methods using the versions of the plugin above 6.0.20 and 6.0.20. It works only with the deprecated methods (canLaunch and launch). Just a heads up if anyone wants to try this code snippet.
with 'url_launcher 6.1.0' + physical address instead of lat & lon,
void _pushMap(String address) async {
String query = Uri.encodeComponent(address);
String googleUrl = "google.navigation:q=$query";
Uri googleUri = Uri.parse(googleUrl);
if (await canLaunchUrl(googleUri)) {
await launchUrl(googleUri);
}
}
This will send an implicit Intent to open related apps including google maps.
Haven't tested on iOS devices.
tldr: I think there's an error in the library and canLaunch sometimes returns false even if the url can be launched.
I was trying to open a google maps link (https://goo.gl/maps/mHGzrGUhUHrQByAm8) the same way I do for another link from my app, but for whatever reason canLaunch always returned false.
So now I launch in a try catch block to make sure it doesn't crash my app, and it's working.
try {
launch(url);
} catch (error, stack) {
// log error
}
you can install the packege url_launcher and use the code down below:
This is the latest code as per
url_launcher: 6.1.6
canLaunch();
launch(); these methods has been deprecated now.
class GoogleMapUtils {
GoogleMapUtils._();
static Future<void> openMapApp(double latitude, double longitude) async {
Uri googleUrl = Uri.parse('https://www.google.com/maps/search/?api=1&query=$latitude,$longitude');
if (await canLaunchUrl(googleUrl)) {
await launchUrl(googleUrl);
} else {
throw 'Unable open the map.';
}
}
}
Use plugin:
intent: ^1.4.0
Try the following code:
static void navigateTo(double lat, double lng) async {
var uri = Uri.parse("google.navigation:q=$lat,$lng&mode=c");
android_intent.Intent()
..setAction(android_action.Action.ACTION_VIEW)
..setData(uri)
..setPackage("com.google.android.apps.maps")
..startActivity().catchError((e) => print(e));
}
Note: Only works on Android devices
Install url_launcher package
use the below function
void launchMap() async {
Uri googleUrl = Uri.parse('https://www.google.com/maps/search/?api=1&query=Googleplex');
if (await canLaunchUrl(googleUrl)) {
await launchUrl(googleUrl, mode:LaunchMode.externalApplication);
}
}
Using url luncher with navigation open by default.
Sample code - if app installed it will open in app otherwise open in any browser
///launch map
Future<void> openMap(double latitude, double longitude) async {
String mapUrl = '';
if (Platform.isIOS) {
mapUrl =
'https://maps.apple.com/?daddr=$latitude,$longitude';
} else {
mapUrl =
'https://www.google.com/maps/dir/?api=1&destination=$latitude,$longitude&travelmode=driving';
}
if (await canLaunchUrl(Uri.parse(mapUrl))) {
await launchUrl(Uri.parse(mapUrl),mode: LaunchMode.externalApplication);
} else {
throw 'Could not open the map.';
}
}
if you like to open google map in apple devices add this code in info.plist
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
</array>
<key>LSApplicationQueriesSchemes</key>
For other queries
Google url params example
https://www.google.com/maps/dir/?api=1
&origin=$latitude,$longitude
&destination=$latitude,$longitude
&travelmode=driving
&dir_action=navigate
Apple
q= query
saddr = starting point for directions
daddr = destination point for directions
dirflg = The transport type
Related
I am trying to add this response data to a zip object and later calling the createZip() method to download file as a zip file. Problem is my file getting downloaded first and the response is coming back. If I try to run the function again then right file is getting downloaded because in the previous API call I already got the response data.
Can anyone help me with this. I am new to angular and don't know how to use async/await properly.
zipFile = new JSZip();
exportIcsReportInJSONFormat() {
this.icsService.getICSReport()
.subscribe(response => {
console.log("JSONFile",response)
this.jsonFile = response;
this.zipFile.file("ics-report_.json", response, {binary:true});
});
To create zip file and download.
createZip() {
this.zipFile.generateAsync({type:"blob"})
.then(function(content) {
saveAs(content, "example.zip");
});
}
You can use the async/await pattern with Promises, something like this:
zipFile = new JSZip();
async mapZip() {
try {
var response = await this.exportIcsReportInJSONFormat();
console.log("JSONFile", response)
this.jsonFile = response;
this.zipFile.file("ics-report_.json", response, { binary: true });
var content = await this.zipFile.generateAsync({ type: "blob" });
saveAs(content, "example.zip");
}
catch {
...
}
}
exportIcsReportInJSONFormat() {
this.icsService.getICSReport().toPromise();
}
So I have a Flutter app which I'm trying to save some data locally as a JSON file. I'm doing this using dart:convert and jsonEncode and jsonDecode and a Dart Map, but for some reason when I save the Map to a file using file.writeAsString(jsonEncode(map)), it seems to be adding a phantom end bracket to the end like so (not consistently either it doesn't seem):
{
"posts_in_progress":
{
"https://dts.podtrac.com/redirect.mp3/chtbl.com/track/9EE2G/pdst.fm/e/rss.art19.com/episodes/67461d56-cdd8-4b03-a957-3edb110232d4.mp3":73035
}
}
}
This then causes an error when trying to read and decode this file with jsonDecode(file.readAsString()) because of the extra bracket.
What is causing this extra bracket? And how can I prevent this from happening or handle for the extra bracket when its there on the decoding side?
Edit:
LocalServices file which handles all the local data json storage. It doesn't seem to always happen and be easily reproducible, but it has happened multiple times (before I was just handling the local file saving and reading within other methods, but tried to create this local services class to help fix it, still haven't figured out what causes it tho).
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
class LocalService {
bool _isInitialized = false;
Map data;
String filepath;
String filename = 'local_data.json';
File file;
initialize() async {
filepath = await getApplicationDocumentsDirectory().then((directory) => directory.path);
file = await File('$filepath/$filename').exists() ? File('$filepath/$filename') : null;
if(file != null) {
print('Local Data File Exists, start decoding... \n${await file.readAsString()}');
data = jsonDecode(await file.readAsString());
print('Decoding complete!');
} else {
file = new File('$filepath/$filename');
data = new Map<String, dynamic>();
}
_isInitialized = true;
}
update() async {
if(!_isInitialized) {
await this.initialize();
}
try {
data = jsonDecode(await file.readAsString());
} catch (e) {
print('Error Updating file: $e');
}
}
Future<void> setData(String key, dynamic value) async {
if(!_isInitialized) {
await this.initialize();
}
try {
data[key] = value;
await file.writeAsString(jsonEncode(data));
await update();
} catch (e) {
print('Error writing update to file: $e\n$key/$value');
}
}
}
I'm implementing a deeplink to a navigation app (like Google Maps or Waze) from a flutter app. I don't have problems doing so for either Google Maps or Waze, my problem is that if I configure the link for Google Maps, the user can still choose to open it in Waze, but obviously no navigation is started since parameters are different.
Is there a way to limit users choice on the app to use? Or is it possible to use a different link depending on which app the user chooses to open? Or even better, is there a universal link for navigation that works on both apps?
You can force to open the selected application using a Universal link (iOS) / App link (Android) and it should force the selected app to be opened. Of course, If the application you want to launch is not found in the device it will open it in the browser.
Here I assume that you are using latitude and longitude to navigate to the place but you can easily adjust it to something else.
I am also using the url_launcher to launch the links.
void launchWaze(double lat, double lng) async {
var url = 'waze://?ll=${lat.toString()},${lng.toString()}';
var fallbackUrl =
'https://waze.com/ul?ll=${lat.toString()},${lng.toString()}&navigate=yes';
try {
bool launched =
await launch(url, forceSafariVC: false, forceWebView: false);
if (!launched) {
await launch(fallbackUrl, forceSafariVC: false, forceWebView: false);
}
} catch (e) {
await launch(fallbackUrl, forceSafariVC: false, forceWebView: false);
}
}
void launchGoogleMaps(double lat, double lng) async {
var url = 'google.navigation:q=${lat.toString()},${lng.toString()}';
var fallbackUrl =
'https://www.google.com/maps/search/?api=1&query=${lat.toString()},${lng.toString()}';
try {
bool launched =
await launch(url, forceSafariVC: false, forceWebView: false);
if (!launched) {
await launch(fallbackUrl, forceSafariVC: false, forceWebView: false);
}
} catch (e) {
await launch(fallbackUrl, forceSafariVC: false, forceWebView: false);
}
}
You also need to add the following to your Info.plist file:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
<string>waze</string>
</array>
Updating to the url_launcher last version
import 'package:url_launcher/url_launcher.dart' as url_launcher;
...
Future<void> launchWazeRoute(double lat, double lng) async {
var url = 'waze://?ll=${lat.toString()},${lng.toString()}';
var fallbackUrl =
'https://waze.com/ul?ll=${lat.toString()},${lng.toString()}&navigate=yes';
try {
bool launched = false;
if (!kIsWeb) {
launched = await url_launcher.launchUrl(Uri.parse(url));
}
if (!launched) {
await url_launcher.launchUrl(Uri.parse(fallbackUrl));
}
} catch (e) {
await url_launcher.launchUrl(Uri.parse(fallbackUrl));
}
}
Future<void> launchGoogleMaps(double lat, double lng) async {
var url = 'google.navigation:q=${lat.toString()},${lng.toString()}';
var fallbackUrl =
'https://www.google.com/maps/search/?api=1&query=${lat.toString()},${lng.toString()}';
try {
bool launched = false;
if (!kIsWeb) {
launched = await url_launcher.launchUrl(Uri.parse(url));
}
if (!launched) {
await url_launcher.launchUrl(Uri.parse(fallbackUrl));
}
} catch (e) {
await url_launcher.launchUrl(Uri.parse(fallbackUrl));
}
}
You also need to put it on info.plist file:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
<string>waze</string>
</array>
I am trying to call a get REST API from my tvOS application. Following is my code when tap the Button:
async void ButtonClicked(UIButton sender)
{
try
{
HttpClient client = new HttpClient();
var response = await client.GetAsync("rest api url");
if (response.IsSuccessStatusCode)
{
var Response = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
if (!string.IsNullOrWhiteSpace(Response.ToString()))
{
var category = JsonConvert.DeserializeObject<Videos>(Response.ToString());
Debug.WriteLine("count:>>" + category.webContentCategoryList.Count);
}
}
}
catch(Exception e)
{
Debug.WriteLine("Exception:>>"+e);
}
I have installed the system.net.http and newtonsoft.json nuget packages. But when I run the project the application showing Main.cs file like below screenshot:
Am I missing something in this?
UPDATE
I have added breakpoint for the first line inside ButtonClicked function. When I taps the Button, the application showing Main.cs file like above screenshot. It is not hitting the first line of ButtonClicked function.
So the issue is something else, I am not an expert in tvOS applications so I can't figure out. I have uploaded a sample project here.
I have fixed this issue by separating the service call on a new function like below, new function is the async method:
partial void ButtonClicked(UIButton sender)
{
LoadData();
}
async void LoadData()
{
HttpClient client = new HttpClient();
var response = await client.GetAsync("service url");
if (response.IsSuccessStatusCode)
{
var Response = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
if (!string.IsNullOrWhiteSpace(Response.ToString()))
{
var category = JsonConvert.DeserializeObject<Videos>(Response.ToString());
Debug.WriteLine("count:>>" + category.Count);
}
}
}
My XF Thread is here for more details.
I have created a url scraper function, working and tested on Google Cloud, but I am really drawing a blank on how to invoke it. I have tried two methods, one using the cloud_functions package, and the other using a standard HTTPS get. I've tried looking online, but none of the solutions/guides involve functions with an input from the Flutter app, and an output back to the app.
Here's the structure of the function (which is working alright). I've named this function Parse in Google Cloud Platform.
<PYTHON PACKAGE IMPORTS>
def Parser(url):
<URL PARSE FUNCTIONS>
return source, datetime, imageurl, keyword
def invoke_parse(request):
request_json = request.get_json(silent=True)
file = Parser(request_json['url'])
return jsonify({
"source": file[0],
"datetime": file[1],
"imageurl": file[2],
"keyword": file[3],
})
The first method I tried was using an HTTP CALL to get the function. But that isn't working, even though there are no errors - I suspect it's just returning nothing.
parser(String url) async{ // Here I honestly don't know where to use the url input within the function
var uri = Uri.parse(<Function URL String>);
HttpClient client;
try {
var request = await client.getUrl(uri);
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
var json = await response.transform(utf8.decoder).join();
Map data = jsonDecode(json) as Map;
source = data['source']; // These are the variables used in the main Flutter app
postedAt = data['datetime'];
_imageUrl = data['image'];
keyword = data['keyword'];
} else {
print('Error running parse:\nHttp status ${response.statusCode}');
}
} catch (exception) {
print('Failed invoking the parse function.');
}
}
That didn't work, so I thought I might alternatively use the cloud_functions package as follows (in lieu of the previous):
parser(String url) async {
var functionUrl = <FUNCTION URL>;
HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(functionName: 'Parse')
..timeout = const Duration(seconds: 30);
try {
final HttpsCallableResult result = await callable.call(
<String, dynamic>{
'url': url,
}
);
setState(() {
source = result.data['source']; //THESE ARE VARIABLES USED IN THE FLUTTER APP
postedAt = result.data['datetime'];
_imageUrl = result.data['image'];
keyword = result.data['keyword'];
});
}
on CloudFunctionsException catch (e) {
print('caught firebase functions exception');
print(e.code);
print(e.message);
print(e.details);
} catch (e) {
print('caught generic exception');
print(e);
}
}
In the latter case, the code ran without errors but doesn't work. My flutter log states the following error:
I/flutter ( 2821): caught generic exception
I/flutter ( 2821): PlatformException(functionsError, Cloud function failed with exception., {code: NOT_FOUND, details: null, message: NOT_FOUND})
which I'm assuming is also an error at not being able to read the function.
Any help on how I should go about processing my function would be appreciated. Apologies if something is a really obvious solution, but I am not familiar as much with HTTP requests and cloud platforms.
Thanks and cheers.
Node Js Backend Function
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.test = functions.https.onCall(async (data, context) => {
functions.logger.info("Hello logs: ", {structuredData: true});
functions.logger.info( data.token, {structuredData: true});
}
Flutter frontend
1- pubspec.yaml
cloud_functions: ^1.1.2
2 - Code
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('test');
final HttpsCallableResult results = await callable.call<Map>( {
'token': token,
});