Flutter自定义下拉选择框dropDownMenu

news/2024/7/20 22:50:30 标签: flutter, ios, android

利用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.niftyadmin.cn/n/5251377.html

相关文章

wordpress小记

1.插件市场搜索redis&#xff0c;并按照 Redis Object cache插件 2.开启php的redis扩展 执行php -m|grep redis&#xff0c;没有显示就执行 yum -y install php-redis3.再次修改wp配置文件&#xff0c;增加redis的配置 define( WP_REDIS_HOST, 114.80.36.124 );define( WP_…

【图像分类】【深度学习】【Pytorch版本】 DenseNet模型算法详解

【图像分类】【深度学习】【Pytorch版本】 DenseNet模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】 DenseNet模型算法详解前言DenseNet讲解Dense Block(稠密块)Dense Layer(稠密层)Transition Layer 过渡层DenseNet模型结构 DenseNet Pytorch代码完整代码附加…

PostgreSQL pgvector:如何利用向量数据库提升搜索效率和精度

LLMs模型实战教程 文章来源&#xff1a;https://zhuanlan.zhihu.com/p/641516393 Kevin 一、介绍 随着基础模型的兴起&#xff0c;向量数据库的受欢迎程度也飙升。事实上&#xff0c;在大型语言模型环境中&#xff0c;向量数据库也很有用。 在机器学习领域&#xff0c;我们经…

实现简易的一对一用户聊天

服务端 package 一对一用户;import java.awt.BorderLayout; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector…

做数据分析为何要学统计学(6)——什么问题适合使用方差分析?

方差分析&#xff08;ANOVA&#xff0c;也称变异数分析&#xff09;是英国统计学家Fisher&#xff08;1890.2.17&#xff0d;1962.7.29&#xff09;提出的对两个或以上样本总体均值进行差异显著性检验的方法。 它的基本思想是将测量数据的总变异&#xff08;即总方差&#xff…

普冉(PUYA)单片机开发笔记(4): 配置通用定时器

概述 在前几篇开发笔记中&#xff0c;从 PY32F003 的一个厂家标准例程开始&#xff0c;实现了中断式无阻塞串口收发、对开发板 LED3 的亮/灭控制&#xff0c;时钟系统的初始化和时钟选择。在此基础上&#xff0c;今天做一下定时器的应用实验。事先考虑以下几个问题&#xff1a…

阿里云SLB的使用总结

一、什么是SLB 实现k8s的服务service的一种推荐方式&#xff0c;也是服务上云后&#xff0c;替代LVS的一个必选产品。 那么它有什么作用呢&#xff1f; 1、负载均衡&#xff0c;是它与生俱来的。可以配置多个服务器组&#xff1a;包括虚拟服务器组、默认服务器组、主备服务器…

在线网页生成工具GrapesJS

项目地址 https://github.com/GrapesJS/grapesjshttps://github.com/GrapesJS/grapesjs 项目简述 这是一个基于node.js的在线网页生成项目&#xff0c;对简化开发有很大的帮助。 主要使用的语言如下&#xff1a; 编辑页面如下&#xff1a; 使用也很简洁 具体可以看下项目。…