Flutter自定義下拉選擇框dropDownMenu

利用PopupMenuButtonPopupMenuItem寫了個(gè)下拉選擇框冷冗,之所以不采用系統(tǒng)的,是因?yàn)樽远x的更能適配項(xiàng)目需求惑艇,話不多說蒿辙,直接看效果

gif.gif
下面直接貼出代碼、代碼中注釋寫的都很清楚滨巴,使用起來應(yīng)該很方便思灌,如果有任何問題,歡迎下方留言...
import 'package:flutter/material.dart';

class DropMenuWidget extends StatefulWidget {
  final List<Map<String, dynamic>> data; //數(shù)據(jù)
  final Function(String value) selectCallBack; //選中之后回調(diào)函數(shù)
  final String? selectedValue; //默認(rèn)選中的值
  final Widget? leading; //前面的widget恭取,一般是title
  final Widget trailing; //尾部widget泰偿,一般是自定義圖片
  final Color? textColor;
  final Offset offset; //下拉框向下偏移量--手動(dòng)調(diào)整間距---防止下拉框遮蓋住顯示的widget
  final TextStyle normalTextStyle; //下拉框的文字樣式
  final TextStyle selectTextStyle; //下拉框選中的文字樣式
  final double maxHeight; //下拉框的最大高度
  final double maxWidth; //下拉框的最大寬度
  final Color? backGroundColor; //下拉框背景顏色
  final bool animation; //是否顯示動(dòng)畫---尾部圖片動(dòng)畫
  final int duration; //動(dòng)畫時(shí)長
  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();
      }
    });
  }

  //根據(jù)傳值處理顯示的文字
  _initLabel() {
    if (_currentValue.isNotEmpty) {
      _selectedLabel = widget.data
          .firstWhere((item) => item['value'] == _currentValue)['label'];
    } else if (widget.data.isNotEmpty) {
      // 沒值默認(rèn)取第一個(gè)
      _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是為了適配當(dāng)字符串長度超過指定寬度的時(shí)候,會(huì)讓字體自動(dòng)縮小
          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度對應(yīng)的弧度值
                      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('當(dāng)前選中:'),
                ),
                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', //默認(rèn)選中第三個(gè)
              ),
            )

如果喜歡蜈垮,希望給個(gè)star???? CSDN地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耗跛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子攒发,更是在濱河造成了極大的恐慌调塌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惠猿,死亡現(xiàn)場離奇詭異羔砾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門蜒茄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人餐屎,你說我怎么就攤上這事檀葛。” “怎么了腹缩?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵屿聋,是天一觀的道長。 經(jīng)常有香客問我藏鹊,道長润讥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任盘寡,我火速辦了婚禮楚殿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竿痰。我一直安慰自己脆粥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布影涉。 她就那樣靜靜地躺著变隔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蟹倾。 梳的紋絲不亂的頭發(fā)上匣缘,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音鲜棠,去河邊找鬼肌厨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛岔留,可吹牛的內(nèi)容都是我干的夏哭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼献联,長吁一口氣:“原來是場噩夢啊……” “哼竖配!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起里逆,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤进胯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后原押,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胁镐,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盯漂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颇玷。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖就缆,靈堂內(nèi)的尸體忽然破棺而出帖渠,到底是詐尸還是另有隱情,我是刑警寧澤竭宰,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布空郊,位于F島的核電站,受9級(jí)特大地震影響切揭,放射性物質(zhì)發(fā)生泄漏狞甚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一廓旬、第九天 我趴在偏房一處隱蔽的房頂上張望哼审。 院中可真熱鬧,春花似錦嗤谚、人聲如沸棺蛛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旁赊。三九已至,卻和暖如春椅野,著一層夾襖步出監(jiān)牢的瞬間终畅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工竟闪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留离福,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓炼蛤,卻偏偏與公主長得像妖爷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子理朋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容