-
Notifications
You must be signed in to change notification settings - Fork 29.3k
Description
I have two deployed apps using this feature that has subsequently stopped working. If I now attempt any new uploads to TestFlight it no longer works.
Currently, the code below works on a physical iPhone running through Android Studio for both debug and deploy, but when uploaded to TestFlight stops working.
The need is to inject JS into a webpage before the JS on the webpage loads. In the code example, both the output on the web page and the postMessage in a JS channel will show that either the web page JS ran or the injected JS ran.
Steps to Reproduce
I am using “webview_flutter: ^3.0.1” in my pubspec.yaml.
- to see the normal functioning of the web page JS, comment out _controller.runJavascript(jsOverLoad); in the onPageStarted handler in the app code below and run.
You should get 3 AlertDialogs. The top two alerts are triggered from _controller.runJavascriptReturningResult('testing();')
in the onPageFinished
handle and will show for the top one “RESULT: Return from WEB”. The next is coming from the channel and shows “CHANNEL: Hello from WEB Channel” The third Alert is triggered by JS on the web page which also comes through the channel and should show the same as the second Alert. Then on the web page displayed, you will see the last line reads “Return from Web”.
- uncomment
_controller.runJavascript(jsOverLoad);
in theonPageStarted
handler in the app code below and run.
Now you will see (if you are running to a physical iPhone) the 3 Alerts with these messages: “RESULT: Injected Return” and “CHANNEL: Hello from My Injected Channel” And the last line on the displayed web page will read “Injected Return”.
- deploy the app to TestFlight and you will get the results from the web page JS and not from the Injected JS, but the code
_controller.runJavascriptReturningResult('testing();')
in theonPageFinished
handle still runs as expected.
Code sample
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final String jsOverLoad = '''
(function() {
window.testing = function(){
try {
MyChannel.postMessage("Hello from My Injected Channel");
} catch (e) {}
return "Injected Return";
}
window.testing();
})();
''';
@override
void initState() {
super.initState();
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
void _showMessage(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Insight'),
content: Text(message),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () {
Navigator.pop(context);
},),
],
),
);
}
JavascriptChannel _myJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'MyChannel',
onMessageReceived: (JavascriptMessage message) {
_showMessage('CHANNEL: ${message.message}');
},);
}
@override
Widget build(BuildContext context) {
final _key = UniqueKey();
late WebViewController _controller;
return Scaffold(
appBar: AppBar(
title: const Text('Injection Demo'),
),
body: Column(
children: [
Expanded(
child: WebView(
key: _key,
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: <JavascriptChannel>{
_myJavascriptChannel(context),
},
onWebViewCreated: (WebViewController webViewController) {
_controller = webViewController;
// NOTE: headers are not needed in this example but are included for testing because the actual app loads the web page this way.
final Map<String, String> headers = {
'Authorization': 'Bearer test123'
};
_controller.loadUrl('https://js-injection-demo-c5dyn.ondigitalocean.app', headers: headers);
},
onPageStarted: (String url) {
_controller.runJavascript(jsOverLoad);
},
onPageFinished: (String url) {
_controller.runJavascriptReturningResult('testing();')
.then((result) {
_showMessage('RESULT: $result');
});
},
),
),
],
),
);
}
}
HTML Code sample
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JS Injection Demo Website</title>
<!-- <script type="text/javascript" src="my-javascript.js"></script> -->
<script>
if (!window.testing) {
window.testing = function(){
try {
MyChannel.postMessage("Hello from WEB Channel");
} catch (e) {
console.warn('MyChannel is not defined');
}
return "Return from WEB";
}
}
</script>
</head>
<body>
<h1>JS Injection Demo Website</h1>
<p><em>Testing JS injection issue.</em></p>
<div id="output"></div>
<script>
var output = document.getElementById('output');
output.innerHTML = window.testing();
</script>
</body>
</html>
Logs
Flutter Doctor:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.10.4, on macOS 12.2.1 21D62 darwin-arm, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.1)
[✓] VS Code (version 1.66.0)
[✓] Connected device (2 available)
[✓] HTTP Host Availability
• No issues found!