热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Flutter专题】123图解简易GroupList二级分组列表

【Flutter专题】123图解简易GroupList二级分组列表-    小菜在实践过程中,想实现一个可选的二级分组列表,pub.dev插件库中已经有很多类似功能的插件,小菜还是

    小菜在实践过程中,想实现一个可选的二级分组列表,pub.dev 插件库中已经有很多类似功能的插件,小菜还是准备从自己角度尝试实现一个简单的二级分组列表;

  • 列表分为两级,默认均折叠
  • 一级列表和二级列表均可选中和取消
  • 二级列表数据可以主动添加

    分组列表的实现有很多方式,小菜准备用最基本的两个 ListView 嵌套的思路来进行展示,默认是展示第一级列表信息,在点击展开操作时,展示对应的二级列表;

GroupList

1. Bean 数据结构

    小菜先准备好数据实体类,一级列表 CategoryBean 中包含对应的二级列表 SubCategoryBean,其中两个实体中均包含一个 isChecked 字段用于保存当前 item 是否选中状态;

class CategoryBean {
  String name;
  String url;
  bool _isChecked = false;
  List itemList;

  bool get isChecked => _isChecked ?? false;

  set isChecked(bool value) => _isChecked = value;

  CategoryBean({this.name, this.url, this.itemList});
}

class SubCategoryBean {
  String name;
  String url;
  bool _isChecked = false;

  SubCategoryBean({this.name, this.url});

  bool get isChecked => _isChecked ?? false;

  set isChecked(bool value) => _isChecked = value;
}

2. 一级列表

    小菜先展示一级列表,可根据使用场景确认是否使用 SliverListView,小菜测试过程中仅采用基本的 ListView;其中单项选择框,小菜采用了之前自定义的 ACECheckbox;其中注意,在 ACECheckbox 点击回调时应注意更改一级实体 BeanisChecked 状态;

return Scaffold(
    appBar: AppBar(title: Text('分组列表')),
    body: ListView.builder(
        itemCount: widget.listData.length,
        itemBuilder: (context, index) {
          return GroupItemWidget(widget.listData[index]);
        }));

class _GroupItemWidgetState extends State {
  bool _isExpand = false;

  @override
  Widget build(BuildContext context) {
    return InkWell(
        child: Column(children: [
          Divider(height: 0.5, color: Colors.blue),
          Padding(
              padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
              child: Row(children: [
                Icon(_isExpand ? Icons.arrow_drop_down : Icons.arrow_right, color: Colors.blue),
                _userIcon(false),
                SizedBox(width: 5.0),
                Expanded(child: Text('${widget.bean.name}', style: TextStyle(fontSize: 16.0))),
                _rightCheckBox(widget.bean, 0)
              ])),
          _subCategoryList(widget.bean)
        ]),
        onTap: () {
          _isExpand = !_isExpand;
          setState(() {});
        });
  }

  _userIcon(isCircle) {
    double size = isCircle ? 40.0 : 45.0;
    return PhysicalModel(
        color: Colors.transparent,
        shape: isCircle ? BoxShape.circle : BoxShape.rectangle,
        clipBehavior: Clip.antiAlias,
        elevation: 2.0,
        borderRadius: BorderRadius.all(Radius.circular(20.0)),
        child: Container(width: size, height: size, child: Image.asset(isCircle ? 'images/icon_qq.png' : 'images/icon_hzw01.jpg')));
  }

  _rightCheckBox(bean, type, {subIndex}) {
    bool _isChecked = type == 0 ? bean.isChecked : bean.itemList[subIndex].isChecked;
    return ACECheckbox(
        value: _isChecked,
        type: ACECheckBoxType.circle,
        unCheckColor: Colors.blue,
        onChanged: (value) {
          setState(() => _isChecked = value);
          if (type == 0) {
            bean.isChecked = _isChecked;
            List.generate(bean.itemList.length, (index) => bean.itemList[index].isChecked = _isChecked);
          }
        });
  }
}

3. 二级列表

    当点击一级列表 item 时,展现二级列表;而实际上只是在一级 item 中添加一个新的列表数据,仅视觉效果上是展开二级列表;当再次点击一级列表 item 时把新加的二级列表替换为空的 Container 布局即可;

_subCategoryList(CategoryBean bean) {
  Widget _widget;
  if (!_isExpand ||
      bean == null ||
      bean.itemList == null ||
      bean.itemList.length == 0) {
    _widget = Container();
  } else {
    _widget = ListView.builder(
        itemCount: bean.itemList.length,
        itemBuilder: (context, index) => Row(children: [ Flexible(child: _subCategoryItem(bean, index)) ]));
  }
  return _widget;
}

_subCategoryItem(CategoryBean bean, index) {
  return Column(children: [
    Divider(height: 0.5, color: Colors.deepOrange),
    Padding(
        padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
        child: Row(children: [
          SizedBox(width: 40.0),
          _userIcon(true),
          SizedBox(width: 5.0),
          Expanded(child: Text(bean.itemList[index].name ?? 'SubName')),
          _rightCheckBox(bean, 1, subIndex: index)
        ]))
  ]);
}

4. ACECheckbox 选中 & 取消

    小菜最想处理的是列表 item 的选中和取消状态;小菜首先在实体 Bean 中添加一个 isChecked 状态用于记录当前选中状态;

    当一级列表选中时,无论展开或折叠,二级列表中各元素也全部选中,小菜通过 List.generate 遍历二级列表更改 isChecked 状态;

    当二级列表 item 部分选中时,对应的一级列表取消选中状态;同时当把二级列表中所有 items 均选中时,对应一级列表也要选中;小菜通过遍历判断二级列表中选中数量来调整一级 item 对应的 isChecked 状态;

_rightCheckBox(bean, type, {subIndex}) {
  bool _isChecked = type == 0 ? bean.isChecked : bean.itemList[subIndex].isChecked;
  return ACECheckbox(
      value: _isChecked,
      type: ACECheckBoxType.circle,
      unCheckColor: Colors.blue,
      onChanged: (value) {
        setState(() => _isChecked = value);
        if (type == 0) {
          bean.isChecked = _isChecked;
          List.generate(bean.itemList.length, (index) => bean.itemList[index].isChecked = _isChecked);
        } else {
          bean.itemList[subIndex].isChecked = _isChecked;
          int checkedSize = 0;
          List.generate(bean.itemList.length, (index) {
            if (bean.itemList[index].isChecked == false) {
              bean.isChecked = false;
            } else {
              checkedSize += 1;
            }
            if (checkedSize == bean.itemList.length) {
              bean.isChecked = true;
            }
          });
        }
      });
}

5. 数据动态添加

    小菜预期的目标是,首次数据只展示一级列表数据,再点击一级列表 item 时才会请求二级列表数据,并动态添加到数据列表中;这样的优势便是减少数据请求,简化数据格式;小菜默认在 分组五 中不设置二级列表数据,在点击时动态添加;

return InkWell(
    child: Column(children: [
      Divider(height: 0.5, color: Colors.blue),
      Padding(
          padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
          child: Row(children: [
            Icon(_isExpand ? Icons.arrow_drop_down : Icons.arrow_right, color: Colors.blue),
            _userIcon(false), SizedBox(width: 5.0),
            Expanded(child: Text('${widget.bean.name}', style: TextStyle(fontSize: 16.0))),
            _rightCheckBox(widget.bean, 0)
          ])),
      _subCategoryList(widget.bean)
    ]),
    onTap: () {
      _isExpand = !_isExpand;
      setState(() {});
      if (widget.bean.name == '分组五' &&
          (widget.bean.itemList == null || widget.bean.itemList.length == 0)) {
        widget.bean.itemList = [
          SubCategoryBean(name: 'O'),
          SubCategoryBean(name: 'P'),
          SubCategoryBean(name: 'Q')
        ];
      }
    });

6. 滑动冲突

    小菜通过两个 ListView 来实现二级分组列表,涉及到手势冲突,在二级列表展开时,手势只能在一级列表处触发,二级列表不会整体滑动且上下有主题色水波纹;小菜之前也曾处理过,只需要在 ListView 中设置 primary: false & shrinkWrap: true 等即可;

_widget = ListView.builder(
    primary: false,
    shrinkWrap: true,
    itemCount: bean.itemList.length,
    itemBuilder: (context, index) => Row(children: [ Flexible(child: _subCategoryItem(bean, index)) ]));


    GroupListPage 案例源码


    小菜对 GroupList 二级分组列表的练习暂时告一段落,但真正应用到实际中还应配合具体的数据请求以及页面操作进行调整,以上仅为一个简单的测试 Demo;如有错误,请多多指导!

来源: 阿策小和尚


推荐阅读
  • 解决发布版APK构建失败的问题 ... [详细]
  • 本文探讨了基于点集估算图像区域的Alpha形状算法在Python中的应用。通过改进传统的Delaunay三角剖分方法,该算法能够生成更加灵活和精确的形状轮廓,避免了单纯使用Delaunay三角剖分时可能出现的过大三角形问题。这种“模糊Delaunay三角剖分”技术不仅提高了形状的准确性,还增强了对复杂图像区域的适应能力。 ... [详细]
  • 深入解析Gradle中的Project核心组件
    在Gradle构建系统中,`Project` 是一个核心组件,扮演着至关重要的角色。通过使用 `./gradlew projects` 命令,可以清晰地列出当前项目结构中包含的所有子项目,这有助于开发者更好地理解和管理复杂的多模块项目。此外,`Project` 对象还提供了丰富的配置选项和生命周期管理功能,使得构建过程更加灵活高效。 ... [详细]
  • 本文深入解析了如何通过自定义 ViewGroup 实现类似支付宝风格的酷炫雷达脉冲动画效果。文章详细介绍了自定义 ViewGroup 的原理和实现步骤,并结合实际案例展示了如何在 Android UI 设计中应用这一技术,为开发者提供了宝贵的参考和实践指导。 ... [详细]
  • Android开发常见问题汇总(含Gradle解决方案)第二篇
    本文继续深入探讨Android开发中常见的问题及其解决方案,特别聚焦于Gradle相关的挑战。通过详细分析和实例演示,帮助开发者高效解决构建过程中的各种难题,提升开发效率和项目稳定性。 ... [详细]
  • Flutter基础Widgets之AppBar详解
    概述AppBarMaterial风格应用栏,有工具栏和其他的Widget构成应用栏通常用于Scaffold.appBar属性,该属性将应用栏放置在屏幕顶部的固定高度小部件中。对于可 ... [详细]
  • com.sun.javadoc.PackageDoc.exceptions()方法的使用及代码示例 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • Flutter中计算文本尺寸的方法
    在Flutter开发中,有时需要计算文本的宽度和高度。本文介绍了一种利用TextPainter类实现这一功能的方法。 ... [详细]
  • 在机器学习领域,深入探讨了概率论与数理统计的基础知识,特别是这些理论在数据挖掘中的应用。文章重点分析了偏差(Bias)与方差(Variance)之间的平衡问题,强调了方差反映了不同训练模型之间的差异,例如在K折交叉验证中,不同模型之间的性能差异显著。此外,还讨论了如何通过优化模型选择和参数调整来有效控制这一平衡,以提高模型的泛化能力。 ... [详细]
  • 在探讨如何在Android的TextView中实现多彩文字与多样化字体效果时,本文提供了一种不依赖HTML技术的解决方案。通过使用SpannableString和相关的Span类,开发者可以轻松地为文本添加丰富的样式和颜色,从而提升用户体验。文章详细介绍了实现过程中的关键步骤和技术细节,帮助开发者快速掌握这一技巧。 ... [详细]
  • 经过半年的精心整理,我们汇总了当前市场上最全面的Android面试题解析,为移动开发人员的晋升和加薪提供了宝贵的参考资料。本书详细涵盖了从基础到高级的各类面试题,帮助读者全面提升技术实力和面试表现。章节目录包括:- 第一章:Android基础面试题- 第二章:... ... [详细]
  • Android目录遍历工具 | AppCrawler自动化测试进阶(第二部分):个性化配置详解
    终于迎来了“足不出户也能为社会贡献力量”的时刻,但有追求的测试工程师绝不会让自己的生活变得乏味。与其在家消磨时光,不如利用这段时间深入研究和提升自己的技术能力,特别是对AppCrawler自动化测试工具的个性化配置进行详细探索。这不仅能够提高测试效率,还能为项目带来更多的价值。 ... [详细]
  • 效果预览1基本使用代码voidmain(){启动根目录runApp(MaterialApp(home:TestTipsPage(),));}classTestTipsPageext ... [详细]
  • 来源|http:sudasuta.comfree-material-design-resources.html如果你正在寻找高品质,免费的设计资源,那么 ... [详细]
author-avatar
宗小胖
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有