Flutter - 底部多选弹框组件

news2024/11/25 20:32:10

demo 地址: https://github.com/iotjin/jh_flutter_demo
代码不定时更新,请前往github查看最新代码

有时需要弹框选择多个数据,因此写了个底部多选弹框组件
支持搜索,设置默认选中数据,暗黑模式适配

效果图

在这里插入图片描述

使用方法


final multiDictArr = [
  {'label': '类型一', 'value': '1'},
  {'label': '类型二', 'value': '2'},
  {'label': '类型三', 'value': '3'},
  {'label': '类型四', 'value': '4'},
  {'label': '类型五', 'value': '5'},
  {'label': '类型六', 'value': '6'},
  {'label': '类型七类型七类型七类型七', 'value': '7'},
  {'label': '类型八类型八类型八类型八类型八类型八类型八类型八类型八类型八', 'value': '8'},
  {'label': '类型九', 'value': '9'},
];

 JhMultiPicker.show(context, data: multiDictArr, values: ['3', '5'], title: '选择类型',
     clickCallBack: (selectValues, selectItemArr) {
   print('selectValues==  $selectValues');
   print('selectItemArr==  $selectItemArr');
   showText(selectValues);
 });

jh_multi_picker代码

///  jh_multi_picker.dart
///
///  Created by iotjin on 2023/08/28.
///  description: 底部多选弹框

// ignore_for_file: library_private_types_in_public_api

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '/jh_common/jh_form/jh_searchbar.dart';
import '/jh_common/utils/jh_common_utils.dart';
import '/project/configs/colors.dart';
import '/project/provider/theme_provider.dart';
import '/project/routes/jh_nav_utils.dart';

const String _labelKey = 'label';
const String _valueKey = 'value';
const String _titleText = '请选择';
const String _cancelText = '取消';
const String _confirmText = '确定';
const String _searchHintText = '搜索';

const double _headerHeight = 50.0;
const double _headerRadius = 10.0;
const double _headerLineHeight = 0.5;
const double _titleFontSize = 18.0;
const double _btnFontSize = 17.0;
const double _textFontSize = 16.0;

/// 选择回调,返回所有选中的values数组和所有item数组
typedef _ClickCallBack = void Function(dynamic selectValues, dynamic selectItemArr);

class JhMultiPicker {
  static bool _isShowPicker = false;

  static void show(
    BuildContext context, {
    required List data, // 数据源数组
    String labelKey = _labelKey, // 数据源数组的文字字段
    String valueKey = _valueKey, // 数据源数组的数值字段
    String title = _titleText,
    List values = const [], // 默认选中的数组(一维数组),通过valuesKey确定是根据value还是label进行比较,最好使用唯一值作为元素
    String valuesKey = _valueKey, // 选中数组内元素使用的字段,对应valueKey或者labelKey
    bool isShowSearch = true,
    String searchHintText = _searchHintText,
    bool isShowRadius = true,
    _ClickCallBack? clickCallBack,
  }) {
    if (_isShowPicker || data.isEmpty) {
      return;
    }

    var radius = isShowRadius ? _headerRadius : 0.0;

    showModalBottomSheet<void>(
      context: context,
      // 使用true则高度不受16分之9的最高限制
      isScrollControlled: false,
      // 设置圆角
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(radius),
          topRight: Radius.circular(radius),
        ),
      ),
      // 抗锯齿
      clipBehavior: Clip.antiAlias,
      builder: (BuildContext context) {
        return SafeArea(
          child: JhMultiPickerView(
            data: data,
            labelKey: labelKey,
            valueKey: valueKey,
            title: title,
            values: values,
            valuesKey: valuesKey,
            isShowSearch: isShowSearch,
            searchHintText: searchHintText,
            clickCallBack: clickCallBack,
          ),
        );
      },
    ).then((value) => _isShowPicker = false);
  }
}

class JhMultiPickerView extends StatefulWidget {
  const JhMultiPickerView({
    Key? key,
    required this.data,
    this.labelKey = _labelKey,
    this.valueKey = _valueKey,
    this.title = _titleText,
    this.values = const [],
    this.valuesKey = _valueKey,
    this.isShowSearch = true,
    this.searchHintText = _searchHintText,
    this.clickCallBack,
  }) : super(key: key);

  final List? data; // 数据源数组
  final String labelKey; // 数据源数组的文字字段
  final String valueKey; // 数据源数组的数值字段
  final String title;
  final List values; // 默认选中的数组(一维数组),通过valuesKey确定是根据value还是label进行比较,最好使用唯一值作为元素
  final String valuesKey; // 选中数组内元素使用的字段,对应valueKey或者labelKey
  final bool isShowSearch;
  final String searchHintText;
  final _ClickCallBack? clickCallBack;

  
  State<JhMultiPickerView> createState() => _JhMultiPickerViewState();
}

class _JhMultiPickerViewState extends State<JhMultiPickerView> {
  late List<dynamic> _selectedValues = [];

  // 搜索数据
  List _searchData = [];
  bool _isShowSearchResult = false;
  String _searchKeyword = '';

  
  void initState() {
    super.initState();
    _selectedValues = List.from(widget.values);
  }

  
  Widget build(BuildContext context) {
    return _body();
  }

  _body() {
    // 默认颜色
    var bgColor = KColors.dynamicColor(context, KColors.kPickerBgColor, KColors.kPickerBgDarkColor);
    var lineColor = KColors.dynamicColor(context, KColors.kLineColor, KColors.kLineDarkColor);

    return Container(
      color: bgColor,
      child: Column(
        children: <Widget>[
          _header(),
          SizedBox(height: _headerLineHeight, child: Container(color: lineColor)),
          _searchBar(),
          _mainWidget(),
        ],
      ),
    );
  }

  _header() {
    // 默认颜色
    var headerColor = KColors.dynamicColor(context, KColors.kPickerHeaderColor, KColors.kPickerHeaderDarkColor);
    var titleColor = KColors.dynamicColor(context, KColors.kPickerTitleColor, KColors.kPickerTitleDarkColor);
    var btnColor = KColors.dynamicColor(context, KColors.kPickerBtnColor, KColors.kPickerBtnDarkColor);

    return Container(
      height: _headerHeight,
      color: headerColor,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          GestureDetector(
            child: Container(
              padding: const EdgeInsets.only(left: 15),
              child: Text(_cancelText, style: TextStyle(fontSize: _btnFontSize, color: btnColor)),
            ),
            onTap: () {
              widget.clickCallBack?.call([], []);
              JhNavUtils.goBack(context);
            },
          ),
          Text(widget.title, style: TextStyle(fontSize: _titleFontSize, color: titleColor)),
          GestureDetector(
              child: Container(
                padding: const EdgeInsets.only(right: 15),
                child: Text(_confirmText, style: TextStyle(fontSize: _btnFontSize, color: btnColor)),
              ),
              onTap: () {
                widget.clickCallBack?.call(_selectedValues, _getSelectItemList());
                JhNavUtils.goBack(context);
              }),
        ],
      ),
    );
  }

  Widget _mainWidget() {
    List dataArr = _isShowSearchResult ? _searchData : (widget.data ?? []);
    return Expanded(
      child: ListView.builder(
        shrinkWrap: true,
        itemCount: dataArr.length,
        itemBuilder: (BuildContext context, int index) {
          return _buildItem(dataArr[index], index);
        },
      ),
    );
  }

  _buildItem(item, index) {
    // TODO: 通过ThemeProvider进行主题管理
    final provider = Provider.of<ThemeProvider>(context);
    var themeColor = KColors.dynamicColor(context, provider.getThemeColor(), KColors.kThemeColor);

    var selectValue = widget.valuesKey == widget.valueKey ? item[widget.valueKey] : item[widget.labelKey];
    return CheckboxListTile(
      title: Text(item[widget.labelKey].toString(), style: const TextStyle(fontSize: _textFontSize)),
      value: _selectedValues.contains(selectValue),
      activeColor: themeColor,
      onChanged: (bool? checked) {
        setState(() {
          if (checked ?? false) {
            _selectedValues.add(selectValue);
          } else {
            _selectedValues.remove(selectValue);
          }
          // widget.clickCallBack?.call(_selectedValues, _getSelectItemList());
        });
      },
    );
  }

  _getSelectItemList() {
    var newList = (widget.data ?? []).where((item) => _selectedValues.contains(item[widget.valueKey])).toList();
    return newList;
  }

  Widget _searchBar() {
    Widget searchbar = JhSearchBar(
      hintText: widget.searchHintText,
      text: _searchKeyword,
      inputCallBack: (value) {
        JhCommonUtils.debounce(() {
          setState(() {
            _searchKeyword = value;
            if (value.isNotEmpty) {
              _searchData = _getSearchData(value);
              _isShowSearchResult = _searchData.isNotEmpty;
            } else {
              _isShowSearchResult = false;
            }
          });
        }, 500);
      },
    );
    return !widget.isShowSearch ? Container() : searchbar;
  }

  /// 根据搜索文字过滤数据
  _getSearchData(keyword) {
    var newList = (widget.data ?? [])
        .where((item) => item[widget.labelKey].toLowerCase().contains(keyword.toLowerCase()))
        .toList();
    return newList;
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1075699.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于FPGA的图像缩小算法实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将FPGA的处理结果导出到matlab中显示图像效果&#xff1a; 2.算法运行软件版本 vivado2019.2 matlab2022a 3.部分核心程序 timescale 1ns / 1p…

mysql8压缩包安装

MySQL 8.0 版压缩包安装教程_mysql 压缩包 8.0安装-CSDN博客 1、mysql压缩包 2、参考链接一步一步操作即可。 3、安装&#xff0c;破解navicat. 4、无法连接&#xff0c;参考该链接修改&#xff1a; Mysql 解决1251- Client does not support authentication protocol reques…

易云维®IBMS系统实现了医院楼宇建筑内各专业子系统间的相互操作、快速响应、与联动控制

易云维医院楼宇智能化管理系统&#xff08;IBMS系统&#xff09;可以通过调研医院项目现场情况&#xff0c;了解用户的实际需求&#xff0c;为用户提供合理投资、高效、舒适、方便的环境空间&#xff1b;对医院建筑多个弱电子系统进行集中监控&#xff0c;确保各个弱电子系统安…

【C进阶】字符串函数

C语言中对字符和字符串的处理很频繁&#xff0c;但是C语言本身是没有字符串类型的&#xff0c;字符串通常放在常量字符串中或者字符数组中 字符串常量适用于那些对它不做修改的字符串函数 本章重点介绍处理字符串函数的库函数的使用和注意事项 一、字符串函数 这些函数都要引…

C语言之动态内存管理_柔性数组篇(2)

目录 柔性数组的特点 柔性数组的使用 动态内存函数增容柔性数组模拟实现 柔性数组的优势 今天接着来讲解一下柔性数组知识。 柔性数组的特点 C99中&#xff0c;结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做【柔性数组】成员。 结构体中最后一个成员未…

PyCharm搭建Scrapy环境

Scrapy入门 1、Scrapy概述2、PyCharm搭建Scrapy环境3、Scrapy使用四部曲4、Scrapy入门案例4.1、明确目标4.2、制作爬虫4.3、存储数据4.4、运行爬虫 1、Scrapy概述 Scrapy是一个由Python语言开发的适用爬取网站数据、提取结构性数据的Web应用程序框架。主要用于数据挖掘、信息处…

k8spod就绪检查失败

pod 一直未就绪 kube-system metrics-server-7764f6c67c-2kts9 0/1 Running 0 10m kubect describe 查看 就绪探针未通过 Normal Started 3m19s kubelet Started container metrics-server Warning Unhealthy 5s (x20 over 2m55s) kubelet Readiness probe failed: HTTP probe…

Springboot整合Hutool自定义注解实现数据脱敏

一、前言 我们在项目中会处理敏感数据&#xff08;如手机号、身份证号、姓名、地址等&#xff09;时&#xff0c;通常需要对这些数据进行脱敏&#xff0c;以确保数据隐私和安全。 我们本次使用 Hutool 库来轻松实现数据脱敏&#xff0c;如果项目中不让使用&#xff0c;可以自…

各类高危漏洞介绍及验证方式教程(二)

本期整理的漏洞验证教程约包含50多类漏洞&#xff0c;分多个章节编写&#xff0c;可从以下链接获取全文&#xff1a; 各类高危漏洞验证方式.docx (访问密码: 1455) 搭建dvwa测试环境基础教程.docx(访问密码: 1455) web逻辑漏洞挖掘快速入门基础教程.docx (访问密码: 1455) 06 I…

工作杂记-YUV的dump和read

工作小记-YUV的dump和read 工作杂记-YUV的dump和read利用dump生成图片 yuv2imgyuv2img代码 工作杂记-YUV的dump和read 工作中涉及到模型验证相关的工作&#xff0c;这里是三个模型的共同作用&#xff0c;在感知模型读取图片的时候&#xff0c;把输入替换成自己给定的输入&…

Python中如何快速解析JSON对象数组

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 由于浏览器可以迅速地解析JSON对象&#xff0c;它们有助于在客户端和服务器之间传输数据。 本文将描述如何使用Python的JSON模块来传输和接收JSON数据。 JavaSc…

优思学院|揭秘六西格玛:七大迷思你不可不知!

六西格玛的核心理念起源于1970年在摩托罗拉公司诞生。其基本精神一直是持续改进和提升品质&#xff0c;随后在各国呈爆炸性的发展。自2000年开始引进中国后&#xff0c;已经过了约16年的应用。但以2017年的角度回顾中国整体六西格玛的应用广度及熟悉度&#xff0c;发现六西格玛…

【ftp篇】 vsftp(ftp) 每天生成一个动态密码

这里写目录标题 前言为什么需要动态每日生成一个密码&#xff1f;编写脚本定时任务java对应的代码 前言 社长最近接到一个需求&#xff0c;需要ftp每天动态生成一个密码 为什么需要动态每日生成一个密码&#xff1f; 在软硬件通讯过程中&#xff0c;就以共享单车为例&#xff0…

Java解析E文件工具类

import lombok.extern.slf4j.Slf4j;import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List;/*** Description E文件工具类*/ Slf4j public class EFileUtils {/*** 读字符串* param text …

智慧公厕的原理与优势,了解一种更智能的卫生设施

智慧公厕是一种基于现代科技的智能化卫生设施&#xff0c;它的出现给人们的生活带来了巨大的改变和便利。本文将详细介绍智慧公厕的原理和优势&#xff0c;让我们一起了解一种更智能的卫生设施。 智慧公厕的原理主要基于物联网技术。通过将公厕内部各种设备和设施连接到互联网…

Java使用Hutool工具包将汉字转换成汉语拼音

主题&#xff1a;使用Java将汉字转换成拼音 介绍 在Java开发中&#xff0c;有时候我们需要将汉字转换成拼音&#xff0c;以方便进行数据处理、搜索和排序等操作。本文将介绍如何使用Hutool和Pinyin4j这两个Java库来实现汉字转拼音的功能。 依赖库介绍 在开始之前&#xff0c;…

无人直播矩阵系统源码开发------

全自动无人直播系统是一款让商家和企业实现无人直播的系统软件&#xff0c;让商家在门店直播卖货&#xff0c;实现解放双手&#xff0c;无需过多的人工干预。为了满足不同用户的需求&#xff0c;我们推出了OEM功能&#xff0c;让用户可以轻松地将该系统集成到自己的应用程序中。…

软考高项-第九章:项目范围管理

重要知识点&#xff1a; 以上总结&#xff0c;仅供参考。

视频通话中的Camera操作

视频通话也有打开本地摄像头预览的场景&#xff0c;但打开本地Camera预览逻辑&#xff0c;并非在Dailer APP中实现&#xff0c;具体流程图如下。 Dialer app中只调用 1、setCamera用于打开摄像头 相关动作在Ims apk中实现&#xff0c;open函数最后调用了VTSource.java中的doOp…

Python+Pickle/Parquet/HDF5...不同文件格式存储模式下的量化因子计算性能对比

在量化交易中&#xff0c;基于金融市场 L1/L2 报价和交易高频数据进行高频因子计算是一项常见的投研需求。随着金融市场数据量的不断增加&#xff0c;传统的关系数据库已经难以满足大规模数据的存储和查询需求。为了应对这一挑战&#xff0c;一部分用户选择了分布式文件系统&am…