项目中的一些模块用flutter从新开发后在两次云测和灰度少许渠道后发现了些问题,分为两类:java
一个是非编译/运行时问题,即dart语法使用错误或widget布局错误使用出现的问题android
由于这类问题对于全部人几乎都会遇到,再也不详细说解决方案,只简单罗列下我遇到的,提醒别人注意便可:git
须要显示的内容超出了widget容器或屏幕的范围会出现,通常放入滚动容器中可解github
由于官方image组件提供了相似scaleType的属性:fit,当出现图片宽大于高,或高大于宽时,fitWidth或fitHeight就直接替换为BoxFit.cover吧,能够作下兼容,另外由于原生组件没有提供磁盘缓存功能,能够本身实现或直接加载个三方库,比较推荐 flutter_cached_network_image ,而后能够封装本身的imageloader了, 我写了个简易的例子web
class FlutterAlign extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: Align(
alignment: Alignment(-1, 1),
child: Container(
child: Text(
"Hello",
),
),
),
);
}
}
复制代码
flutter做为一款UI框架,使用原则貌似是只要是实现不复杂,原生非不可替代的控件都应该用flutter来作,不过toast这个东西为了保持跟原生界面一致性,仍是调用原生吧,不过我遇到的一个小问题是:有个界面有可能须要快速频繁提示toast,并且原生代码中使用的toast是自定义的不排队toast(继承Toast),因为内部机制,快速插入toast队列信息会形成系统等待,短期再也不响应toast弹出,解决方案能够是按钮控制节流(throttle)或防抖(debounce),也能够优化下toast排队机制。 不过最后仍是决定用flutter作一个备用吧,网上的多数方案是使用Overlaysshell
Overlays经过把子widget插入到overlay的stack里面, 让依赖它的子widget能够浮在其它的可见元素上面。OverlayEntry能够管理漂浮的widgets。(一个OverlayEntry就是一个层)
复制代码
我贴一个简单实现api
import 'package:flutter/material.dart';
class FlutterToast {
static OverlayEntry _overlayEntry; //toast靠它加到屏幕上
static bool _showing = false; //toast是否正在showing
static DateTime _startedTime; //开启一个新toast的当前时间,用于对比是否已经展现了足够时间
static String _msg;
static void toast(
BuildContext context,
String msg,
) async {
assert(msg != null);
_msg = msg;
_startedTime = DateTime.now();
//获取OverlayState
OverlayState overlayState = Overlay.of(context);
_showing = true;
if (_overlayEntry == null) {
_overlayEntry = OverlayEntry(
builder: (BuildContext context) => Positioned(
//top值,能够改变这个值来改变toast在屏幕中的位置
top: MediaQuery.of(context).size.height * 2 / 3,
child: Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 80.0),
child: AnimatedOpacity(
opacity: _showing ? 1.0 : 0.0, //目标透明度
duration: _showing
? Duration(milliseconds: 100)
: Duration(milliseconds: 400),
child: _buildToastWidget(),
),
)),
));
overlayState.insert(_overlayEntry);
} else {
_overlayEntry.markNeedsBuild();
}
await Future.delayed(Duration(milliseconds: 2000)); //等待两秒
if (DateTime.now().difference(_startedTime).inMilliseconds >= 2000) {
_showing = false;
_overlayEntry.markNeedsBuild();
}
}
static _buildToastWidget() {
return Center(
child: Card(
color: Colors.black,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
child: Text(
_msg,
style: TextStyle(
fontSize: 14.0,
color: Colors.white,
),
),
),
),
);
}
}
复制代码
能够根据须要调节UI,让它和你的原生toast保持一致,可是有个问题,它弹出后会被键盘覆盖,因为机制问题,因此暂时貌似还不能解决,也因如此,flutter这种toast默认在中间弹出防止被盖住。缓存
发生场景:bash
String name;
Container(
padding: EdgeInsets.only(top: 5.0),
child: Text(
"名字:" + name ?? "",
style: TextStyle(fontSize: 14.0, color: const Color(0xFF878E9F)),
),
),
复制代码
对非空判断符号使用的错误网络
name ?? "" , 独立使用没有问题,不过拼接到字符串中须要改为这样:
"名字:" + (name ?? "")
强大的网络请求Dio库在请求过程当中发生错误,入无网络或者非200的错误码响应等异常只会在控制台中打印出如下相似信息
Unhandled Exception: DioError [DioErrorType.DEFAULT]: SocketException: Failed host lookup:
复制代码
假如你用
try{
dio.post()...
}on DioError catch (e) {
print(e.message);
}
复制代码
没法捕获到请求内部异常,查看源码可知 dio.post的返回值是Future, 而且它也使用的是catchError((err) => throw_assureDioError(err)); 来捕获异常。
因此咱们使用的时候也要使用Future的api来捕获
dio.post()
.then((resp) => {
...
}).catchError((e){
print(e.message);
});
复制代码
--------------------------------------分割线---------------------------------------
另一类问题就是直接debug或release报错,或者机型适配出现的问题了,多出如今dart VM或SDK中
问题发生于升级flutter sdk以后,看上边的日志Wrong full snapshot version能够猜想:升级了sdk,可是以前已生成的编译产物仍是旧的,不匹配,须要从新build一下,搜索了issue后找到了个方案:
1) removing the content of flutter/bin/cache
2) then running flutter upgrade again
3) then running flutter clean prior to flutter run
复制代码
E/flutter: [ERROR:flutter/runtime/dart_vm.cc(259)] VM snapshot must be valid. A/flutter: [FATAL:flutter/shell/common/shell.cc(212)] Check failed: vm. Must be able to initialize the VM. --------- beginning of crash
升级sdk到1.4以后解决
由于是混合开发,flutter做为插件支持原生项目,因此须要支持什么样的cpu架构视原生项目状况而定,咱们的平台底层库只支持
ndk {
abiFilters 'armeabi'
}
复制代码
对于flutter SDK的修改:咱们只须要修改android-arm、android-arm-profile和android-arm-release下的flutter.jar,将其中的lib/armeabi-v7a/libflutter.so移动到lib/armeabi/libflutter.so便可:
1 先进入目录
cd $FLUTTER_ROOT/bin/cache/artifacts/engine
2 而后执行
for arch in android-arm android-arm-profile android-arm-release; do
pushd $arch
cp flutter.jar flutter-armeabi-v7a.jar # 备份
unzip flutter.jar lib/armeabi-v7a/libflutter.so
mv lib/armeabi-v7a lib/armeabi
zip -d flutter.jar lib/armeabi-v7a/libflutter.so
zip flutter.jar lib/armeabi/libflutter.so
popd
done
复制代码
注: 全部的x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的.so文件,所以彷佛移除其余ABIs的.so文件是一个减小APK大小的好技巧。但事实上并非:这不仅影响到函数库的性能和兼容性,x86设备可以很好的运行ARM类型函数库,但并不保证100%不发生crash,特别是对旧设备。64位设备(arm64-v8a, x86_64, mips64)可以运行32位的函数库,可是以32位模式运行,在64位平台上运行32位版本的ART和Android组件,将丢失专为64位优化过的性能(ART,webview,media等等)。
java.lang.IllegalStateException: Reply already submitted at io.flutter.view.FlutterNativeView$PlatformMessageHandlerImplIncomingMethodCallHandler$1.success(MethodChannel.java:204)
发生场景: Flutter 经过 eventMethod 调用原生端方法,该方法是在switch case 语句段中,由于没有写break,大概致使了 MethodChannel.Result 返回给Flutter参数时调用了屡次,直接把switch case 中的break补充上就没有再复现,不过GitHub中的issue场景貌似要复杂一些,能够参考github