纯 Flutter 开发应该不会遇到这个问题,但是如果是已有一个原生工程中集成 Flutter,在 Activity 里面内嵌 FlutterView,当屏幕旋转、键盘弹出等导致 FlutterView 容器大小出现突变时,Flutter 界面会有很明显的拉伸。
先说解决方案
固定宽高
不修改 Flutter 引擎最稳妥的解决方案。宽高不要设置为 MATCH_PARENT
(根据实际情况,不会突变的可以设置为 MATCH_PARENT
), 而是设置为固定值。在 Flutter 侧底部或者右边留空白 Container
,通过动态控制 Container
的宽高来实现业务效果,如业务视图的高度变化。如果是键盘场景,原生通过把键盘高度传递给 Flutter,让空白 Container
的高度跟键盘高度一致来控制输入框的弹起。
示例:
示例中的场景是一个可以拖动大小变化的底部弹出框,Flutter 的内容包含标题栏、聊天内容、输入框根据以上的解决方案,在 Flutter 的底部加一个空白 (高度为 blankViewHeight
)
|
动态控制空白区域的高度:
channel.setMethodCallHandler((call) async { |
原生侧固定 FlutterView 高度固定为 Activity 的高度,BottomSheetDialog 变化时,计算好 Flutter 底部需要留出来的高度,给到 Flutter:
mBottomSheetDialog.behavior.addBottomSheetCallback(object : BottomSheetCallback() { |
ScrollView 嵌套 FlutterView
还是固定 FlutterView 的高度,通过监听屏幕旋转,addOnGlobalLayoutListener
键盘弹出之后确定 FlutterView 的高度,直接设置即可。这种好处是可以利用 ScrollView 对键盘的处理,键盘不会遮挡 Flutter 输入框,不需要 Flutter 配合处理,缺点是处理 FlutterView 连续变化还是有问题。
其它知识点:
通过查看调试 FlutterView 的源码可以发现 ,触发
onSizeChanged
或者onApplyWindowInsets
都可以让 Flutter 进行重绘。windowInset
可以实现原生侧让 Flutter 留白,可以按需使用最常见的
setLayoutParams
可以让 FlutterView 的大小变化,但是这个操作太快的话,Flutter 绘制会异常导致大小突变,如果我们给setLayoutParams
加一个 16ms 的防抖,FlutterView 可以正常实现动画效果。这种方案适合代码主动给 FlutterView 做动画,如果是用户手指拖动导致的变化就不行,因为拖快了之后位移的 x,y太大,还是16ms 才能绘制一帧的话也会界面异常
例如给 FlutterView 做一个减小
400dp
高度的动画:ValueAnimator.ofInt(lp.height, lp.height - dip2px(this@MainActivity, 400))
var lastUpdateTime = System.currentTimeMillis()
animator.addUpdateListener { valueAnimator ->
val currentTime = System.currentTimeMillis()
if (currentTime - lastUpdateTime < 16) {
return@addUpdateListener
}
lastUpdateTime = currentTime
val lp1 = binding.rootView.layoutParams
lp1.height = valueAnimator.animatedValue as Int
binding.rootView.layoutParams = lp1
}
animator.duration = 300
animator.start()