行业知识
Flutter自定义下拉选择框dropDownMenu
2026-11-09 20:52:52
来源:互联网转载
利用PopupMenuButton和PopupMenuItem写了个下拉选择框,之所以不采用系统的,是因为自定义的更能适配项目需求,话不多说,直接看效果
下面直接贴出代码、代码中注释写的都很清楚,使用起来应该很方便,如果有任何问题,欢迎下方留言…
import 'package:flutter/material.dart';class DropMenuWidget extends StatefulWidget { final List 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 createState() => _DropMenuWidgetState();}class _DropMenuWidgetState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _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
上一篇:中转服务器怎么免流?
下一篇:MyBatis多表查询和注解开发