From ce201aa3901f1b02e944d10e6e830374e519ee5d Mon Sep 17 00:00:00 2001 From: Vishesh Handa Date: Tue, 19 May 2020 18:36:16 +0200 Subject: [PATCH] Add an experimental Katex rendering Widget Related to #125 This still needs to be integrated into the Flutter Markdown renderer. But the good news is that this works! It currently requires network access to download the katex scripts. It does NOT send the katex string to any server. The rendering is performed in the app. --- ios/Runner/Info.plist | 7 + lib/main_webview.dart | 281 ---------------------------------- lib/widgets/katex_widget.dart | 92 +++++++++++ pubspec.lock | 10 +- pubspec.yaml | 3 +- 5 files changed, 104 insertions(+), 289 deletions(-) delete mode 100644 lib/main_webview.dart create mode 100644 lib/widgets/katex_widget.dart diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 682c2fa3..891e134e 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -64,5 +64,12 @@ itms + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsArbitraryLoadsInWebContent + + diff --git a/lib/main_webview.dart b/lib/main_webview.dart deleted file mode 100644 index 331b42ad..00000000 --- a/lib/main_webview.dart +++ /dev/null @@ -1,281 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; - -import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; - -const kAndroidUserAgent = - 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36'; - -String selectedUrl = 'https://gitjournal.io/test_katex.html'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - final flutterWebViewPlugin = FlutterWebviewPlugin(); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter WebView Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - routes: { - '/': (_) => const MyHomePage(title: 'Flutter WebView Demo'), - '/widget': (_) { - return WebviewScaffold( - url: selectedUrl, - appBar: AppBar( - title: const Text('Widget WebView'), - ), - withZoom: true, - withLocalStorage: true, - hidden: true, - initialChild: Container( - color: Colors.redAccent, - child: const Center( - child: Text('Waiting.....'), - ), - ), - bottomNavigationBar: BottomAppBar( - child: Row( - children: [ - IconButton( - icon: const Icon(Icons.arrow_back_ios), - onPressed: flutterWebViewPlugin.goBack, - ), - IconButton( - icon: const Icon(Icons.arrow_forward_ios), - onPressed: () { - print(""); - flutterWebViewPlugin.goForward(); - }, - ), - IconButton( - icon: const Icon(Icons.autorenew), - onPressed: () { - print(""); - flutterWebViewPlugin.reload(); - }, - ), - ], - ), - ), - ); - }, - }, - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key key, this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - // Instance of WebView plugin - final flutterWebViewPlugin = FlutterWebviewPlugin(); - - // On destroy stream - StreamSubscription _onDestroy; - - // On urlChanged stream - StreamSubscription _onUrlChanged; - - // On urlChanged stream - StreamSubscription _onStateChanged; - - StreamSubscription _onHttpError; - - StreamSubscription _onProgressChanged; - - StreamSubscription _onScrollYChanged; - - StreamSubscription _onScrollXChanged; - - final _urlCtrl = TextEditingController(text: selectedUrl); - - final _codeCtrl = TextEditingController(text: 'window.navigator.userAgent'); - - final _scaffoldKey = GlobalKey(); - - final _history = []; - - Uint8List imageData = Uint8List(0); - - @override - void initState() { - super.initState(); - - flutterWebViewPlugin.close(); - - _urlCtrl.addListener(() { - selectedUrl = _urlCtrl.text; - }); - - // Add a listener to on destroy WebView, so you can make came actions. - _onDestroy = flutterWebViewPlugin.onDestroy.listen((_) { - if (mounted) { - // Actions like show a info toast. - _scaffoldKey.currentState - .showSnackBar(const SnackBar(content: Text('Webview Destroyed'))); - } - }); - - // Add a listener to on url changed - _onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen((String url) { - if (mounted) { - setState(() { - _history.add('onUrlChanged: $url'); - }); - } - }); - - _onScrollYChanged = - flutterWebViewPlugin.onScrollYChanged.listen((double y) { - if (mounted) { - setState(() { - _history.add('Scroll in Y Direction: $y'); - }); - } - }); - - _onScrollXChanged = - flutterWebViewPlugin.onScrollXChanged.listen((double x) { - if (mounted) { - setState(() { - _history.add('Scroll in X Direction: $x'); - }); - } - }); - - _onStateChanged = - flutterWebViewPlugin.onStateChanged.listen((WebViewStateChanged state) { - if (mounted) { - setState(() { - _history.add('onStateChanged: ${state.type} ${state.url}'); - }); - } - }); - - _onHttpError = - flutterWebViewPlugin.onHttpError.listen((WebViewHttpError error) { - if (mounted) { - setState(() { - _history.add('onHttpError: ${error.code} ${error.url}'); - }); - } - }); - } - - @override - void dispose() { - // Every listener should be canceled, the same should be done with this stream. - _onDestroy.cancel(); - _onUrlChanged.cancel(); - _onStateChanged.cancel(); - _onHttpError.cancel(); - _onProgressChanged.cancel(); - _onScrollXChanged.cancel(); - _onScrollYChanged.cancel(); - - flutterWebViewPlugin.dispose(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - key: _scaffoldKey, - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (imageData.isNotEmpty) Image.memory(imageData), - Container( - padding: const EdgeInsets.all(24.0), - child: TextField(controller: _urlCtrl), - ), - RaisedButton( - onPressed: () { - flutterWebViewPlugin.launch(selectedUrl, hidden: true); - }, - child: const Text('Open "hidden" Webview'), - ), - Container( - padding: const EdgeInsets.all(24.0), - child: TextField(controller: _codeCtrl), - ), - RaisedButton( - onPressed: () { - var js = - """katex.render("\\\\pm\\\\sqrt{a^2 + b^2}", document.body, { - throwOnError: false -});"""; - final future = flutterWebViewPlugin.evalJavascript(js); - future.then((String result) async { - print("Eval done $result"); - setState(() { - _history.add('eval: $result'); - }); - - var _imageData = await flutterWebViewPlugin.takeScreenshot(); - print("Got $_imageData"); - setState(() { - imageData = _imageData; - }); - //flutterWebViewPlugin.close(); - }); - }, - child: const Text('Eval javascript katex'), - ), - RaisedButton( - onPressed: () { - setState(() { - print(""); - _history.clear(); - }); - flutterWebViewPlugin.close(); - }, - child: const Text('Close'), - ), - RaisedButton( - onPressed: () { - flutterWebViewPlugin.getCookies().then((m) { - setState(() { - _history.add('cookies: $m'); - }); - }); - }, - child: const Text('Cookies'), - ), - Text(_history.join('\n')) - ], - ), - ), - ); - } -} - -/* - -html2canvas(document.body).then(function(canvas) { - var img = canvas.toDataURL("image/png"); - console.log(img); -}); - -*/ diff --git a/lib/widgets/katex_widget.dart b/lib/widgets/katex_widget.dart new file mode 100644 index 00000000..b0c4b0b7 --- /dev/null +++ b/lib/widgets/katex_widget.dart @@ -0,0 +1,92 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; +import 'package:path/path.dart' as p; + +class KatexWidget extends StatefulWidget { + final String input; + + KatexWidget(this.input, {Key key}) : super(key: key); + + @override + _KatexWidgetState createState() => _KatexWidgetState(); +} + +class _KatexWidgetState extends State { + String imagePath; + JavascriptChannel jsChannel; + + final flutterWebViewPlugin = FlutterWebviewPlugin(); + final selectedUrl = 'https://gitjournal.io/test_katex.html'; + + @override + void initState() { + super.initState(); + + jsChannel = JavascriptChannel( + name: 'Print', + onMessageReceived: (JavascriptMessage message) { + print("-----JS CHANNEL ----"); + print(message.message); + + var uri = UriData.parse(message.message); + var tmpFile = p.join(Directory.systemTemp.path, "katex.png"); + File(tmpFile).writeAsBytesSync(uri.contentAsBytes()); + + setState(() { + print("State has been set"); + imagePath = tmpFile; + }); + }, + ); + + flutterWebViewPlugin.onStateChanged.listen((WebViewStateChanged state) { + if (!mounted) return; + + if (state.type == WebViewState.finishLoad) { + _renderKatex(); + } + }); + + flutterWebViewPlugin.close(); + flutterWebViewPlugin.launch( + selectedUrl, + hidden: true, + javascriptChannels: {jsChannel}, + withJavascript: true, + ); + } + + @override + void dispose() { + flutterWebViewPlugin.dispose(); + + super.dispose(); + } + + void _renderKatex() { + var katex = widget.input; + var js = """katex.render("$katex", document.body, { + throwOnError: false +}); + +html2canvas(document.body).then(function(canvas) { + var img = canvas.toDataURL("image/png"); + Print.postMessage(img); +}); +"""; + flutterWebViewPlugin.evalJavascript(js); + } + + @override + Widget build(BuildContext context) { + if (imagePath == null) { + return Container(); + } + + print("Building Network Image"); + return Image.file(File(imagePath)); + } +} diff --git a/pubspec.lock b/pubspec.lock index 06e4adde..48233d5d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -318,12 +318,10 @@ packages: flutter_webview_plugin: dependency: "direct main" description: - path: "." - ref: HEAD - resolved-ref: "045bd6357ce81162d05e367133a95a53ad291451" - url: "https://github.com/breez/flutter_webview_plugin.git" - source: git - version: "0.3.0+2" + name: flutter_webview_plugin + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.11" font_awesome_flutter: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 9332741a..1ccce37d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,8 +50,7 @@ dependencies: ssh_key: git: https://github.com/GitJournal/ssh_key.git isolate: ^2.0.3 - flutter_webview_plugin: - git: https://github.com/breez/flutter_webview_plugin.git + flutter_webview_plugin: ^0.3.11 image_picker: ^0.6.6+1 easy_localization: ^2.2.1 easy_localization_loader: ^0.0.2