點燈坊

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

DropdownBottomSheet 顯示下方有捲軸選單

Sam Xiao's Avatar 2024-10-29

Flutter 並沒有提供 DropdownBottomSheet 顯示下方有捲軸選單,必須自行使用 ModalBottomSheetListViewScrollbar 實現。

Version

Flutter 3.24

Flutter

scrollable01

  • Android 與 iOS 都成功使用 DropdownBottomSheet 顯示下方有捲軸選單
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';
  var options = [
    'Option 1',
    'Option 2',
    'Option 3',
    'Option 4',
    'Option 5',
    'Option 6',
  ];

  List<Widget> _buildListTiles() {
    return options.map(
      (option) {
        return ListTile(
          title: Text(option),
          onTap: () {
            setState(() => _selectedText = option);
            Navigator.pop(context);
          },
        );
      },
    ).toList();
  }

  void _showModalBottomSheet(BuildContext context) {
    var deviceHeight = MediaQuery.of(context).size.height;

    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(
          top: Radius.circular(16),
        ),
      ),
      builder: (BuildContext context) {
        return SizedBox(
          height: deviceHeight * 0.25,
          child: Scrollbar(
            thumbVisibility: true,
            child: ListView(
              shrinkWrap: true,
              children: _buildListTiles(),
            ),
          ),
        );
      },
    );
  }

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

    var dropdownButton = 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: dropdownButton,
    );
  }
}

Line 12

var options = [
  'Option 1',
  'Option 2',
  'Option 3',
  'Option 4',
  'Option 5',
  'Option 6',
];
  • options:定義下拉選單所顯示的資料

Line 36

builder: (BuildContext context) {
  return SizedBox(
    height: deviceHeight * 0.25,
    child: Scrollbar(
      thumbVisibility: true,
      child: ListView(
        shrinkWrap: true,
        children: _buildListTiles(),
      ),
    ),
  );
}
  • SizedBoxListView 本身並沒有 height 設定高度,需在外層使用 SizeBox 定義 height
    • height:可使用 MediaQuery 取得裝置高度動態決定 height 不寫死
  • Scrollbar:使 ListView 有捲軸
    • thumbVisibility:是否永遠顯示捲軸

Line 21

List<Widget> _buildListTiles() {
  return options.map(
    (option) {
      return ListTile(
        title: Text(option),
        onTap: () {
          setState(() => _selectedText = option);
          Navigator.pop(context);
        },
      );
    },
  ).toList();
}
  • options 產生 ListTile

Conclusion

  • ListView 本身無法設定高度,必須由外層的 SizedBox 設定
  • ListView 本身無法產生捲軸,必須由外層的 Scrollbar 產生