點燈坊

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

DropdownBottomSheet 顯示兩層選單

Sam Xiao's Avatar 2024-10-29

Flutter 並沒有提供 DropdownBottomSheet 顯示兩層選單,必須自行使用 ModalBottomSheetListViewPadding 實現。

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> {
  String _selectedText = 'Select Option';

  void _showModalBottomSheet(BuildContext context) {
    showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      builder: (BuildContext context) {
        var deviceHeight = MediaQuery.of(context).size.height;

        return SizedBox(
          height: deviceHeight * 0.4,
          child: ListView(
            shrinkWrap: true,
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ListTile(
                    title: const Text(
                      'Option 1',
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                    onTap: () {
                      setState(() => _selectedText = 'Option 1');
                      Navigator.pop(context);
                    },
                  ),
                  Padding(
                    padding: const EdgeInsets.only(left: 32.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        ListTile(
                          title: const Text('Option 1.1'),
                          onTap: () {
                            setState(() => _selectedText = 'Option 1.1');
                            Navigator.pop(context);
                          },
                        ),
                        ListTile(
                          title: const Text('Option 1.2'),
                          onTap: () {
                            setState(() => _selectedText = 'Option 1.2');
                            Navigator.pop(context);
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ListTile(
                    title: const Text(
                      'Option 2',
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                    onTap: () {
                      setState(() => _selectedText = 'Option 2');
                      Navigator.pop(context);
                    },
                  ),
                  Padding(
                    padding: const EdgeInsets.only(left: 32.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        ListTile(
                          title: const Text('Option 2.1'),
                          onTap: () {
                            setState(() => _selectedText = 'Option 2.1');
                            Navigator.pop(context);
                          },
                        ),
                        ListTile(
                          title: const Text('Option 2.2'),
                          onTap: () {
                            setState(() => _selectedText = 'Option 2.2');
                            Navigator.pop(context);
                          },
                        ),
                        ListTile(
                          title: const Text('Option 2.3'),
                          onTap: () {
                            setState(() => _selectedText = 'Option 2.3');
                            Navigator.pop(context);
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ListTile(
                    title: const Text(
                      'Option 3',
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                    onTap: () {
                      setState(() => _selectedText = 'Option 3');
                      Navigator.pop(context);
                    },
                  ),
                  Padding(
                    padding: const EdgeInsets.only(left: 32.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        ListTile(
                          title: const Text('Option 3.1'),
                          onTap: () {
                            setState(() => _selectedText = 'Option 3.1');
                            Navigator.pop(context);
                          },
                        ),
                        ListTile(
                          title: const Text('Option 3.2'),
                          onTap: () {
                            setState(() => _selectedText = 'Option 3.2');
                            Navigator.pop(context);
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ],
          ),
        );
      },
    );
  }

  
  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 27

ListTile(
  title: const Text(
    'Option 1',
     style: TextStyle(fontWeight: FontWeight.bold),
  ),
  onTap: () {
    setState(() => _selectedText = 'Option 1');
    Navigator.pop(context);
  },
),
  • ListTile:第一層選單,使用 ListTile 即可,特別加上 粗體 識別

Line 35

Padding(
  padding: const EdgeInsets.only(left: 32.0),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      ListTile(
        title: const Text('Option 1.1'),
        onTap: () {
          setState(() => _selectedText = 'Option 1.1');
          Navigator.pop(context);
        },
      ),
    ],
  ),
)
  • Padding:第二層選單只要加上 Padding 即可

Conclusion

  • 由於 ModalBottomSheetbuilder() 非常靈活,可以自行以 Padding 實現兩層選單