【Flutter】实战问题集锦(四)

585 阅读3分钟

ListView的Widget被更新

在业务开发中ListView每个Cell有一个点赞功能。点赞动画由AnimationController控制,通过didUpdateWidget判断点赞值执行动画。问题在当删除未点赞状态Cell,被删除的未点赞Cell下面是点赞Cell上移,而上移点赞Cell会再执行一次动画。

image.png

@override
  void didUpdateWidget(ItemCell oldWidget) {
    if (oldWidget.value != value) {
      if (_controller != null) {
        if (value) {
          _controller?.duration = Duration(seconds: 2);
          _controller?.forward();
        } else {
          _controller?.value = 0;
          _controller?.stop(canceled: true);
        }
      }
    }
    super.didUpdateWidget(oldWidget);
  }

从代码层面上看只有widget的value值发生改变了才会执行动画。debug后发现oldWidget的value并不是该Cell原来的值,而是被删除Cell的值。由此可知ListView的Cell应该存在复用问题。

解决方案

  1. ListView每个itemCell增加Key保证唯一性。 image.png 🚀Demo案例🚀

TextField在iOS平台输入中文

当TextField设置字数限制时,在iOS上会出现输入拼音字母会限制中文生成。比如限制输入字数是4个,当你想输入中文”你好“,无法输入完整拼音字符”nihao“。

官方Issue

解决方案

  1. 简单处理,不设置字数输入限制将字数限制交给业务方实现。
  2. 暂无完美解决方案;一个开源库解决方案是将iOS输入框字符判断通过原生插件实现:CustomTextField

正确使用MethodChannel

在业务开发中遇到一个沙雕问题,插件回调结果报错异常。看了下插件代码才知道原来的同学以result.error返回了结果。

image.png 业务错误信息应该也是以result.success返回才对,业务异常也需要返回携带错误信息的回参。由于历史遗留问题最终是在Flutter层做try-catch保护。

result.error并不会返回和success一样的Map<String,String>对象而是一个Flutter层异常。只能通过try-catch获取到。

try{
  Map map = await FlutterAppPlugin.testChanelFail();
  Scaffold.of(context).showSnackBar(
      SnackBar(content: Text("testChannel ${map.toString()}"))
  );
}catch(e){
  Scaffold.of(context).showSnackBar(
      SnackBar(content: Text("testChannel error ${e.toString()} "))
  );
}

解决方案

1.通过try-catch方式在Flutter层捕获异常来知悉原生层面失败异常。

2.原生层统一返回result.success在回参字段中判断是否失败。 🚀Demo案例🚀

TextField获取/失去焦点以及软键盘获取

业务开发中遇到的输入框一些奇葩问题:

  1. 让输入框失去焦点:FocusScope.of(context).requestFocus(new FocusNode());
  2. 主动唤起软键盘: SystemChannels.textInput.invokeMethod('TextInput.show');
  3. 获取输入框光标位置:textEditingController.selection中的textSelection.start和textSelection.end。当输入框失去焦点时start和end会变成-1。这时最好通过一个变量记住原来的start和end。对于自定义emoji或是@用户等需求时可在原光标位置插入数据。

非循环Gif的播放问题

已知Gif有loop属性,有播放次数限制的gif会在播放次数到达之后停止。但重新加载图片应该是可以重新播放。但Flutter组件Image存在不再播放的情况,猜测是图片缓存导致重新加载后不再播放。

image.png

在页面dispose方法清缓存重新进页面gif可重新播放,由此可判断是图片缓存导致。

 PaintingBinding.instance.imageCache.clear();

在image_stream.dart的_handleAppFrame找到Gif播放限制,当循环次数completedCycles超过 编码器次数_codec!.repetitionCount则不在绘制下一帧图像。 image.png 由此可知在image_provider.dart的resolveStreamForKey缓存中取出已有MultiFrameImageStreamCompleter在播放循环次数超过后取出时gif就不会再播放。

解决方案

  1. 简单解决办法执行PaintingBinding.instance.imageCache.clear();在不需要时暴力清缓存,但会误伤所有缓存。
  2. 重写image_provider.dart方法resolveStreamForKey逻辑,在缓存中取出ImageStreamCompleter对_framesEmitted参数置为0。

🚀Demo案例🚀