Running JavaScript Code in Flutter Webview
Table of contents
Have you ever used the webview_flutter package? It's awesome. You can render web view with ease. Have you ever stumbled upon a requirement when you had to run custom JavaScript code in Flutter on a certain website with or without rendering the website?
You might already know about WebViewController().runJavaScript()
method. Let's dive deep into it.
Let us suppose, We need to scrape certain data from a website and you need to render those in your Flutter app. In my case, I'm using my friend Nirav's website: https://niravko.com. I'm going to scrape all the titles of the blogs that he has posted so far. Firstly, I need to head to his website in the browser and write custom javascript codes to fetch all the titles as displayed below:
This JavaScript code runs fine in the browser. Now, we need to implement it in the app.
First, create a Flutter app, and add webview_flutter in pubspec.yaml file and run flutter pub get
.
Make a new screen where you need to display the data scraped (HomePage in my case).
Add these codes in the initState method your HomePage()
late WebViewController _controller;
String? message;
@override
void initState() {
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse("https://niravko.com"))
..addJavaScriptChannel("myChannel",
onMessageReceived: (JavaScriptMessage message) {
setMessage(message.message);
})
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (String url) {
injectJavascript(_controller);
},
),
);
super.initState();
}
You need to initialize the web controller with the above properties. To run the JavaScript you need to set JavaScript mode as unrestricted with setJavascriptMode(JavaScriptMode.unrestricted)
and to listen to the message that is to be passed via running JavaScript to the Flutter app, you need to add a JavaScript channel with some name and you can receive the message that is passed via JavaScript code.
The setMessage
function sets the message
variable with the String passed through JavaScript.
setMessage(String javascriptMessage) {
if (mounted) {
setState(() {
message = javascriptMessage;
});
}
}
Inside the injectJavaScript
function, We need to run the JavaScript code that we were running earlier in the browser. The only additional thing to the previous code is we need to notify our JavaScript channel i.e. myChannel
with the message in String
form i.e. myChannel.postMessage(message)
is enough to send a message to the channel that we set up earlier.
injectJavascript(WebViewController controller) async {
controller.runJavaScript('''
const items = Array.from(document.getElementsByClassName("Post_title__MJ8Hr __className_ff0aba"));
function getTitle(data){
return data.textContent.trim();
}
const titleList = items.map(getTitle);
//nameList is the list of titles that we scraped.
myChannel.postMessage(JSON.stringify(titleList));
''');
}
The basic flow is that once the website is rendered completely, the channel is set up and the JavaScript code is run and when myChannel.postMessage
is triggered, then setMessage
function is triggered and the message variable is set with the titleList
in String form.
Now to display the fetched title list, we can add the following code in the build method of HomePage
.
@override
Widget build(BuildContext context) {
return Scaffold(
body: message == null
? const Center(
child: CircularProgressIndicator(),
)
: Builder(
builder: (context) {
List<dynamic> items = jsonDecode(message!);
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
);
},
),
);
}
Now, the result will be displayed as shown below:
And you're done :)
The full source code is available at: https://github.com/ankeet7x/javascript_injection_in_flutter_webview
Subscribe to my newsletter
Read articles from Ankeet Karki directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Ankeet Karki
Ankeet Karki
I love to code.