配置擴展javascript
Echarts 有很豐富的 擴展 ,包括圖表、地圖、WebGL 等,在 Web 開發中,它們能夠以腳本的形式引入代碼,從而擴展 Echarts 的功能。爲知足開箱即用, flutter_echarts 內置了最新版的 Echarts 腳本,無需額外引入,同時提供了 extensions
參數,方便使用者引入所需的擴展腳本。 extensions
參數類型爲字符串數組,使用者可直接拷貝腳本做爲字符串到源碼中,避免了文件讀寫操做和繁瑣的 asset 目錄。html
封裝功能性的組件,其易用性每每比完備性更重要,要讓任意水平的開發者都能開箱即用。Echarts 自己在設計時也是遵循易用性的原則,儘量的將全部配置工做,交給 option
這一個參數 去完成( 詳見論文 )。所以 flutter_echarts 在設計時也儘可能簡化組件參數:java
optiongit
Stringgithub
字符串形式的 JavaScript Echarts Option。Echarts 圖表主要就是經過這個參數配置的。你能夠經過 dart:convert 中的 jsonEncode()
來轉換 Dart 對象類型的數據:web
source: ${jsonEncode(_data1)},
複製代碼
因爲 JavaScript 沒有'''
符號,你可使用它來包裹字符串,以省掉一些引號的轉義:apache
Echarts(
option: ''' // option string ''',
),
複製代碼
extraScriptjson
Stringredux
在 Echarts.init()
和任意 chart.setOption()
之間執行的 JavaScript 腳本。在組件中咱們已經內置了一個 名爲 Messager
的 JavascriptChennel,因此你可使用這個標識符來進行 JavaScript 向 Flutter 的通訊:數組
extraScript: ''' chart.on('click', (params) => { if(params.componentType === 'series') { Messager.postMessage('anything'); } }); ''',
複製代碼
onMessage
void Function(String)
處理 extraScript
中 Messager.postMessage()
發送的消息的函數。
extensions
List
從 Echarts 擴展中拷貝的腳本字符串組成的數組,好比各類組件、WebGl、語言等。能夠從 這裏 下載。將它們做爲原始字符串(raw string)引入:
const liquidPlugin = r''' // copy from liquid.min.js ''';
複製代碼
目前僅有以上 4 個參數,控制更新等由內部機制完成,爭取作到用起來就像個簡單的表現型 StatelessWidget,只要使用者熟悉 Echarts 自己而不須要額外的學習成本。
固然,若是有建議或要求,請發起 issue 。
html 的加載
對於跨平臺的開發方案,因爲不一樣的底層操做系統,文件資源目錄一直是個麻煩的事情,在 React Native 中有時甚至必須手動將 html 拷貝到 Android 對應的目錄。Flutter 雖然有了完善的 asset 系統,但也須要額外的依賴和配置。直接將本地 html 做爲源碼中的文本字符串加載是解決這些問題的好辦法,webview_flutter 的官方示例也比較推薦用這種辦法處理本地 html 。
所以咱們將模板 html 、Echarts 腳本、擴展腳本、初始化邏輯等在組件初始化時拼接成字符串,做爲 uri 資源供 WebView 加載:
@override
void initState() {
super.initState();
_htmlBase64 = 'data:text/html;base64,' + base64Encode(
const Utf8Encoder().convert(_getHtml(
echartsScript,
widget.extensions ?? [],
widget.extraScript ?? '',
))
);
_currentOption = widget.option;
}
...
@override
Widget build(BuildContext context) {
return WebView(
initialUrl: _htmlBase64,
...
);
}
複製代碼
值得注意的是,做爲 uri 資源的字符串,是有一些特殊字符限制的,所以加載時咱們將字符串轉爲 Base64 編碼。
這裏有一個小技巧因爲 JavaScript 中沒有 '''
這個符號,所以在 Dart 中用 '''
包裹 JavaScript 腳本字符串能夠減小不少轉義工做。
圖表更新
響應式更新基本的實現機制就是在 State.didUpdateWidget 方法中經過setOption
通知 Echarts 更新圖表:
void update(String preOption) async {
_currentOption = widget.option;
if (_currentOption != preOption) {
await _controller?.evaluateJavascript(''' chart && chart.setOption($_currentOption, true); ''');
}
}
@override
void didUpdateWidget(Echarts oldWidget) {
super.didUpdateWidget(oldWidget);
update(oldWidget.option);
}
複製代碼
這其中比較麻煩的是在組件剛剛初始化的時候。
咱們知道 WebView 加載 html 和外部數據的獲取都是異步的,事先並不知道誰會先完成。WebView 初始化時生命週期的順序是:
onWebViewCreated --> 加載html --> onPageFinished
複製代碼
而 WebViewController 通常是在 onWebViewCreated 中獲取的。換言之,當組件拿到 WebViewController 時,並不能確保 WebView 中的 html 已經加載完成,因此 didUpdateWidget
不能僅依據是否已經拿到 WebViewController 決定是否能夠更新了。
解決辦法是將「外部數據更新時更新圖表」解耦爲「外部數據更新時更新內部 _currentOption 」 和 」當須要更新圖表時調用 _currentOption 「兩步,從而確保 html 加載完成前獲取的數據也能被記錄更新:
String _currentOption;
void init() async {
await _controller?.evaluateJavascript(''' chart.setOption($_currentOption, true); ''');
}
void update(String preOption) async {
_currentOption = widget.option;
...
}
@override
Widget build(BuildContext context) {
return WebView(
...
onPageFinished: (String url) {
init();
},
...
);
}
複製代碼
內置信道
webview_flutter 提供了 javascriptChannels 參數,能夠設置多路命名信道。不過爲了使不熟悉 webview_flutter 的使用者也能快速上手, flutter_echarts 並無暴露這個參數來管理通訊,而是內置創建了一個名爲「 Messager 」的信道:
@override
Widget build(BuildContext context) {
return WebView(
...
javascriptChannels: <JavascriptChannel>[
JavascriptChannel(
name: 'Messager',
onMessageReceived: (JavascriptMessage javascriptMessage) {
widget?.onMessage(javascriptMessage.message);
}
),
].toSet(),
);
}
複製代碼
使用者若是有多種事件須要通訊,能夠像 redux action 那樣進行設置:
chart.on('click', (params) => {
if(params.componentType === 'series') {
Messager.postMessage(JSON.stringify({
type: 'select',
payload: params.dataIndex,
}));
}
});
複製代碼