Flutter 可以快速实现单项或者多项选择,你知道吗?

1,074 阅读4分钟

WeChat4df36c077689189202cc1a9d2fc236e0.png

[》跳过拾光记忆]

拾光记忆

1. Flutter 项目资产管理,看这一篇就够了!

简介: 针对 Flutter 项目资产管理的脚本服务。Fam 具有以下特点: 支持多种平台以及各平台无差异化、界面美观、功能齐全、快捷方便。
推荐: ⭐️⭐️⭐️⭐️⭐️

2. Flutter 手势在多指触摸时一些方法会多次触发

简介: 针对 Flutter 多手指检测以及手势触发其他手势也触发的问题。
推荐: ⭐️⭐️⭐️⭐️⭐️

3. Dart 的枚举类型的高阶用法

简介: 这是让开发者更深入的了解 Dart 的枚举以及相关使用和方法。
推荐: ⭐️⭐️⭐️

[返回拾光记忆《]

一、选择简述

在使用 Flutter 开发中,业务需求往往是多种多样的,比如:常见的一个需求就是进行单项或者多项选择的需求。然而,Flutter 并没有给我们提供便捷的实现方式或者组件。我们要自己在项目中去实现这个功能,肯定需要耗费很多时间以及精力。但是,好的消息是 Pub.dev 上的 idkit 开发包为开发者提供了这样功能的组件,该小部件是 IDKitChoice

二、IDKitChoice 的介绍

IDKitChoiceFlutter 业务开发中能便捷实现 单选多选 的快捷小部件。

  1. IDKitChoice 提供了四种初始化的方法,如下:
    1. const IDKitChoice.list(xx)
    2. const IDKitChoice.listSeparated(xx)
    3. const IDKitChoice.grid(xx)
    4. const IDKitChoice.warp(xx)
    
    可根据自己软件的视图设计选择对应的方法快捷实现选择。
  2. IDKitChoice 可以设置多选最大选择数量
    控制选择最大数量的属性是 int? maximumChoice
  3. IDKitChoice 可以设置选择后是否可以完全取消
    控制选择是否能完全取消的属性是 bool isCancelAll = true
  4. IDKitChoice 可以设置初始是否选择回调
    控制初始是否选择回调的属性是 bool isStartChoicedCall = false
  5. IDKitChoice 可以设置初始选择的对象
    控制初始选择对象的属性是 List<int>? chosenIndexs
  6. IDKitChoice 可以便捷的实现单选和多选的切换
    控制单选和多选的切换属性是 ChoiceType type = ChoiceType.single

三、IDKitChoice 的配置

IDKitChoiceidkit 包里面的小部件。idkit 依托于 pub.dev 进行管理。所以我们可以使用下面指令在项目的根目录执行来添加idkit包,指令如下: flutter pub add idkit。如下图所示:

添加 idkit 或者可以拷贝 pub.dev 上的 idkit 的使用版本,如下下图所示:

sd_add_idkit.png 将考本的文本添加到 Flutter 项目的 pubspec.yaml 文件的 dependencies 标签下,如上图所示,然后需要执行 flutter pub get 指令拉取 idkit 包。

四、IDKitChoice 的应用

1. 单选 -- 性别选择

数据源如下:

/// 性别
static List<Map<String, dynamic>> sexList = [
  {
    'label': '未知',
    'tag': 0,
  },
  {
    'label': '男',
    'tag': 1,
  },
  {
    'label': '女',
    'tag': 2,
  }
];

实例应用代码如下:

SizedBox(
   height: 40,
   child: IDKitChoice<Map<String, dynamic>>.warp(
     direction: Axis.vertical,
     isCancelAll: true,
     runSpacing: 10,
     sources: ChooseConfig.sexList,
     choiceCallMethod: (result, results) {
       print(result);
     },
     itemBuilder: (context, state, data) {
       return Row(
         children: [
           Icon(
             state
                 ? Icons.radio_button_checked
                 : Icons.radio_button_off,
             size: 20,
           ),
           5.hGap,
           Text(data['label'] as String)
         ],
       );
     },
   ).insetsSymmetric(vertical: 10),
 )

实现效果如下:

WeChat2b976e019492cb4477636fced4099761.png 从上图可以看出,我们可以完全自定义选中和未选中的视图样式。

2. 多选 -- 多标签选择

选择数据源:

/// 标签
static List<Map<String, dynamic>> markList = [
  {
    'label': '善良勤恳',
    'tag': 0,
  },
  {
    'label': '美丽身材好',
    'tag': 1,
  },
  {
    'label': '落落大方',
    'tag': 2,
  },
  {
    'label': '可爱动人',
    'tag': 3,
  },
  {
    'label': '妖娆妩媚迷人',
    'tag': 4,
  },
];

应用代码如下:

IDKitChoice.warp(
  spacing: 10,
  runSpacing: 10,
  sources: ChooseConfig.markList,
  type: ChoiceType.multiple,
  maximumChoice: 3,
  itemBuilder: (context, state, data) {
    return Container(
      decoration: BoxDecoration(
        color: state ? Colors.green : Colors.grey,
        borderRadius: BorderRadius.circular(6),
      ),
      child: Text(data['label'])
          .insetsSymmetric(horizontal: 5, vertical: 3),
    );
  },
),

效果显示如下:

WeChatc5dda5cc860b85d5dd278ee2295bfa73.png

3. 单选 -- 侧边选择列表

数据源如下:

/// 测列表切换
static List<Map<String, dynamic>> sildeList = [
  {
    'label': '北京市',
    'tag': 0,
  },
  {
    'label': '朝阳区',
    'tag': 1,
  },
  {
    'label': '西城区',
    'tag': 2,
  },
  {
    'label': '东城区',
    'tag': 3,
  },
  {
    'label': '通州区',
    'tag': 4,
  },
];

应用实例代码如下:

const Text('侧列表切换:').insetsOnly(top: 30, bottom: 10),
Container(
  decoration: BoxDecoration(
    border: Border.all(color: Colors.green),
    borderRadius: BorderRadius.circular(10),
  ),
  height: 300,
  child: Row(
    children: [
      SizedBox(
        width: 120,
        child: IDKitChoice.list(
          sources: ChooseConfig.sildeList,
          choiceCallMethod: (result, results) {
            silderStreamController.add(result['label']);
          },
          itemBuilder: (context, state, data) {
            return Container(
              color: state ? Colors.red : Colors.white,
              child: Text(data['label']),
            );
          },
        ),
      ),
      const VerticalDivider(
        color: Colors.grey,
      ),
      Expanded(
        child: StreamBuilder(
          initialData: '请选择区域',
          stream: silderStreamController.stream,
          builder: (context, snapshot) {
            return Center(child: Text(snapshot.data!));
          },
        ),
      ),
    ],
  ).insetsAll(10),
).insetsOnly(right: 10)

实现效果如下:

WeChat71b4c61cf3c2ffd4bcda0a50763350a7.png

4. 单选 -- TabBar的选择

选择的数据源:

/// Tabbar 的切换
static List<Map<String, dynamic>> tabbarList = [
  {
    'label': '早间新闻',
    'tag': 0,
  },
  {
    'label': '今日热榜',
    'tag': 1,
  },
  {
    'label': '视频号',
    'tag': 2,
  },
  {
    'label': '咨询',
    'tag': 3,
  },
  {
    'label': '抖音短视频',
    'tag': 4,
  },
];

用于实例的代码:

const Text('Tabbarde 的切换:').insetsOnly(top: 30, bottom: 10),
Container(
  decoration: BoxDecoration(
    border: Border.all(color: Colors.green),
    borderRadius: BorderRadius.circular(10),
  ),
  child: Column(
    children: [
      SizedBox(
        height: 44,
        child: IDKitChoice.list(
          scrollDirection: Axis.horizontal,
          sources: ChooseConfig.tabbarList,
          choiceCallMethod: (result, results) {
            silderStreamController.add(result['label']);
          },
          itemBuilder: (context, state, data) {
            return Center(
              child: Text(
                data['label'],
                style: TextStyle(
                  decorationColor:
                      state ? Colors.red : Colors.transparent,
                  decorationThickness: state ? 3 : 0,
                  decoration: TextDecoration.underline,
                  decorationStyle: TextDecorationStyle.solid,
                ),
              ).insetsSymmetric(horizontal: 10),
            );
          },
        ),
      ),
      SizedBox(
        height: 100,
        child: StreamBuilder(
          initialData: '请选择区域',
          stream: silderStreamController.stream,
          builder: (context, snapshot) {
            return Center(child: Text(snapshot.data!));
          },
        ),
      )
    ],
  ),
).insetsOnly(right: 10)

代码效果展示如下:

WeChatf6c7149a819ac10e017afd781c97fa99.png