Flutter Webview Apple Pay - ios

In an attempt to use WebViewController.evaluateJavascript('myjs') I get this error in my debug console.
[VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: PlatformException(evaluateJavaScript_failed, Failed evaluating JavaScript, JavaScript string was: 'doucment.getElementById("checkout_email").value = "Hell0";'
Error Domain=WKErrorDomain Code=4 "A JavaScript exception occurred" UserInfo={WKJavaScriptExceptionLineNumber=0, WKJavaScriptExceptionMessage=Unable to run user agent scripts because this document has previously accessed Apple Pay. Documents can be prevented from accessing Apple Pay by adding a WKUserScript to the WKWebView's WKUserContentController., WKJavaScriptExceptionColumnNumber=0, NSLocalizedDescription=A JavaScript exception occurred})
#0 StandardMethodCodec.decodeEnvelope
package:flutter/…/services/message_codecs.dart:572
#1 MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:161
<asynchronous suspension>
#2 MethodChannel.invokeMethod
package:flutter/…/services/platform_channel.dart:334
#3 MethodChannelWebViewPlatform<…>
My Dart file looks like this,
On page load I do a simple check to ensure I execute the js on the correct page. If the check passes the js is executed but unfortunately returns the error above. Ive tried looking all over Google for an answer but ive found nothing similar to the issue I am having.
final Completer<WebViewController> _controller =
Completer<WebViewController>();
TextEditingController _searchQueryController = TextEditingController();
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
elevation: 0,
title: _searchBar(),
leading: _directionButton('backward'),
actions: [_directionButton('forward')],
),
body: WebView(
initialUrl: 'https://google.com',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
onPageStarted: (url) {
_searchQueryController.text = url;
},
onPageFinished: (url) async {
print('Page Finished: ' + url);
final controller = await _controller.future;
if (url.contains('checkouts')) {
await controller.evaluateJavascript('doucment.getElementById("checkout_email").value = "Hello";');
}
},
),
));
}

Try to add this into your info.plist , Replasing domain of your payment gateway
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>yourdomain.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
and Dont forget to re-install app

Related

How to run app on mobile web with flutter URI schemes

I'm trying to start my flutter app when it connects to a specific URI on the mobile web.
Among other methods, we decided to use URI schemes.
Assuming my specific URI link is e.g. https://myFlutterTest.URISchemes
Run the URI like this: https://myFlutterTest.URISchemes/home?<parameter>
I determine whether to run the app normally based on /home. After that, the value of <parameter> is stored in the Map.
I succeeded in receiving android as a URI and running it normally.
But IOS didn't succeed.
Added below to info.plist
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>https</string>
</array>
<key>CFBundleURLName</key>
<string>myFlutterTest.URISchemes</string>
</dict>
</array>
main.dart(
The part that receives and determines the URI path)
Widget build(BuildContext context) {
HttpProvider httpProbvider = HttpProvider();
return Platform.isAndroid
? MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: "/",
onGenerateRoute: (RouteSettings path) {
if (path.name!.contains("/home")) {
print("path.name : ${path.name}");
return MaterialPageRoute(
builder: (BuildContext context) => Home(),
);
}
},
)
: CupertinoApp(
debugShowCheckedModeBanner: false,
initialRoute: "/",
onGenerateRoute: (RouteSettings path) {
if (path.name!.contains("/home")) {
print("path.name : ${path.name}");
return CupertinoPageRoute(
builder: (BuildContext context) => Home(),
);
}
},
);
}
}
If you know anything wrong, please let me know. Thank you.

Can't pass headers in flutter_inappwebview & webview_flutter in iOS

versions:
flutter_inappwebview: 5.3.2
webview_flutter: 2.3.1
I'm trying to pass headers to a webview URL, on Android everything is working fine but in ios, I'm getting an unauthorized error displayed.
I tried adding NSAppTransportSecurity in Info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
also enabled embedded preview
<key>io.flutter.embedded_views_preview</key>
<true/>
but unable to make it work. Below is my code.
code:
webview_flutter:
body: WebView(
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (c) {
c.loadUrl(
url,
headers: {'Authorization': 'Bearer ' + token},
);
},
),
flutter_inappwebview:
body: InAppWebView(
initialUrlRequest: URLRequest(
url: Uri.parse(url),
headers: {'Authorization': 'Bearer ' + token},
),
),
initialize variable in the stateful class and try this solution
bool _init = true;
onLoadStop: (controller, url) async {
if (Platform.isIOS) {
if (_isInit) {
await webViewController!.loadUrl(
urlRequest: URLRequest(
url: url,
headers: headers,
),
);
setState(() {
_isInit = false;
});
}
}
pullToRefreshController!.endRefreshing();
this.url = url.toString();
urlController.text = this.url;
},

Flutter webview is blank on IOS but working on android

I'm using flutter webview to present the payment url in my app using the following class:
class YourWebView extends StatelessWidget {
String url;
bool isFinshed = false;
YourWebView(this.url);
final Completer<WebViewController> _controller =
Completer<WebViewController>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('اكمال عملية الدفع..'),
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () {
if(isFinshed) {
Provider.of<MatchProvider>(context, listen: false)
.getMyComingMatches();
Navigator.of(context).popUntil((route) => route.isFirst);
} else {
Navigator.pop(context);
}
}),
),
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: Uri.encodeFull(url),
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
debuggingEnabled: true,
onPageFinished: (String url) {
SystemChannels.textInput.invokeMethod('TextInput.hide');
if (url.contains("tap/check?tap_id")) {
isFinshed = true;
}
print('Page finished loading: $url');
},
gestureRecognizers: null,
// gestureNavigationEnabled: false
);
}));
}
The url looks like:
https://xxxxxx.com/tap/check?tap_id=chg_TS05162021120xxxxxxx
Everything is working on Android, but on IOS i get a blank screen and i see this error in xcode debug logs :
WebPageProxy::didFailProvisionalLoadForFrame
I have tried to run another urls on the webview and it was working, but the payment url isn't, even though it's working on Android or other browsers.
I think you maybe tried to load a http link in webview instead of https. In that case you should add the following in your info.plist file.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>
I am not familiar with Flutter, but on iOS, if the system's process for rendering the web content terminated, the webview would be blank, the flutter plugin is handling the situation in here, you may have to check the error and do a reload.

Flutter Bluetooth detection on iOS for scanning beacons

I'm developing an app and I need to scan iBeacons. I used the beacons_plugin (https://pub.dev/packages/beacons_plugin) and it was working well on Android and iOS. But since the 2.0.0 it's not working anymore on iOS. I tried to understand why but I couldn't. Any help please ?
my info.plist :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>MiLo</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>App needs location permissions to scan nearby beacons.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>App needs location permissions to scan nearby beacons.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>App needs location permissions to scan nearby beacons.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
My widget where I use the plugin :
// All packages needed by the widget
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io' show Platform;
import 'dart:convert';
import 'package:beacons_plugin/beacons_plugin.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_application_1/widgets/beacon_card.dart';
class BeaconDetection extends StatefulWidget {
// Declaration of the variables needed by the widget 1/2
final townId;
final fontsizeValue;
final languageId;
final wordMessageBeforeScan;
final wordButtonLaunchScan;
final wordButtonStopScan;
// Declaration of the variables needed by the widget 2/2
BeaconDetection(
{this.townId,
this.fontsizeValue,
this.languageId,
this.wordMessageBeforeScan,
this.wordButtonLaunchScan,
this.wordButtonStopScan});
#override
_BeaconDetectionState createState() => _BeaconDetectionState();
}
class _BeaconDetectionState extends State<BeaconDetection> {
bool isRunning = false;
String _beaconUuid = '';
double _beaconDistance = 0;
String _beaconDetectedUuid = 'null';
double _beaconDetectedDistance = 0;
final StreamController<String> beaconEventsController =
StreamController<String>.broadcast();
#override
void initState() {
super.initState();
initPlatformState();
}
#override
void dispose() {
beaconEventsController.close();
super.dispose();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
if (Platform.isAndroid) {
// Prominent disclosure
await BeaconsPlugin.setDisclosureDialogMessage(
title: "Need Location Permission",
message: "This app collects location data to work with beacons.");
// Only in case, you want the dialog to be shown again. By Default, dialog will never be shown if permissions are granted.
// await BeaconsPlugin.clearDisclosureDialogShowFlag(false);
}
BeaconsPlugin.listenToBeacons(beaconEventsController);
// Extraction of the name and the uuid of the beacons of the chosen city from the firebase
FirebaseFirestore.instance
.collection('towns/${widget.townId}/beacons')
.snapshots()
.listen((event) {
event.docs.forEach((element) async {
if (element['name'].isNotEmpty == true &&
element['uuid'].isNotEmpty == true &&
element['visibility'] == true) {
await BeaconsPlugin.addRegion(element['name'], element['uuid']);
}
});
});
// When listening the data from beacons detected
beaconEventsController.stream.listen(
(data) {
if (data.isNotEmpty) {
setState(() {
Map _beaconScanned = jsonDecode(data);
_beaconUuid = _beaconScanned['uuid'];
_beaconDistance = double.parse(_beaconScanned['distance']);
});
// print("Beacons DataReceived: " + data);
FirebaseFirestore.instance
.collection('towns/${widget.townId}/beacons')
.where('uuid', isEqualTo: _beaconUuid)
.snapshots()
.listen((event) {
event.docs.forEach((element) {
if (_beaconUuid == element['uuid'] &&
element['visibility'] == true &&
_beaconDistance <= element['distance']) {
print('Beacon: $_beaconUuid | Distance: $_beaconDistance');
_beaconDetectedUuid = _beaconUuid;
_beaconDetectedDistance = _beaconDistance;
}
});
});
}
},
onDone: () {},
onError: (error) {
print("Error: $error");
});
await BeaconsPlugin.runInBackground(
isRunning); // Send 'true' to run in background
if (Platform.isAndroid) {
BeaconsPlugin.channel.setMethodCallHandler((call) async {
if (call.method == 'scannerReady') {
await BeaconsPlugin.startMonitoring();
setState(() {
isRunning = true;
});
}
});
} else if (Platform.isIOS) {
await BeaconsPlugin.startMonitoring();
setState(() {
isRunning = true;
});
}
if (!mounted) return;
}
Widget build(BuildContext context) {
return Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('towns/${widget.townId}/beacons')
.where('uuid', isEqualTo: _beaconDetectedUuid)
.snapshots(),
builder: (ctx, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) // Initialisation state
return Container(
margin: const EdgeInsets.all(10),
height: 314, // Beacon card's size
child: Center(child: CircularProgressIndicator()),
);
if (_beaconDetectedUuid == 'null' &&
isRunning == false) // Before first scan State
return Container(
margin: const EdgeInsets.all(10),
height: 314, // Beacon card's size
child: Center(child: Text(widget.wordMessageBeforeScan)),
);
if (_beaconDetectedUuid == 'null' &&
isRunning == true) // Launch first scan state
return Container(
margin: const EdgeInsets.all(10),
height: 314, // Beacon card's size
child: Center(
child: CircularProgressIndicator(),
),
);
final beacons = snapshot.data!.docs;
return BeaconCard( // When the detection is done => Display the widget 'BeaconCard' with the following data
title: beacons[0]['title'],
monument: beacons[0]['monument'],
image: beacons[0]['image'],
duration: beacons[0]['duration'],
distance: _beaconDetectedDistance,
townId: widget.townId,
uuid: beacons[0]['uuid'],
fontsizeValue: widget.fontsizeValue,
languageId: widget.languageId,
);
}),
isRunning
? FloatingActionButton.extended( // If 'isRunning' is true => Display a button to stop the scan
icon: Icon(Icons.bluetooth_disabled),
label: Text(widget.wordButtonStopScan),
backgroundColor: Colors.red,
onPressed: () async {
await BeaconsPlugin.stopMonitoring();
setState(() {
isRunning = false;
});
},
)
: FloatingActionButton.extended( // If 'isRunning' is false => Display a button to start the scan
icon: Icon(Icons.bluetooth),
label: Text(widget.wordButtonLaunchScan),
backgroundColor: Theme.of(context).primaryColor,
onPressed: () async {
initPlatformState();
await BeaconsPlugin.startMonitoring();
setState(() {
isRunning = true;
_beaconDetectedUuid = 'null';
});
},
),
SizedBox(
height: 16,
)
],
);
}
}
Thanks a lot

WebView not working in iOS using flutter but working in Android

i am using webview_flutter: 1.0.7 in flutter. It's working in android but not working in iOS.
I have added below code in info.plist for webview_flutter.
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
and webview controller code is
Container(
height: wvHeight,
child: WebView(
onPageFinished: (some) async {
String h = await mWebViewControllerSolution
.evaluateJavascript("document.documentElement.scrollHeight;");
if (h != null) {
print("H = " + h.toString());
double height = double.parse(h);
setState(() {
wvHeight = height;
});
}
},
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
mWebViewControllerSolution = webViewController;
wvSolutionNavigation(context);
},
),
);
wvSolutionNavigation(BuildContext context) async {
String html = getSolution();
if (!Utils.isTable(html)) {
return;
}
if (html.isEmpty) {
return;
}
final String contentBase64 =
base64Encode(const Utf8Encoder().convert(html));
await mWebViewControllerSolution
.loadUrl('data:text/html; charset=utf-8;base64,$contentBase64');
}
giving error :
Unhandled Exception: PlatformException(loadUrl_failed, Failed parsing the URL, Request was: '{
headers = "";
url = "data:text/html; charset=utf-8;base64,PHA+VGhlIGNvcnJlY3QgYW5zd2VyIGlzIDxzdHJvbmc+T3B0aW9uIDI8L3N0cm9uZz4gaS5lJm5ic3A7PHN0cm9uZz5LZXNhdmFuYW5kYSBCaGFyYXRpIGNhc2UuPC9zdHJvbmc+PC9wPg0KDQo8dWw+DQoJPGxpPkluIHRoZSZuYnNwOzxzdHJvbmc+S2VzYXZhbmFuZGEgQmhhcmF0aSBjYXNlPC9zdHJvbmc+LCB0aGUgU3VwcmVtZSBjb3VydCBoZWxkIHRoYXQgdGhlICZxdW90OzxzdHJvbmc+YmFzaWMgc3RydWN0dXJlIG9mIHRoZSBDb25zdGl0dXRpb24gY291bGQgbm90IGJlIGFicm9nYXRlZCBldmVuIGJ5IGEgY29uc3RpdHV0aW9uYWwgYW1lbmRtZW50JnF1b3Q7PC9zdHJvbmc+PC9saT4NCjwvdWw+DQoNCjx0YWJsZSBib3JkZXI9IjEiIGNlbGxwYWRkaW5nPSIxIiBjZWxsc3BhY2luZz0iMSIgc3R5bGU9IndpZHRoOjM4OC4wcHgiPg0KCTx0Ym9keT4NCgkJPHRyPg0KCQkJPHRkIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlciI+PHN0cm9uZz5DYXNlczwvc3Ryb25nPjwvdGQ+DQoJCQk8dGQgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyIj48c3Ryb25nPlN1cHJlbWUgQ291cnQgdmVyZGljdDwvc3Ryb25nPjwvdGQ+DQoJCTwvdHI+DQoJCTx0cj4NCgkJCTx0ZD5Hb2xha25hdG<…>

Resources