Flutter自定义下拉选择框dropDownMenu

news2024/11/17 9:50:27

利用PopupMenuButtonPopupMenuItem写了个下拉选择框,之所以不采用系统的,是因为自定义的更能适配项目需求,话不多说,直接看效果

请添加图片描述

下面直接贴出代码、代码中注释写的都很清楚,使用起来应该很方便,如果有任何问题,欢迎下方留言…
import 'package:flutter/material.dart';

class DropMenuWidget extends StatefulWidget {
  final List<Map<String, dynamic>> data; //数据
  final Function(String value) selectCallBack; //选中之后回调函数
  final String? selectedValue; //默认选中的值
  final Widget? leading; //前面的widget,一般是title
  final Widget trailing; //尾部widget,一般是自定义图片
  final Color? textColor;
  final Offset offset; //下拉框向下偏移量--手动调整间距---防止下拉框遮盖住显示的widget
  final TextStyle normalTextStyle; //下拉框的文字样式
  final TextStyle selectTextStyle; //下拉框选中的文字样式
  final double maxHeight; //下拉框的最大高度
  final double maxWidth; //下拉框的最大宽度
  final Color? backGroundColor; //下拉框背景颜色
  final bool animation; //是否显示动画---尾部图片动画
  final int duration; //动画时长
  const DropMenuWidget({
    super.key,
    this.leading,
    required this.data,
    required this.selectCallBack,
    this.selectedValue,
    this.trailing = const Icon(Icons.arrow_drop_down),
    this.textColor = Colors.white,
    this.offset = const Offset(0, 30),
    this.normalTextStyle = const TextStyle(
      color: Colors.white,
      fontSize: 12.0,
    ),
    this.selectTextStyle = const TextStyle(
      color: Colors.red,
      fontSize: 12.0,
    ),
    this.maxHeight = 200.0,
    this.maxWidth = 200.0,
    this.backGroundColor = const Color.fromRGBO(28, 34, 47, 1),
    this.animation = true,
    this.duration = 200,
  });

  @override
  State<DropMenuWidget> createState() => _DropMenuWidgetState();
}

class _DropMenuWidgetState extends State<DropMenuWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _animation;
  String _selectedLabel = '';
  String _currentValue = '';
  // 是否展开下拉按钮
  bool _isExpand = false;

  @override
  void initState() {
    super.initState();
    _currentValue = widget.selectedValue ?? '';
    if (widget.animation) {
      _animationController = AnimationController(
        vsync: this,
        duration: Duration(milliseconds: widget.duration),
      );
      _animation = Tween(begin: 0.0, end: 0.5).animate(
        CurvedAnimation(
          parent: _animationController,
          curve: Curves.easeInOut,
        ),
      );
    }
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  _toggleExpand() {
    setState(() {
      if (_isExpand) {
        _animationController.forward();
      } else {
        _animationController.reverse();
      }
    });
  }

  //根据传值处理显示的文字
  _initLabel() {
    if (_currentValue.isNotEmpty) {
      _selectedLabel = widget.data
          .firstWhere((item) => item['value'] == _currentValue)['label'];
    } else if (widget.data.isNotEmpty) {
      // 没值默认取第一个
      _selectedLabel = widget.data[0]['label'];
      _currentValue = widget.data[0]['value'];
    }
  }

  @override
  Widget build(BuildContext context) {
    _initLabel();
    return PopupMenuButton(
      constraints: BoxConstraints(
        maxHeight: widget.maxHeight,
        maxWidth: widget.maxWidth,
      ),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(4.0),
      ),
      offset: widget.offset,
      color: widget.backGroundColor,
      onOpened: () {
        if (widget.animation) {
          setState(() {
            _isExpand = true;
            _toggleExpand();
          });
        }
      },
      onCanceled: () {
        if (widget.animation) {
          setState(() {
            _isExpand = false;
            _toggleExpand();
          });
        }
      },
      child: Container(
        alignment: Alignment.centerLeft,
        height: 40,
        child: FittedBox(
          //使用FittedBox是为了适配当字符串长度超过指定宽度的时候,会让字体自动缩小
          child: Row(
            children: [
              if (widget.leading != null) widget.leading!,
              Text(
                _selectedLabel,
                style: TextStyle(
                  color: widget.textColor,
                  fontSize: 14.0,
                ),
              ),
              if (widget.animation)
                AnimatedBuilder(
                  animation: _animation,
                  builder: (context, child) {
                    return Transform.rotate(
                      angle: _animation.value * 2.0 * 3.14, // 180度对应的弧度值
                      child: widget.trailing,
                    );
                  },
                ),
              if (!widget.animation) widget.trailing,
            ],
          ),
        ),
      ),
      itemBuilder: (context) {
        return widget.data.map((e) {
          return PopupMenuItem(
            child: Text(
              e['label'],
              style: e['value'] == _currentValue
                  ? widget.selectTextStyle
                  : widget.normalTextStyle,
            ),
            onTap: () {
              setState(() {
                _currentValue = e['value'];
                widget.selectCallBack(e['value']);
              });
            },
          );
        }).toList();
      },
    );
  }
}


使用
Container(
              color: Colors.grey,
              width: 130,
              alignment: Alignment.centerLeft,
              child: DropMenuWidget(
                leading: const Padding(
                  padding: EdgeInsets.only(right: 10),
                  child: Text('当前选中:'),
                ),
                data: const [
                  {'label': '华为', 'value': '1'},
                  {'label': '小米', 'value': '2'},
                  {'label': 'Apple', 'value': '3'},
                  {'label': '乔布斯', 'value': '4'},
                  {'label': '啦啦啦啦啦', 'value': '5'},
                  {'label': '呵呵', 'value': '7'},
                  {'label': '乐呵乐呵', 'value': '7'},
                ],
                selectCallBack: (value) {
                  print('选中的value是:$value');
                },
                offset: const Offset(0, 40),
                selectedValue: '3', //默认选中第三个
              ),
            )

简书地址如果喜欢,希望给个star😄😄

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

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

相关文章

【完整项目】双模式答题卡识别软件中YOLO模式的训练部分详解,包括训练填涂区域和手写准考证号,手把手详细教学,可延申拓展训练其他图像数据

目录 前言1. 数据准备2. 数据标注3. 先跑起来Windows下用本地的CPU或GPU训练本地Windows系统连接服务器训练前言 前文:【完整项目】基于Python+Tkinter+OpenCV+Yolo+手写OCR的双模式答题卡识别软件的设计与实现 如果你需要训练自己的答题卡模型,那么请先看上面的文章链接。…

uniapp自定义的日历(纯手写)

效果图&#xff1a; html&#xff1a; <!-- 年月 --><view class"box"><view class"box_time"><view class"time"><image click"lefts" :src"url/uploads/20231206/9d1fb520b12383960dca3c214d84fa0…

uniapp图片预览

用的是Uview组件库里面的 直接在页面写上&#xff1a; <u-album singleSize"100" :urls"[https://lxt.jingyi.icu/item.img]"></u-album> 这图片路径是我自己的 你们可以按照组件库里面的方法去实现

掌握JavaScript继承的精髓:原型继承、构造函数继承以及组合继承的实现技巧

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;JavaScript篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript-Javascript如何实现继承&#xff1f; 目录 一、是什么 二、实现方式 …

智能优化算法应用:基于蜉蝣算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜉蝣算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜉蝣算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜉蝣算法4.实验参数设定5.算法结果6.参考文献7.MA…

侮辱性涨薪!业绩得了S,调薪涨了450

信安这个行业3年前各大媒体&#xff0c;信安自己人都觉得自己在个朝阳行业&#xff0c;红利在咋弄不得再吃5年。 现在拉个干网络安全的再去问问&#xff0c;看看谁不是去年年终奖砍了一半、或者根本就没了&#xff0c;再或者每天岌岌可危生怕去领大礼包。 原本10月份的激励性…

python变量的命名和使用

变量名只能包含字母、数字和下划线 变量名只能包含字母、数字和下划线。变量名可以字母或下划线打头&#xff0c;但不能以数字打头。例如&#xff0c;可将变量命名为message_1&#xff0c;但不能将其命名为1_message。 Python 语言中&#xff0c;以下划线开头的标识符有特殊含…

普冉(PUYA)单片机开发笔记(5): 配置定时器PWM输出

概述 定时器的输出通道作为 PWM 驱动是 MCU 的常用功能。 PY32F003 有一个高级定时器 TIM1 和一个通用定时器 TIM3&#xff0c;这两个定时器都可以驱动4个输出通道。现在我们就利用 TIM1 的某一个通道实现可控占空比的 PWM 输出。 原理简介 看数据手册&#xff0c;简单摘录…

【文件上传系列】No.2 秒传(原生前端 + Node 后端)

上一篇文章 【文件上传系列】No.1 大文件分片、进度图展示&#xff08;原生前端 Node 后端 & Koa&#xff09; 秒传效果展示 秒传思路 整理的思路是&#xff1a;根据文件的二进制内容生成 Hash 值&#xff0c;然后去服务器里找&#xff0c;如果找到了&#xff0c;说明已经…

AI模型平台Hugging Face存在API令牌漏洞;大型语言模型与任务模型

&#x1f989; AI新闻 &#x1f680; AI模型平台Hugging Face存在API令牌漏洞&#xff0c;黑客可窃取、修改模型 摘要&#xff1a;安全公司Lasso Security发现AI模型平台Hugging Face上存在API令牌漏洞&#xff0c;黑客可获取微软、谷歌等公司的令牌&#xff0c;并能够访问模…

若依框架启动过程中遇到的控制台使用npm i下载相关依赖报错的问题以及前端启动遇到的问题

目录 报错截图问题解决其他问题 npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次。问题解决更改环境变量新建系统变量 其他问题 错误解决Error: error:0…

外贸建站要国外服务器吗?海外服务器推荐?

外贸建站如何选国外服务器&#xff1f;海洋建站用什么服务器好&#xff1f; 外贸建站已经成为企业拓展国际市场的一项重要举措。然而&#xff0c;一个关键问题摆在许多企业面前&#xff1a;外贸建站是否需要选择国外服务器呢&#xff1f;这个问题涉及到多方面的考虑因素&#…

idea开发环境配置

idea重新安装后&#xff0c;配置的东西还挺多的&#xff0c;这里简单记录一下。 1、基础配置 1.1、主题、背景、主题字体大小 1.2、默认字体设置 控制台默认编码设置&#xff1a; 全局文件默认编码设置&#xff1a; 2、构建、编译、部署配置 说明&#xff1a;本地装了JD…

10 大 Mac 数据恢复软件深度评测

对于任何依赖计算机获取重要文件&#xff08;无论是个人照片还是重要商业文档&#xff09;的人来说&#xff0c;数据丢失可能是一场噩梦。值得庆幸的是&#xff0c;有多种专门为 Mac 用户提供的数据恢复工具&#xff0c;可以帮助检索丢失或意外删除的文件。在本文中&#xff0c…

文心一言API(高级版)使用

文心一言API高级版使用 一、百度文心一言API(高级版)二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、百度文心一言API(高级版) 基于百度文心一言语言大模型的智能文本对话AI机器…

冒泡排序和直接选择排序(C/C++实现)

文章目录 冒泡排序(交换排序&#xff09;基本思想特性总结代码实现 直接选择排序基本思想特性总结代码实现&#xff08;优化&#xff0c;每次循环同时选择最小和最大的数&#xff09; 冒泡排序(交换排序&#xff09; 基本思想 基本思想&#xff1a;所谓交换&#xff0c;就是根…

以pycharm为例,生成Python项目所需要的依赖库/包文档:requirements.txt

平时我们在编写或者使用别人的Python项目时&#xff0c;往往会看到一个文档requirements.txt&#xff0c;该文档是描述一个Python项目中的第三方库的名称以及版本。本文介绍导出python当前项目依赖包requirements.txt的操作步骤。 方法一&#xff1a;如果每个项目有对应的虚拟…

飞天使-linux操作的一些技巧与知识点3

http工作原理 http1.0 协议 使用的是短连接&#xff0c;建立一次tcp连接&#xff0c;发起一次http的请求&#xff0c;结束&#xff0c;tcp断开 http1.1 协议使用的是长连接&#xff0c;建立一次tcp的连接&#xff0c;发起多次http的请求&#xff0c;结束&#xff0c;tcp断开ngi…

阿里云(云服务器)上搭建项目部署环境

目录 安装docker docker安装MySQL5.7.37 安装MySQL 方式一&#xff1a;docker中MySQL时区调整 方式二&#xff1a;docker中MySQL时区调整 docker安装MySQL8.0.27 docker安装redis5.0.14 云服务器上安装jdk1.8 安装docker 1、先卸载docker&#xff0c;因为有一些服务器…

docker学习(七、搭建mysql8.2主从)

一、主库搭建 1.构建主库镜像 # 运行mysql镜像&#xff0c;配置端口3307为主库 docker run -p 3307:3306 --name mysql-master --privilegedtrue -v /mydata/mysql-master/log:/var/log/mysql -v /mydata/mysql-master/data:/var/lib/mysql -v /mydata/mysql-master/conf:/etc…