Flutter listview根据索引自动锚点实现方案

一、需求分析

  • 开发一个动态可配置的订单评价页面,评价列表里有订单评价以及多个商品的评价信息,每个商品卡片又有标签、评分、文本框、图片四种评价项,这四个评价项都是后台可动态配置的,一个商品的评价项可以是一个至多个,以一个商品卡片为单位,每个卡片的高度是不一样的;
  • 由于一屏无法展示完所有商品列表的评价信息,需要页面可以自由滚动来查看填写所有的评价项;
  • 每个商品的评价项存在必填校验,点击提交按钮页面会自动定位到具体某个评价项没有填写完整的商品。

二、实现思路

  1. 这是一种很常见的滚动列表,使用ListView.builder来构建最方便,界面实现很容易,关键在于定位功能的实现,也就是点击提交按钮后要准确定位到某个商品卡片;
  2. 如何定位到我们想跳转到的goods卡片呢?针对ListView.builder,如果能通过index索引跳转到指定位置就完美实现需求了,android中有ListView.setSelection(int position)方法,iOS里有tableview scrollToRowAtIndexPath等方式快速实现,flutter没有现成方法调用,但是我们可以用另一种方式实现同样的效果。
  3. 我们可以通过拿到某个商品卡片的位置信息,然后通过scrollcontroller.jumpTo跳转就可以实现需求了,要拿到某个商品卡片的位置信息,可以先给每个商品卡片widget指定一个globalKey,通过key使用以下方法就可以拿到每个商品卡片的位置信息offset了
  4. 将每个商品子视图的位置信息offset保存成一个List,对应商品列表的索引,现在我们就拿到了每个商品子视图的位置信息

三、解决方案

1. ListView.builder() ❌

采用的是懒加载模式,只有滚动到屏幕内的内容才会执行builder方法构建列表子条目,所以一进来没办法拿到未加载子条目的视图信息,也就没方法拿到未加载子条目的位置信息

2. Listview() ❌

一次加载出所有子条目视图,没有出现在屏幕中的子条目仍然会被ListView所创建,对于那些长列表或者需要较昂贵渲染开销的子组件,使用不当可能会引起性能问题甚至卡顿,但是仅仅对于我们的需求来说,一次下单的商品不会很多,正常情况下都在个位数以内,性能影响几乎可以忽略不计,貌似符合需求;
实际使用发现Listview确实能一次加载所有的子条目,但是却没办法拿到未出现载屏幕中子条目的位置信息,需要将剩余的子条目滑动到屏幕中,才能正常拿到所有子条目的位置信息,使用中有bug

3. SingleScrollerView(child:Column()) ✅

这种方式等同于Listview,能够一次加载出所有子条目的视图信息,但是它跟Listview的区别在于它能同时拿到未出现在屏幕中的子条目位置信息

四、核心逻辑

  • 通过给SingleChildScrollView绑定scrollerController,使用scrollerController.jumpTo方法跳转到指定位置,jumoTo接受的是一个double类型,也就是Y轴dy的偏移值,传入列表要跳转到的指定索引postions[i].dy的y轴偏移距离就可以实现滚动到列表指定item位置的需求了
  • 因为界面存在AppBar,需要减去appbar的高度来修正偏移误差(如果没有appbar可以省略这一步)
  • 这里有一个需要注意的问题,jumpTo滚动基于的坐标轴原点是屏幕当前滚动到位置的相对原点还是所渲染界面的绝对原点,答案是后者,但是通过renderObject拿到的位置信息是基于当前在屏幕中视图左上角为坐标原点就算得出(不论是否有滚动), 滚动界面会导致坐标原点一直在变,这会导致每次计算出的子条目位置不一样,定位精度不准确,所以在计算位置的时候需要考虑到当前视图基于绝对原点已滚动的距离,也就是需要加上scrollerController.offset的值,这样就校正了每个子条目的位置信息,不论怎么滚动,每次定位到的子条目都是绝对位置

五、最终效果

不论当前订单评价页面滚动到任一位置,点击提交评价,页面就会自动定位到必填校验项为空的商品卡片,并提示未填校验信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容