Flutter下拉菜单覆盖层插件dropdown_overlay的使用

发布于 1周前 作者 gougou168 来自 Flutter

Flutter下拉菜单覆盖层插件dropdown_overlay的使用

标题

Flutter下拉菜单覆盖层插件dropdown_overlay的使用

内容

  • 简介

    • dropdown_overlay 是一个提供简单方式创建自定义下拉菜单的Flutter包。
  • 功能

    • 只支持单选下拉菜单
    • 支持搜索和可选下拉菜单
    • 支持下拉菜单定制
      • 精确对齐下拉菜单与触发器通过DropdownMenuPosition
      • 使用SimpleDropdown.custom自定义下拉菜单
    • 控制下拉菜单程序化管理通过DropdownController
      • 由控制器管理选择项,共享状态跨组件/页面
    • 搜索下拉菜单
    • 同步/异步加载项目通过DropdownController
  • 开始使用

    import 'package:dropdown_overlay/dropdown_overlay.dart';
    
  • 示例代码

import 'package:flutter/material.dart';
import 'package:dropdown_overlay/dropdown_overlay.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Simple Dropdown Example'),
        ),
        body: const Center(
          child: DropdownExample(),
        ),
      ),
    );
  }
}

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

  [@override](/user/override)
  State<DropdownExample> createState() => _DropdownExampleState();
}

class _DropdownExampleState extends State<DropdownExample> {
  final _singleSelectionController = DropdownController<String>.single(
    items: List.generate(15, (index) => DropdownItem(value: "Single $index")),
  );
  final _multiSelectionController = DropdownController<String>.multi(
    items: List.generate(15, (index) => DropdownItem(value: "Multi $index")),
  );

  final TextEditingController _singleSearch = TextEditingController();
  final TextEditingController _multiSearch = TextEditingController();

  [@override](/user/override)
  void dispose() {
    _singleSelectionController.dispose();
    _multiSelectionController.dispose();
    _singleSearch.dispose();
    _multiSearch.dispose();

    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        children: [
          const SizedBox(height: 30),
          SimpleDropdown&lt;String&gt;.list(
            controller: _singleSelectionController,
            builder: (_) =&gt; Container(
              width: 150,
              decoration: BoxDecoration(
                border: Border.all(color: Colors.grey),
                borderRadius: BorderRadius.circular(4),
              ),
              child: const Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  Text("Single Select"),
                  SizedBox(width: 8),
                  Icon(Icons.arrow_drop_down),
                ],
              ),
            ),
            menuPosition: const DropdownMenuPosition(
              offset: Offset(0, 10),
            ),
            menuConstraints: const BoxConstraints(
              maxHeight: 150,
            ),
            itemBuilder: (_, item) =&gt; GestureDetector(
              onTap: () {
                _singleSelectionController.select(item.value, dismiss: false);
              },
              child: Card(
                color: item.selected ? Colors.green : Colors.grey,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(4),
                ),
                child: Padding(
                  padding: const EdgeInsets.all(8),
                  child: Text(item.value),
                ),
              ),
            ),
          ),
          const SizedBox(height: 50),
          SimpleDropdown&lt;String&gt;.list(
            controller: _multiSelectionController,
            builder: (_) =&gt; Container(
              width: 150,
              decoration: BoxDecoration(
                border: Border.all(color: Colors.grey),
                borderRadius: BorderRadius.circular(4),
              ),
              child: const Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  Text("Multi Select"),
                  SizedBox(width: 8),
                  Icon(Icons.arrow_drop_down),
                ],
              ),
            ),
            menuPosition: const DropdownMenuPosition(
              offset: Offset(0, 5),
            ),
            menuConstraints: const BoxConstraints(
              maxHeight: 150,
            ),
            itemBuilder: (_, item) =&gt; GestureDetector(
              onTap: () {
                _multiSelectionController.select(item.value, dismiss: false);
              },
              child: Card(
                color: item.selected ? Colors.green : Colors.grey,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(4),
                ),
                child: Padding(
                  padding: const EdgeInsets.all(8),
                  child: Text(item.value),
                ),
              ),
            ),
          ),
          const SizedBox(height: 50),
          const Divider(),
          const SizedBox(height: 50),
          SimpleDropdown&lt;String&gt;.list(
            controller: _singleSearchController,
            builder: (_) =&gt; SizedBox(
              width: 1150,
              child: TextField(
                controller: _singleSearch,
                decoration: const InputDecoration(
                  labelText: "Single Search",
                  border: OutlineInputBorder(),
                ),
                onChanged: (value) {
                  if (!_singleSearchController.isOpen) {
                    _singleSearchController.open();
                  }

                  if (value.isEmpty) {
                    _singleSearchController.restore();
                  } else {
                    _singleSearchController.search(
                      value,
                      matcher: (query, item) =&gt item.contains(query),
                    );
                  }
                },
              ),
            ),
            menuPosition: const DropdownMenuPosition(
              offset: Offset(0, 5),
            ),
            menuConstraints: const BoxConstraints(
              maxHeight: 150,
            ),
            itemBuilder: (_, item) =&gt; GestureDetector(
              onTap: () {
                _singleSearchController.select(item.value, dismiss: false);
              },
              child: Card(
                color: item.selected ? Colors.green : Colors.grey,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(4),
                ),
                child: Padding(
                  padding: const EdgeInsets.all(8),
                  child: Text(item.value),
                ),
              ),
            ),
          ),
          const SizedBox(height: 50),
          SimpleDropdown&lt;String&gt;.list(
            controller: _multiSearchController,
            builder: (_) =&gt; SizedBox(
              width: 150,
              child: TextField(
                controller: _multiSearch,
                decoration: const InputDecoration(
                  labelText: "Multi Search",
                  border: OutlineInputBorder(),
                ),
                onChanged: (value) {
                  if (!_multiSearchController.isOpen) {
                    _multiSearchController.open();
                  }
                  if (value.isEmpty) {
                    _multiSearchController.restore();
                  } else {
                    _multiSearchController.search(
                      value,
                      matcher: (query, item) =&gt item.contains(query),
                    );
                  }
                },
              ),
            ),
            menuPosition: const DropdownMenuPosition(
              targetAnchor: Alignment.topLeft,
              anchor: Alignment.bottomLeft,
              offset: Offset(0, -5),
            ),
            menuConstraints: const BoxConstraints(
              maxHeight: 1150,
            ),
            itemBuilder: (_, item) =&gt; GestureDetector(
              onTap: () {
                _multiSearchController.select(item.value, dismiss: false);
              },
              child: Card(
                color: item.selected ? Colors.green : Colors.grey,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(4),
                ),
                child: Padding(
                  padding: const EdgeInsets.all(8),
                  child: Text(item.value),
                ),
              ),
            ),
          ),
          const SizedBox(height: 50),
          TextButton(
            onPressed: () {
              _multiSearchController.load(
                () =&gt; List.generate(
                  5,
                  (index) =&gt DropdownItem(value: "Load More $index"),
                ),
              );
            },
            style: TextButton.styleFrom(
              backgroundColor: Colors.green,
              side: const BorderSide(color: Colors.grey),
            ),
            child: const Text("Load More"),
          ),
        ],
      ),
    );
  }
}

更多关于Flutter下拉菜单覆盖层插件dropdown_overlay的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter下拉菜单覆盖层插件dropdown_overlay的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用dropdown_overlay插件来实现下拉菜单覆盖层的示例代码。这个插件允许你自定义下拉菜单的样式和行为。首先,确保你已经在pubspec.yaml文件中添加了dropdown_overlay依赖:

dependencies:
  flutter:
    sdk: flutter
  dropdown_overlay: ^x.y.z  # 请替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,这里有一个完整的示例代码,展示了如何使用dropdown_overlay

import 'package:flutter/material.dart';
import 'package:dropdown_overlay/dropdown_overlay.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Dropdown Overlay Example'),
        ),
        body: Center(
          child: MyDropdownOverlay(),
        ),
      ),
    );
  }
}

class MyDropdownOverlay extends StatefulWidget {
  @override
  _MyDropdownOverlayState createState() => _MyDropdownOverlayState();
}

class _MyDropdownOverlayState extends State<MyDropdownOverlay> {
  final List<String> items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
  String? selectedItem;

  @override
  Widget build(BuildContext context) {
    return DropdownOverlay(
      // Customize the overlay configuration
      overlayConfiguration: OverlayConfiguration(
        alignment: Alignment.topLeft,
        margin: EdgeInsets.only(top: 20),
        padding: EdgeInsets.all(8),
        borderRadius: BorderRadius.circular(10),
        backgroundColor: Colors.white,
        elevation: 4,
        shadowColor: Colors.black.withOpacity(0.2),
        shadowBlurRadius: 10,
      ),
      child: DropdownButton<String>(
        value: selectedItem,
        hint: Text('Select an item'),
        icon: Icon(Icons.arrow_downward),
        iconSize: 24,
        elevation: 16,
        style: TextStyle(color: Colors.deepPurple),
        underline: Container(
          height: 2,
          color: Colors.deepPurpleAccent,
        ),
        onChanged: (String? newValue) {
          setState(() {
            selectedItem = newValue;
          });
        },
        items: items.map<DropdownMenuItem<String>>((String value) {
          return DropdownMenuItem<String>(
            value: value,
            child: Text(value),
          );
        }).toList(),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 引入依赖:首先确保dropdown_overlay依赖已经添加到项目中。
  2. 定义UI结构:使用DropdownOverlay包裹DropdownButton
  3. 配置覆盖层:通过overlayConfiguration属性自定义下拉菜单覆盖层的样式,比如对齐方式、边距、内边距、圆角、背景颜色、阴影等。
  4. 处理选项变化:在onChanged回调中更新选中的值。

这个示例展示了如何使用dropdown_overlay插件来自定义下拉菜单的样式,使其更符合你的应用需求。你可以根据实际需求进一步调整覆盖层的配置。

回到顶部