123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- import 'dart:async';
- import 'dart:convert';
- import 'dart:html';
- import 'dart:math';
- import 'dart:typed_data';
- import 'dart:ui';
- import 'package:flutter/material.dart' hide Image;
- import 'package:flutter/services.dart';
- import 'package:webview_demo/utils/helper.dart';
- import 'package:webviewx/webviewx.dart';
- class WebViewXPage extends StatefulWidget {
- const WebViewXPage({
- Key? key,
- }) : super(key: key);
- @override
- _WebViewXPageState createState() => _WebViewXPageState();
- }
- class _WebViewXPageState extends State<WebViewXPage> {
- late WebViewXController webviewController;
- final executeJsErrorMessage = '脚本执行失败,请检查代码是否正确嵌入';
- /// 预加载的脚本文件
- List<String> scriptsFromAssets = [];
- Size get screenSize => MediaQuery.of(context).size;
- /// 预加载脚本文件
- Future<String> _preloadJS() async {
- final scriptsList = [
- "threejsview/js/build/three.js",
- "threejsview/js/build/THREE.MeshLine.js",
- "threejsview/js/controls/OrbitControls.js",
- "threejsview/js/gui/dat.gui.min.js",
- "threejsview/js/gui/stats.min.js",
- "threejsview/js/customScript/PointsConverter.js",
- "threejsview/js/customScript/Vector3Extention.js",
- "threejsview/js/customScript/EventCenter.js",
- "threejsview/js/customScript/CefInterface.js",
- "threejsview/js/customScript/InitPage.js",
- "threejsview/js/customScript/clipWorkflow/ClipMouseEvent.js",
- "threejsview/js/customScript/clipWorkflow/ClipMeshUpgrader.js",
- "threejsview/js/customScript/clipWorkflow/ClipDataDefine.js",
- "threejsview/js/customScript/clipWorkflow/ClipPlaneMover.js",
- "threejsview/js/customScript/clipWorkflow/ClipPlaneRotator.js",
- "threejsview/js/customScript/manager/CubeEdgesManager.js",
- "threejsview/js/customScript/manager/LinesDrawingManager.js",
- "threejsview/js/customScript/manager/ClipPlaneManager.js",
- "threejsview/js/customScript/manager/SpriteManager.js",
- "threejsview/js/customScript/utility/LoaderUtility.js",
- "threejsview/js/customScript/utility/LineIntersectionHelper.js",
- "threejsview/js/customScript/utility/PolyPointsTool.js",
- "threejsview/js/customScript/utility/CubeFaceNormalHelper.js",
- "threejsview/js/customScript/utility/OSHelper.js",
- ];
- for (String path in scriptsList) {
- var value = await _loadJSFromAssets(path);
- scriptsFromAssets.add(value);
- }
- return Future.value("");
- }
- Future<String> _loadJSFromAssets(String path) async {
- try {
- return await rootBundle.loadString(path);
- } catch (e) {
- return Future.error(e);
- }
- }
- String _loadRandomImage(int width, int height) {
- CanvasElement canvas = CanvasElement(width: width, height: height);
- var ctx = canvas.context2D
- ..fillStyle =
- "rgb(${Random().nextInt(255)},${Random().nextInt(255)},${Random().nextInt(255)})"
- ..fillRect(0, 0, width, height);
- var dataUrl = canvas.toDataUrl("image/jpeg", 0.95);
- return dataUrl;
- }
- @override
- void dispose() {
- webviewController.dispose();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('WebViewX Demo Page'),
- ),
- body: Center(
- child: Container(
- padding: const EdgeInsets.all(10.0),
- child: Row(
- children: [
- Expanded(
- flex: 2,
- child: Column(
- children: <Widget>[
- buildSpace(
- direction: Axis.vertical, amount: 10.0, flex: false),
- Container(
- padding: const EdgeInsets.only(bottom: 10.0),
- child: Text(
- '通过右侧的按钮操作 webview 注意!不可以在 webview 上面覆盖 Flutter 内容,会导致无法响应触摸',
- style: Theme.of(context).textTheme.bodyText2,
- ),
- ),
- buildSpace(
- direction: Axis.vertical, amount: 10.0, flex: false),
- Container(
- decoration: BoxDecoration(
- border: Border.all(width: 0.2),
- ),
- child: _buildWebViewX(),
- ),
- ],
- ),
- ),
- Expanded(
- child: Scrollbar(
- isAlwaysShown: true,
- child: SizedBox(
- width: 200,
- child: ListView(
- children: _buildButtons(),
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
- Widget _buildWebViewX() {
- return FutureBuilder<String>(
- future: _preloadJS(),
- builder: (context, AsyncSnapshot<String> snapshot) {
- if (snapshot.hasData) {
- return WebViewX(
- key: const ValueKey('webviewx'),
- initialContent: "<h2> Hello webviewX ! <h2>",
- initialSourceType: SourceType.html,
- height: screenSize.height / 1.5,
- width: min(screenSize.width * 0.8, 1024),
- onWebViewCreated: (controller) => webviewController = controller,
- onPageStarted: (src) =>
- debugPrint('A new page has started loading'),
- onPageFinished: (src) =>
- debugPrint('The page has finished loading'),
- jsContent: scriptsFromAssets
- .map((e) => EmbeddedJsContent(js: e))
- .toSet(),
- dartCallBacks: {
- DartCallback(
- name: 'TestDartCallback',
- callBack: (msg) => showSnackBar(msg.toString(), context),
- ),
- DartCallback(
- name: 'Dart_GetClipPlaneData',
- callBack: (msg) {
- final data = jsonDecode(msg);
- ///random color
- final imageData = _loadRandomImage(300, 300);
- /// result 需要传入 WorldPoints 列表(切面端点的三维坐标)、ImagePoints 列表(切面端点的二维坐标)、ImageData 图像路径(Base64)
- final result = {
- "ErrorCode": 1000,
- "WorldPoints": data["PointsList"],
- "ImagePoints": [
- {"X": 0.0, "Y": 1.0},
- {"X": 1.0, "Y": 1.0},
- {"X": 1.0, "Y": 0.0},
- {"X": 0.0, "Y": 0.0}
- ],
- "ImageData": imageData
- };
- return jsonEncode(result);
- },
- ),
- DartCallback(
- name: "Dart_GetStringData",
- callBack: (msg) {
- print("webview 触发 Dart_GetStringData :$msg");
- // showSnackBar(msg.toString(), context);
- return jsonEncode("{success:true}");
- },
- )
- },
- webSpecificParams: const WebSpecificParams(
- printDebugInfo: true,
- ),
- mobileSpecificParams: const MobileSpecificParams(
- androidEnableHybridComposition: true,
- ),
- navigationDelegate: (navigation) {
- debugPrint(navigation.content.sourceType.toString());
- return NavigationDecision.navigate;
- },
- );
- } else {
- return const CircularProgressIndicator();
- }
- });
- }
- List<Widget> _buildButtons() {
- return [
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(child: createButton(onTap: _goBack, text: '返回')),
- buildSpace(amount: 12, flex: false),
- Expanded(child: createButton(onTap: _goForward, text: '前进')),
- buildSpace(amount: 12, flex: false),
- Expanded(child: createButton(onTap: _reload, text: '重载')),
- ],
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '跳转至百度 (UrlBypass)',
- onTap: _setUrlBypass,
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '事件忽略开关 (点击、滚动等...)',
- onTap: _toggleIgnore,
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '在浏览器环境全局执行 2+2 计算,结果返回到 flutter',
- onTap: _evalRawJsInGlobalContext,
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- const Text(
- "👇👇 Threejs 页面相关(建议顺序执行) 👇👇",
- style: TextStyle(fontSize: 20),
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '1. 载入页面 (从 assets 文件夹)',
- onTap: _setHtmlFromAssets,
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '2. 执行 initPage() (iframe 中)',
- onTap: _initPage,
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '3. 执行 setSurface(100,100,100,path) (iframe 中)',
- onTap: _setSurface,
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '4. 执行 mdlFileLoaded("Carotid","True") (iframe 中)',
- onTap: _mdlFileLoaded,
- ),
- buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
- createButton(
- text: '切换模型 changeSurface (iframe 中)',
- onTap: _changeSurface,
- ),
- ];
- }
- Future<void> _initPage() async {
- try {
- await webviewController.callJsMethod('initPage', []);
- } catch (e) {
- showAlertDialog(
- executeJsErrorMessage,
- context,
- );
- }
- }
- Future<void> _setSurface() async {
- try {
- await webviewController
- .callJsMethod('setSurface', [759, 596, 397, "../3DWeb/FaceImage"]);
- } catch (e) {
- showAlertDialog(
- executeJsErrorMessage,
- context,
- );
- }
- }
- Future<void> _changeSurface() async {
- final faceImages = [
- _loadRandomImage(500, 500),
- _loadRandomImage(500, 500),
- _loadRandomImage(500, 500),
- _loadRandomImage(500, 500),
- _loadRandomImage(500, 500),
- _loadRandomImage(500, 500),
- ];
- // print([759, 596, 397, ...faceImages]);
- try {
- await webviewController
- .callJsMethod('changeSurface', [759, 596, 397, ...faceImages]);
- } catch (e) {
- showAlertDialog(
- executeJsErrorMessage,
- context,
- );
- }
- }
- Future<void> _mdlFileLoaded() async {
- try {
- await webviewController
- .callJsMethod('mdlFileLoaded', ["Carotid", "True"]);
- } catch (e) {
- showAlertDialog(
- executeJsErrorMessage,
- context,
- );
- }
- }
- void _setUrlBypass() {
- webviewController.loadContent(
- 'https://www.baidu.com/',
- SourceType.urlBypass,
- );
- }
- Future<void> _setHtmlFromAssets() async {
- return await webviewController.loadContent(
- 'threejsview/MainPage.html',
- SourceType.html,
- fromAssets: true,
- );
- }
- Future<void> _goForward() async {
- if (await webviewController.canGoForward()) {
- await webviewController.goForward();
- showSnackBar('Did go forward', context);
- } else {
- showSnackBar('Cannot go forward', context);
- }
- }
- Future<void> _goBack() async {
- if (await webviewController.canGoBack()) {
- await webviewController.goBack();
- showSnackBar('Did go back', context);
- } else {
- showSnackBar('Cannot go back', context);
- }
- }
- void _reload() {
- webviewController.reload();
- }
- /// 事件忽略开关
- void _toggleIgnore() {
- final ignoring = webviewController.ignoresAllGestures;
- webviewController.setIgnoreAllGestures(!ignoring);
- showSnackBar('Ignore events = ${!ignoring}', context);
- }
- /// 在浏览器环境全局执行
- Future<void> _evalRawJsInGlobalContext() async {
- try {
- final result = await webviewController.evalRawJavascript(
- '2+2',
- inGlobalContext: true,
- );
- showSnackBar('The result is $result', context);
- } catch (e) {
- showAlertDialog(
- executeJsErrorMessage,
- context,
- );
- }
- }
- Widget buildSpace({
- Axis direction = Axis.horizontal,
- double amount = 0.2,
- bool flex = true,
- }) {
- return flex
- ? Flexible(
- child: FractionallySizedBox(
- widthFactor: direction == Axis.horizontal ? amount : null,
- heightFactor: direction == Axis.vertical ? amount : null,
- ),
- )
- : SizedBox(
- width: direction == Axis.horizontal ? amount : null,
- height: direction == Axis.vertical ? amount : null,
- );
- }
- }
|