點燈坊

失くすものさえない今が強くなるチャンスよ

DropdownBottomSheet 顯示下方彈出選單

Sam Xiao's Avatar 2024-10-29

Flutter 並沒有提供 DropdownBottomSheet 顯示下方彈出選單,必須自行使用 ModalBottomSheetListView 實現。

Version

Flutter 3.24

Flutter

overview01

  • Android 與 iOS 都成功使用 DropdownBottomSheet 顯示由下方彈出的選單

DropBottomSheet

import 'package:flutter/material.dart';

class Home extends StatefulWidget {
  const Home({super.key});

  
  State<Home> createState() => _Home();
}

class _Home extends State<Home> {
  var _selectedText = 'Select Option';

  ListTile buildListTile(
    BuildContext context,
    String text,
  ) {
    return ListTile(
      title: Text(text),
      onTap: () {
        setState(() => _selectedText = text);
        Navigator.pop(context);
      },
    );
  }

  void _showModalBottomSheet(BuildContext context) {
    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(
          top: Radius.circular(16),
        ),
      ),
      builder: (BuildContext context) => ListView(
        shrinkWrap: true,
        children: [
          buildListTile(context, 'Option 1'),
          buildListTile(context, 'Option 2'),
          buildListTile(context, 'Option 3'),
        ],
      ),
    );
  }

  
  Widget build(BuildContext context) {
    var appBar = AppBar(title: const Text('DropdownBottomSheet'));

    var dropdownBottomSheet = GestureDetector(
      onTap: () => _showModalBottomSheet(context),
      child: Container(
        margin: const EdgeInsets.all(16.0),
        padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
        decoration: BoxDecoration(
          border: Border.all(color: Colors.grey),
          borderRadius: BorderRadius.circular(8.0),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(_selectedText),
            const Icon(Icons.arrow_drop_down),
          ],
        ),
      ),
    );

    return Scaffold(
      appBar: appBar,
      body: dropdownBottomSheet,
    );
  }
}

Line 11

var _selectedText = 'Select Option';
  • _selectedText:儲存用戶所選擇的項目

Line 49

var dropdownBottomSheet = GestureDetector(
  onTap: () => _showModalBottomSheet(context),
  child: Container(
    margin: const EdgeInsets.all(16.0),
    padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
    decoration: BoxDecoration(
      border: Border.all(color: Colors.grey),
      borderRadius: BorderRadius.circular(8.0),
    ),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Text(_selectedText),
        const Icon(Icons.arrow_drop_down),
      ],
    ),
  ),
);
  • GestureDetector:包裹整個 Container,當 Container 被點擊時會觸發 onTap()
    • onTap():馬上呼叫 _showModalBottomSheet()

Line 58

child: Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Text(_selectedText),
    const Icon(Icons.arrow_drop_down),
  ],
),
  • 上方的下拉選單,其實是用 TextIcon 組合出來的

Line 23

void _showModalBottomSheet(BuildContext context) {
  showModalBottomSheet(
    context: context,
    shape: const RoundedRectangleBorder(
      borderRadius: BorderRadius.vertical(
        top: Radius.circular(16),
      ),
    ),
    builder: (BuildContext context) => ListView(
      shrinkWrap: true,
      children: [
        buildListTile(context, 'Option 1'),
        buildListTile(context, 'Option 2'),
        buildListTile(context, 'Option 3'),
      ],
    ),
  );
}
  • 呼叫 showModalBottomSheet() 顯示 ModalBottomSheet

Line 34

builder: (BuildContext context) => ListView(
  shrinkWrap: true,
  children: [
    buildListTile(context, 'Option 1'),
    buildListTile(context, 'Option 2'),
    buildListTile(context, 'Option 3'),
  ],
),
  • builder()ModelBottomSheet 實際 UI 之處,可在這裡刻任何自己的 UI
  • ListView:建立選單
    • shrinkWarpListView 會根據內容縮小,而不會佔據整個可用空間
    • buildListTile():建立 ListTile

Line 13

ListTile buildListTile(
  BuildContext context,
  String text,
) {
  return ListTile(
    title: Text(text),
    onTap: () {
      setState(() => _selectedText = text);
      Navigator.pop(context);
    },
  );
}
  • ListTileListView 的每個選項
    • title:設定顯示文字
    • onTap():當點擊時觸發,以 Navigator.pop(context) 關閉 ModalBottomSheet

Conclusion

  • 上方的下拉選單並不是 DropdownButton,而是用 TextIcon 模擬出來而已
  • 下方則藉由 ModalBottomSheetListView 搭配模擬出選單