项目里一直在用flutter_echarts作为图表展示。 今天发现一个bug,ios如果echarts不在当前屏幕,则echarts会显示白屏幕。
然后在github上发现有人有相同的问题: github.com/entronad/fl...
令人懊恼的是这个项目已经不再维护了。没办法,只有自己动手丰衣足食啦。
重现这个问题可以将Echarts放在ScrollView里,并且确保它不在当前屏幕。我看这个issues里说可以刷新两次。但治标不治本偶尔仍然会出现白屏。
由于flutter_echarts是通过webview展示的,刚开始不管如何修改都没办法。后来将内置的webview修改成inappwebview。发现ios里inappwebview会回调 onContentSizeChanged函数。将window size从0设置成具体的值。我猜测是因为ios webview如果判断是离屏的则webview的size是0,这个时候去初始化图表控件尺寸就会出问题。解决办法也很简单,就是当回调 onContentSizeChanged时再去初始化。
以下为修改后的代码:
dart
import 'dart:convert';
import 'dart:io';
import 'package:bdh_smart_agric_app/utils/log.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter_echarts/echarts_script.dart' show echartsScript;
/// <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0, target-densitydpi=device-dpi" /><style type="text/css">body,html,#chart{height: 100%;width: 100%;margin: 0px;}div {-webkit-tap-highlight-color:rgba(255,255,255,0);}</style></head><body><div id="chart" /></body></html>
/// 'data:text/html;base64,' + base64Encode(const Utf8Encoder().convert( /* STRING ABOVE */ ))
const htmlBase64 =
'PCFET0NUWVBFIGh0bWw+PGh0bWw+PGhlYWQ+PG1ldGEgY2hhcnNldD0idXRmLTgiPjxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAsIHVzZXItc2NhbGFibGU9MCwgdGFyZ2V0LWRlbnNpdHlkcGk9ZGV2aWNlLWRwaSIgLz48c3R5bGUgdHlwZT0idGV4dC9jc3MiPmJvZHksaHRtbCwjY2hhcnR7aGVpZ2h0OiAxMDAlO3dpZHRoOiAxMDAlO21hcmdpbjogMHB4O31kaXYgey13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjpyZ2JhKDI1NSwyNTUsMjU1LDApO308L3N0eWxlPjwvaGVhZD48Ym9keT48ZGl2IGlkPSJjaGFydCIgLz48L2JvZHk+PC9odG1sPg==';
class InAppWebViewEcharts extends StatefulWidget {
InAppWebViewEcharts(
{Key? key,
required this.option,
this.extraScript = '',
this.onMessage,
this.extensions = const [],
this.theme,
this.captureAllGestures = false,
this.captureHorizontalGestures = false,
this.captureVerticalGestures = false,
this.onLoad,
this.onWebResourceError,
this.reloadAfterInit = false})
: super(key: key);
final String option;
final String extraScript;
final void Function(String message)? onMessage;
final List<String> extensions;
final String? theme;
final bool captureAllGestures;
final bool captureHorizontalGestures;
final bool captureVerticalGestures;
final void Function(WebViewController)? onLoad;
final void Function(WebViewController, Exception)? onWebResourceError;
final bool reloadAfterInit;
@override
_EchartsState createState() => _EchartsState();
}
class _EchartsState extends State<InAppWebViewEcharts> {
InAppWebViewController? inAppWebViewController;
String? _currentOption;
String? html;
@override
void initState() {
super.initState();
_currentOption = widget.option;
html = utf8.fuse(base64).decode(htmlBase64);
}
void init() {
final extensionsStr = widget.extensions.isNotEmpty ? widget.extensions.reduce((value, element) => value + '\n' + element) : '';
final themeStr = this.widget.theme != null ? '\'${this.widget.theme}\'' : 'null';
inAppWebViewController?.evaluateJavascript(source: '''
$echartsScript
$extensionsStr
var chart = echarts.init(document.getElementById('chart'), $themeStr);
${widget.extraScript}
chart.setOption($_currentOption, true);
''').then((v) {
Log.d("init data success $v");
});
}
Set<Factory<OneSequenceGestureRecognizer>> getGestureRecognizers() {
Set<Factory<OneSequenceGestureRecognizer>> set = {};
if (widget.captureAllGestures || widget.captureHorizontalGestures) {
set.add(Factory<HorizontalDragGestureRecognizer>(() {
return HorizontalDragGestureRecognizer()
..onStart = (DragStartDetails details) {}
..onUpdate = (DragUpdateDetails details) {}
..onDown = (DragDownDetails details) {}
..onCancel = () {}
..onEnd = (DragEndDetails details) {};
}));
}
if (widget.captureAllGestures || widget.captureVerticalGestures) {
set.add(Factory<VerticalDragGestureRecognizer>(() {
return VerticalDragGestureRecognizer()
..onStart = (DragStartDetails details) {}
..onUpdate = (DragUpdateDetails details) {}
..onDown = (DragDownDetails details) {}
..onCancel = () {}
..onEnd = (DragEndDetails details) {};
}));
}
return set;
}
void update() {
_currentOption = widget.option;
inAppWebViewController?.evaluateJavascript(source: '''
try {
chart.setOption($_currentOption, true);
} catch(e) {
}
''');
}
void loadData() {
inAppWebViewController?.loadData(data: html ?? "");
}
@override
void didUpdateWidget(InAppWebViewEcharts oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.option != widget.option) {
update();
}
}
@override
void dispose() {
// inAppWebViewController?.dispose();
inAppWebViewController = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
return InAppWebView(
initialSettings: InAppWebViewSettings(
allowsInlineMediaPlayback: false,
allowsAirPlayForMediaPlayback: false,
allowsPictureInPictureMediaPlayback: false,
allowsLinkPreview: false,
mediaPlaybackRequiresUserGesture: false,
underPageBackgroundColor: Colors.transparent,
transparentBackground: true,
verticalScrollBarEnabled: false,
horizontalScrollBarEnabled: false,
disableHorizontalScroll: true,
disableVerticalScroll: true,
disallowOverScroll: true,
displayZoomControls: true,
clearCache: true,
supportZoom: false,
builtInZoomControls:false,
geolocationEnabled:false,
cacheEnabled: false),
onWebViewCreated: (controller) async {
inAppWebViewController = controller;
loadData();
},
onContentSizeChanged: (controller, oldContentSize, newContentSize) {
loadData();
},
onZoomScaleChanged: (controller, oldScale, newScale) {
loadData();
},
onLoadStop: (controller, url) {
init();
},
gestureRecognizers: getGestureRecognizers(),
);
}
}
上面代码的修改点有:
- 将flutter_webview替换成了inappwebview
- 在onContentSizeChanged 和 onZoomScaleChanged时重新加载了数据