From 4b5128630d71b3e5a4333cc3596ae55ad31c4df4 Mon Sep 17 00:00:00 2001 From: KevinR Date: Sun, 10 Jan 2021 18:15:40 +0100 Subject: [PATCH 1/4] implementation of the ability to change the base color --- lib/model_viewer.dart | 1 + lib/src/controller.dart | 19 +++++++++++++++++++ lib/src/html_builder.dart | 26 ++++++++++++++++++++++++++ lib/src/model_viewer.dart | 33 ++++++++++++++++++++++++++++++++- 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 lib/src/controller.dart diff --git a/lib/model_viewer.dart b/lib/model_viewer.dart index 042bbc2..a5883c9 100644 --- a/lib/model_viewer.dart +++ b/lib/model_viewer.dart @@ -11,3 +11,4 @@ library model_viewer; export 'src/model_viewer.dart'; +export 'src/controller.dart'; diff --git a/lib/src/controller.dart b/lib/src/controller.dart new file mode 100644 index 0000000..dc6af3c --- /dev/null +++ b/lib/src/controller.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +typedef ChangeColorTypeDef = Future Function(String); + +/// The [ModelViewerColorController] is used to control the color settings of the model viewer. +/// If the function [changeColor(colorString)] is called, then the base color of the model will be changed. +/// +/// At the moment the [ModelViewerColorController] can only change the base color of the model. +/// The base color is auto detected by the biggest size object of the model. +class ModelViewerColorController { + /// change the color by a given colorString + ChangeColorTypeDef changeColor; + // ToDo: Add a function to get possibble color areas + // ToDo: set colors for detected areas + + void dispose() { + changeColor = null; + } +} diff --git a/lib/src/html_builder.dart b/lib/src/html_builder.dart index 8d3fc08..1987757 100644 --- a/lib/src/html_builder.dart +++ b/lib/src/html_builder.dart @@ -19,6 +19,7 @@ abstract class HTMLBuilder { final int autoRotateDelay, final bool autoPlay, final bool cameraControls, + final bool enableColorChange, final String iosSrc}) { final html = StringBuffer(htmlTemplate); html.write(''); + + if (enableColorChange ?? false) { + html.write(_buildColorChangeJSFunction()); + } + + print(html.toString()); return html.toString(); } + + static String _buildColorChangeJSFunction() { + return ''' + + '''; + } } diff --git a/lib/src/model_viewer.dart b/lib/src/model_viewer.dart index 5f67e84..696ba64 100644 --- a/lib/src/model_viewer.dart +++ b/lib/src/model_viewer.dart @@ -12,6 +12,7 @@ import 'package:flutter_android/android_content.dart' as android_content; import 'package:webview_flutter/platform_interface.dart'; import 'package:webview_flutter/webview_flutter.dart'; +import 'controller.dart'; import 'html_builder.dart'; /// Flutter widget for rendering interactive 3D models. @@ -28,6 +29,8 @@ class ModelViewer extends StatefulWidget { this.autoRotateDelay, this.autoPlay, this.cameraControls, + this.enableColorChange, + this.colorController, this.iosSrc}) : super(key: key); @@ -66,6 +69,15 @@ class ModelViewer extends StatefulWidget { /// Defaults to "auto" which allows the model to be resized. final String arScale; + /// Enable the ability to change the color of the model with the [ModelViewerColorController] + /// If this value is set to true, it's possible to set the color of the model. + final bool enableColorChange; + + /// Controller to set the color of the model + /// Call the Function [ModelViewerColorController.changeColor(colorString)] + /// to set a color by an given colorString + ModelViewerColorController colorController; + /// Enables the auto-rotation of the model. final bool autoRotate; @@ -98,6 +110,10 @@ class _ModelViewerState extends State { @override void initState() { super.initState(); + var _colorController = widget.colorController; + if (_colorController != null) { + _colorController.changeColor = _changeColor; + } _initProxy(); } @@ -170,8 +186,21 @@ class _ModelViewerState extends State { ); } + Future _changeColor(String color) async { + var c = Completer(); + var webviewcontroller = await _controller.future; + await webviewcontroller + .evaluateJavascript('changeColor("$color")') + .then((result) { + c.complete(result); + }).catchError((onError) { + c.completeError(onError.toString()); + }); + return c.future; + } + String _buildHTML(final String htmlTemplate) { - return HTMLBuilder.build( + var htmlBuild = HTMLBuilder.build( htmlTemplate: htmlTemplate, backgroundColor: widget.backgroundColor, src: '/model', @@ -183,8 +212,10 @@ class _ModelViewerState extends State { autoRotateDelay: widget.autoRotateDelay, autoPlay: widget.autoPlay, cameraControls: widget.cameraControls, + enableColorChange: widget.enableColorChange, iosSrc: widget.iosSrc, ); + return htmlBuild; } Future _initProxy() async { From 8f5e60d189f38cab21f8f2b368dbedbcc05ef1b9 Mon Sep 17 00:00:00 2001 From: KevinR Date: Sun, 10 Jan 2021 18:59:44 +0100 Subject: [PATCH 2/4] removed print output --- lib/src/html_builder.dart | 3 +-- lib/src/model_viewer.dart | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/src/html_builder.dart b/lib/src/html_builder.dart index 1987757..7583e4a 100644 --- a/lib/src/html_builder.dart +++ b/lib/src/html_builder.dart @@ -85,8 +85,7 @@ abstract class HTMLBuilder { if (enableColorChange ?? false) { html.write(_buildColorChangeJSFunction()); } - - print(html.toString()); + return html.toString(); } diff --git a/lib/src/model_viewer.dart b/lib/src/model_viewer.dart index 696ba64..902389c 100644 --- a/lib/src/model_viewer.dart +++ b/lib/src/model_viewer.dart @@ -200,7 +200,7 @@ class _ModelViewerState extends State { } String _buildHTML(final String htmlTemplate) { - var htmlBuild = HTMLBuilder.build( + return HTMLBuilder.build( htmlTemplate: htmlTemplate, backgroundColor: widget.backgroundColor, src: '/model', @@ -215,7 +215,6 @@ class _ModelViewerState extends State { enableColorChange: widget.enableColorChange, iosSrc: widget.iosSrc, ); - return htmlBuild; } Future _initProxy() async { From b9dbe743e29d10a8a8390f2bd86b239ef3028d6c Mon Sep 17 00:00:00 2001 From: KevinR Date: Sat, 16 Jan 2021 19:02:48 +0100 Subject: [PATCH 3/4] added state callbacks to the ModelViewer widget --- lib/src/model_viewer.dart | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/src/model_viewer.dart b/lib/src/model_viewer.dart index 902389c..92b7340 100644 --- a/lib/src/model_viewer.dart +++ b/lib/src/model_viewer.dart @@ -31,7 +31,11 @@ class ModelViewer extends StatefulWidget { this.cameraControls, this.enableColorChange, this.colorController, - this.iosSrc}) + this.iosSrc, + this.onModelViewCreated, + this.onModelViewError, + this.onModelViewFinished, + this.onModelViewStarted}) : super(key: key); /// The background color for the model viewer. @@ -97,6 +101,21 @@ class ModelViewer extends StatefulWidget { /// via AR Quick Look. final String iosSrc; + /// Invoked once when the model viewer is created. + final VoidCallback onModelViewCreated; + + /// Invoked when the model viewer has finished loading the url. + final VoidCallback onModelViewStarted; + + /// Invoked when the model viewer has finished loading the url. + /// + /// Please note: This function is invoked when the url has finished loading, + /// but it doesn't represents the finished loading process of the model view. + final VoidCallback onModelViewFinished; + + /// Invoked when the model viewer has failed to load the resource. + final ValueChanged onModelViewError; + @override State createState() => _ModelViewerState(); } @@ -144,7 +163,9 @@ class _ModelViewerState extends State { final port = _proxy.port; final url = "http://$host:$port/"; print('>>>> ModelViewer initializing... <$url>'); // DEBUG - await webViewController.loadUrl(url); + await webViewController + .loadUrl(url) + .then((value) => widget.onModelViewCreated()); }, navigationDelegate: (final NavigationRequest navigation) async { //print('>>>> ModelViewer wants to load: <${navigation.url}>'); // DEBUG @@ -174,12 +195,15 @@ class _ModelViewerState extends State { return NavigationDecision.prevent; }, onPageStarted: (final String url) { + widget.onModelViewStarted(); //print('>>>> ModelViewer began loading: <$url>'); // DEBUG }, onPageFinished: (final String url) { + widget.onModelViewFinished(); //print('>>>> ModelViewer finished loading: <$url>'); // DEBUG }, onWebResourceError: (final WebResourceError error) { + widget.onModelViewError(error.description); print( '>>>> ModelViewer failed to load: ${error.description} (${error.errorType} ${error.errorCode})'); // DEBUG }, From 818d2d9b0547fb5d31bf590488b31a6ecc67804c Mon Sep 17 00:00:00 2001 From: KevinR Date: Sat, 16 Jan 2021 20:34:05 +0100 Subject: [PATCH 4/4] added `onModelIsVisisble` --- lib/src/html_builder.dart | 15 ++++++++++++++- lib/src/model_viewer.dart | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/lib/src/html_builder.dart b/lib/src/html_builder.dart index 7583e4a..9f2c34c 100644 --- a/lib/src/html_builder.dart +++ b/lib/src/html_builder.dart @@ -85,7 +85,9 @@ abstract class HTMLBuilder { if (enableColorChange ?? false) { html.write(_buildColorChangeJSFunction()); } - + + html.write(_buildModelVisibilityEventJSFunction()); + return html.toString(); } @@ -102,4 +104,15 @@ abstract class HTMLBuilder { '''; } + + static String _buildModelVisibilityEventJSFunction() { + return ''' + + '''; + } } diff --git a/lib/src/model_viewer.dart b/lib/src/model_viewer.dart index 92b7340..44f1b01 100644 --- a/lib/src/model_viewer.dart +++ b/lib/src/model_viewer.dart @@ -35,6 +35,7 @@ class ModelViewer extends StatefulWidget { this.onModelViewCreated, this.onModelViewError, this.onModelViewFinished, + this.onModelIsVisisble, this.onModelViewStarted}) : super(key: key); @@ -110,9 +111,13 @@ class ModelViewer extends StatefulWidget { /// Invoked when the model viewer has finished loading the url. /// /// Please note: This function is invoked when the url has finished loading, - /// but it doesn't represents the finished loading process of the model view. + /// but it doesn't represents the finished loading process of the model visibility. final VoidCallback onModelViewFinished; + /// Invoked when the model viewer has loaded the model and the model is visibile. + /// See: https://modelviewer.dev/docs/#entrydocs-loading-events-modelVisibility + final VoidCallback onModelIsVisisble; + /// Invoked when the model viewer has failed to load the resource. final ValueChanged onModelViewError; @@ -156,6 +161,17 @@ class _ModelViewerState extends State { return WebView( initialUrl: null, javascriptMode: JavascriptMode.unrestricted, + javascriptChannels: Set.from({ + JavascriptChannel( + name: 'messageIsVisibile', + onMessageReceived: (JavascriptMessage message) { + if (widget.onModelIsVisisble != null) { + if (message.message == 'true') { + widget.onModelIsVisisble(); + } + } + }), + }), initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, onWebViewCreated: (final WebViewController webViewController) async { _controller.complete(webViewController); @@ -163,9 +179,11 @@ class _ModelViewerState extends State { final port = _proxy.port; final url = "http://$host:$port/"; print('>>>> ModelViewer initializing... <$url>'); // DEBUG - await webViewController - .loadUrl(url) - .then((value) => widget.onModelViewCreated()); + await webViewController.loadUrl(url).then((value) { + if (widget.onModelViewCreated != null) { + widget.onModelViewCreated(); + } + }); }, navigationDelegate: (final NavigationRequest navigation) async { //print('>>>> ModelViewer wants to load: <${navigation.url}>'); // DEBUG @@ -195,15 +213,22 @@ class _ModelViewerState extends State { return NavigationDecision.prevent; }, onPageStarted: (final String url) { - widget.onModelViewStarted(); + if (widget.onModelViewStarted != null) { + widget.onModelViewStarted(); + } //print('>>>> ModelViewer began loading: <$url>'); // DEBUG }, onPageFinished: (final String url) { - widget.onModelViewFinished(); + if (widget.onModelViewFinished != null) { + widget.onModelViewFinished(); + } //print('>>>> ModelViewer finished loading: <$url>'); // DEBUG }, onWebResourceError: (final WebResourceError error) { - widget.onModelViewError(error.description); + if (widget.onModelViewError != null) { + widget.onModelViewError(error.description); + } + print( '>>>> ModelViewer failed to load: ${error.description} (${error.errorType} ${error.errorCode})'); // DEBUG },