likes
comments
collection
share

Flutter- 在GetX仿fish-redux ListAdapter实现items懒加载

作者站长头像
站长
· 阅读数 53

Flutter- 在GetX仿fish-redux ListAdapter实现items懒加载

前景

公司前端组在21年中旬经过fish-redux烂尾行为决定放弃使用,转为投入GetX的怀抱.GetX的便利这里就不在赘述,下面分享在实际项目中遇到的一些问题及解决办法:

fish-redux仓库 GetX仓库

起源

  1. 在业务开发中常常遇到一些业务复杂的列表,而Item子项牵扯到许多通用组件,而往往也需要知道Item子项的生命周期,比如在我们的业务开发中,Item项会在自己创建的时候去获取一些数据,并跟其他Item隔离,在fish-redux中,我们可以使用adapter,而在GetX里还没有类似组件可以直接用.

  2. Adapter 具体理论见文档,简单来说adapter也是一个component,component产物是一个Widget,adapter产物是ListAdapter可生成一组Widget.

  3. 在转使用GetX中非常不适应没有ListAdapter的情况,想在Item抽离成组件,又想使用GetXController去控制状态,Item的GetController也注册到全局依赖管理,结果就是List跟Item数据没有保持一致,违背了单一数据源. 在fish-redux里,使用connector连接器使得父数据与子数据保持一致.那么只需要一个connector打通数据,再加上Getview实际本身就是StatelessWidget即可以在结构上实现仿ListAdapter

结构

Flutter- 在GetX仿fish-redux ListAdapter实现items懒加载

  • 设计思路就是列表里没有实际数据,只存了管理的tag,实际数据由子item组件保存,列表通过tag操作,获取实际数据.这样下来,列表就只负责列表项的修改以及渲染,子Item管理自己部分数据修改以及渲染

  • item项依赖使用lazyPut注入,那么只有在界面需要渲染到或者逻辑使用到子项时才会通过Get.find(tag:itemTag);实际调用_initDependencies见源码加载到依赖管理里

  • 实际上我们需要实现的是一个ListConnector而非ListAdapter,不管是List还是Items依然保持原结构,只是将数据转换成标签加以关联

具体实现

  1. 要实现的功能已经很明确:

    • 一个Tag List.操作它同时可能伴随子项dependency操作等,比如新增,删除,列表绑定,当然还得是RxList,列表本身的变化也得通知界面刷新
    • 一个ItemBindings,主要是item注册自己的dependencies,与GetX的[Bindings].dependencies()(github.com/jonataslaw/…)不同的地方在于itemDependencies(T itemState,String tag)

    tag : item在列表中的标记,列表想操作也是通过这个tag,所有,在这个方法put的dependency一定要加上这个tag,不然列表会找不到 itemState : 子item的数据

    • 一个mixin 去组装实现TagList和 ItemBindings,业务listGetController继承,注入itemState2Tag(T itemState)转换方法和一些必要参数
  2. RTList: 待通知机制Tag List

    /// Create a list similar to `List<T>`
    class RTList<E> extends ListMixin<E> with NotifyManager<List<String?>> implements RxInterface<List<String?>> {
    RTList({required this.item2LogicTag, required this.itemDependencies, required this.tag2ItemState}) {
        _tagValues = [];
    }
    ///通过标签获取实体
    final Tag2ItemFunc<E> tag2ItemState;
    
    /// 通过实体获取标签
    final Item2LogicTagFunc<E> item2LogicTag;
    
    ///列表标签
    late final List<String?> _tagValues;
    
    /// 通过标签注册方法
    late final ItemDependencies<E> itemDependencies;
    
    ...
    }
    

    因为是操作的标签,所以需要注入一些必要的转换以及初始方法,下面就是实际使用:

    @override
    void add(E element) {
        //拿到itemState之后转换为tag
        final tag = item2LogicTag(element);
        //判断是否是重复添加
        if (!_tagValues.contains(tag)) {
        //不是则注册依赖(注意,这里如果itemDependencies是lazyPut的话,实际的itemLogic还没有创建,等待find)
        itemDependencies(element, tag);
        }
        //将tag添加到数组
        _tagValues.add(tag);
        //变化通知监听
        subject.add(_tagValues);
    }
    
    

    上面描述的是当数组变化tag的操作,那要如何绑定通知呢

    @override
    E operator [](int index) {
        //GetX挂载监听
        RxInterface.proxy?.addListener(subject);
        //用tag查询实际的itemState返回
        return tag2ItemState(_tagValues[index]!);
    }
    
    

    其他修改,查询方法同理;

  3. ItemBindings: item注册本身和自己使用的组件的依赖

    ///listItem 依赖bind
    abstract class ListItemBindings<T> {
    ///item注册本身和自己使用的组件的依赖
    void itemDependencies(T item, String tag);
    }
    
  4. RxListMixin: 组装混入结构,这个只是简单封装,还有很大优化空间

///列表自加载itemLogic mixin
///[T] list item state
///[P] list item bindings
///[K] list item logic
mixin RxListMixin<T, P extends ListItemBindings<T>, K extends GetStateController> on DisposableInterface implements RxConnector<T> {
 late final RTList<T> itemList;
 
 bool _removeItemLogicsWhenClose = false;

 /// 是否在onClose时删除itemLogic
 /// 是onClose不一定是退路由触发,itemLogic没有remove
 /// 退路由一定触发onClose
 set removeItemLogicsWhenClose(bool removeItemLogicsWhenClose) => _removeItemLogicsWhenClose = removeItemLogicsWhenClose;

 ListItemBindings<T>? _itemLazyBindings;

 set itemBindings(ListItemBindings<T> itemBindings) => _itemLazyBindings = itemBindings;

 Route? route;

 @override
 void onInit() {
   super.onInit();
    ...
   itemList = RTList(item2LogicTag: item2LogicTag, itemDependencies: _itemLazyBindings!.itemDependencies, tag2ItemState: tag2ItemState);
 }


 @override
 void onClose() {
   super.onClose();
   if (_removeItemLogicsWhenClose) {
     itemList.forEach((element) {
       final tag = itemList.item2LogicTag(element);
       Get.delete<K>(tag: tag, force: true);
     });
   }
 }

 ///通过索引获取标签
 String? getItemTagByIndex(int index) => itemList._tagValues[index];
}

///连接器
///[T] list item 泛型
///[P] list item logic 泛型
abstract class RxConnector<T> {
 String item2LogicTag(T t);
}

使用

list目录结构: Flutter- 在GetX仿fish-redux ListAdapter实现items懒加载 主体为list/和item/,需要关注的:

list/logic.dart

class TestListLogic extends GetxController with RxListMixin<TestListItemState, TestListItemBinding, TestListItemLogic> {
  TestListLogic() : super() {
    super.itemBindings = TestListItemBinding();
  }

...

  @override
  String item2LogicTag(TestListItemState t) {
    //todo set item unionKey
    throw Exception('None set itemKey');
    return t.hashCode.toString();
  }
}

list/view.dart

class TestListView extends GetView<TestListLogic> {
 const TestListView({BuildContext? context, Key? key})
     : super(
         context: context,
         key: key,
       );

 @override
 Widget build(BuildContext context) {
   return Obx(() => ListView.builder(
         itemCount: controller.itemList.length,
         itemBuilder: (context, index) => TestListItemView(
           context: context,
           tag: controller.getItemTagByIndex(index),
         ),
       ));
 }
}

list/xxx_item/binding.dart

class TestListItemBinding extends ListItemBindings<TestListItemState> {
  @override
  void itemDependencies(TestListItemState item, String tag) {
    //todo 懒加载的child logic
   Get.lazyPut<TestListItemLogic>(() =>TestListItemLogic(item), tag: tag);
  }
}

实际效果: demo演示,图片较大

Flutter- 在GetX仿fish-redux ListAdapter实现items懒加载 因为封装不是很好,需要注意的地方还很多,多了就容易犯错,还配套一个模板生成工具,减少负担. 从效果上来看,item为懒加载模式,用到才开始走生命周期,且当列表销毁,item依赖同样销毁

后记

本次只针对List的数据类型的实现,Map同理

这次的改造引入了更多的复杂性,当然也只是我们习惯性的将复杂业务拆分成多个独立组件引起的,当然有很多别的办法也能达到目的,还更简单.这个只是一种想法,抛砖引玉吧

招聘

惠群2022前端招新啦,前端组19年开始使用flutter,并一直坚持flutter打造公司通用技术栈的道路,经过几年的沉淀,我们在app端,桌面端,web端都有项目产出,并有着自己的规范及插件库,我们欢迎志同道合且喜欢flutter的同学加入.投递邮箱:wlm@wiqun.com

转载自:https://juejin.cn/post/7065118867424542750
评论
请登录