'How to Handle (Tel:,Mailto:,Wa:,Sms:) In Flutter Inappwebview?
I'm making a humanitarian flutter project using the inappwebview & url_launcher package, I've been looking for some references but all the code doesn't work properly, can you help me how to make my coding work?
My hope: I want when the url tel, mailto, sms and whatsapp when clicked will go to the application according to the clicked link, please help, thank you very much in advance
and below is my project code
Android Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app4">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.permission.MAPS_RECEIVE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
</intent>
</queries>
<application
android:label="app4"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
main.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:permission_handler/permission_handler.dart';
void main() async {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.white, // status bar color
statusBarIconBrightness: Brightness.dark, // status bar icon color
systemNavigationBarColor: Colors.white, // navigation bar color
systemNavigationBarIconBrightness: Brightness.dark, // color of navigation controls
));
WidgetsFlutterBinding.ensureInitialized();
await Permission.camera.request();
await Permission.microphone.request();
await Permission.storage.request();
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
await FlutterDownloader.initialize(debug: true);
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
late InAppWebViewController webViewController;
GlobalKey<ScaffoldState>webViewKey=GlobalKey<ScaffoldState>();
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false,
javaScriptEnabled: true,
javaScriptCanOpenWindowsAutomatically: true,
useOnDownloadStart: true,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
)
);
late PullToRefreshController pullToRefreshController;
String url = "";
double progress = 0;
final urlController = TextEditingController();
@override
void initState() {
super.initState();
pullToRefreshController = PullToRefreshController(
options: PullToRefreshOptions(
color: Colors.red,
),
onRefresh: () async {
if (Platform.isAndroid) {
webViewController.reload();
} else if (Platform.isIOS) {
webViewController.loadUrl(
urlRequest: URLRequest(url: await webViewController.getUrl()));
}
},
);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onBack,
child: Scaffold(
key: webViewKey,
appBar: null,
body: SafeArea(
child: Column(children: <Widget>[
Expanded(
child: Stack(
children: [
InAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse("https://google.com/")),
initialOptions: options,
pullToRefreshController: pullToRefreshController,
onWebViewCreated: (controller) {
webViewController = controller;
},
onLoadStart: (controller, url) {
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
androidOnPermissionRequest: (controller, origin, resources) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT);
},
shouldOverrideUrlLoading: (controller, shouldOverrideUrlLoadingRequest) async {
String action = shouldOverrideUrlLoadingRequest.url.split(':').first;
List<String> customActions = ['tel', 'whatsapp', 'mailto'];
bool isCustomAction = customActions.contains(action);
if (isCustomAction) {
if (await canLaunch(url)) {
await launch(
url,
);
return ShouldOverrideUrlLoadingAction.CANCEL;
}
}
return ShouldOverrideUrlLoadingAction.ALLOW;
},
onLoadStop: (controller, url) async {
pullToRefreshController.endRefreshing();
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
onLoadError: (controller, url, code, message) {
pullToRefreshController.endRefreshing();
},
onProgressChanged: (controller, progress) {
if (progress == 100) {
pullToRefreshController.endRefreshing();
}
setState(() {
this.progress = progress / 100;
urlController.text = this.url;
});
},
onUpdateVisitedHistory: (controller, url, androidIsReload) {
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
onConsoleMessage: (controller, consoleMessage) {
print(consoleMessage);
},
),
progress < 1.0
? LinearProgressIndicator(
value: progress,
minHeight: 2,
)
: Container(),
],
),
),
]
)
)
),
);
}
Future<bool> _onBack() async {
bool goBack;
if (await webViewController.canGoBack()) {
webViewController.goBack();
return false;
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Kamu yakin ingin keluar ?'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Tidak'),
),
FlatButton(
onPressed: () {
SystemNavigator.pop();
},
child: Text('Iya'),
),
],
));
return Future.value(true);
}
}
}
And I also tried the code below but when clicked it immediately exits my application and goes to the browser not to tel, sms or whatsapp:
shouldOverrideUrlLoading: (controller, shouldOverrideUrlLoadingRequest) async {
var uri = shouldOverrideUrlLoadingRequest.request.url!;
if (uri.scheme.startsWith("tel")) {
if (await canLaunch(url)) {
await launch(
url,
);
return NavigationActionPolicy.CANCEL;
}
}
return NavigationActionPolicy.ALLOW;
},
Solution 1:[1]
I had the same issue. This is what finally worked for me:
shouldOverrideUrlLoading:
(controller, shouldOverrideUrlLoadingRequest) async {
var url = shouldOverrideUrlLoadingRequest.request.url;
var uri = Uri.parse(_url);
if (url.scheme.startsWith("tel")) {
await launch('tel:+233123456789',
forceSafariVC: false,
forceWebView: false,
universalLinksOnly: true,
);
return NavigationActionPolicy.CANCEL;
}
if (url.scheme.startsWith("whatsapp")) {
await launch('whatsapp://send/?phone=233123456789',
forceSafariVC: false,
forceWebView: false,
universalLinksOnly: true,
);
// and cancel the request
return NavigationActionPolicy.CANCEL;
}
return NavigationActionPolicy.ALLOW;
},
I referred to my initial url as "_url". I hope this helps in adjusting it to work for you. All the best.
Solution 2:[2]
Try to remove this line and call launch directly.
if (await canLaunch(url)) {}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Selorm D |
Solution 2 | petrabios |