小工具知识网

当前位置: 首页 > 行业知识 > Flutter自定义下拉选择框dropDownMenu

行业知识

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多表查询和注解开发