Flutter下拉菜单插件flex_dropdown的使用

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

Flutter下拉菜单插件flex_dropdown的使用

Flex DropDown | Custom DropDown

pub package

通过Flex DropDown插件,你可以轻松创建优雅且可定制的下拉菜单。这个Flutter插件允许你方便地实现带有自定义样式、动画和数据源的下拉菜单,以满足你的应用需求。

开始使用

如果你对这个包的开发背景故事感兴趣,可以阅读我们在Medium上的文章:Creating Custom Dropdowns with OverlayPortal in Flutter

要开始使用Flex DropDown插件,请确保你已经安装了Flutter并且对Flutter插件的基本操作有所了解。你可以按照以下步骤将插件集成到你的项目中:

  • pubspec.yaml文件中添加如下依赖:
    dependencies:
      flex_dropdown: ^0.1.0  
    
  • 运行以下命令获取插件:
    flutter pub get
    
  • 在Dart代码中导入插件:
    import 'package:flex_dropdown/flex_dropdown.dart';
    

在线演示

你可以通过以下链接查看Flex DropDown的交互式在线示例:Flex DropDown Online Example

使用方法

下面是一个简单的例子,展示了如何使用Flex DropDown插件:

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final OverlayPortalController _controller = OverlayPortalController();
  MenuPosition position = MenuPosition.bottomStart;
  bool dismissOnTapOutside = true;
  bool useButtonSize = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).colorScheme.onInverseSurface,
      appBar: AppBar(
        title: const Text('Flex Drop Down'),
      ),
      body: Column(
        children: [
          const SizedBox(height: 150),
          Align(
            alignment: Alignment.topCenter,
            child: Padding(
              padding: const EdgeInsets.only(top: 64.0),
              child: RawFlexDropDown(
                controller: _controller,
                menuPosition: position,
                dismissOnTapOutside: dismissOnTapOutside,
                buttonBuilder: (context, onTap) {
                  return ButtonWidget(
                    width: 400,
                    onTap: onTap,
                  );
                },
                menuBuilder: (context, width) {
                  return Padding(
                    padding: const EdgeInsets.only(top: 4),
                    child: MenuWidget(
                      width: useButtonSize ? width : 300,
                      onItemTap: () {
                        _controller.hide();
                      },
                    ),
                  );
                },
              ),
            ),
          ),
          const SizedBox(height: 250),
          Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text('Dismiss on tap outside'),
              const SizedBox(width: 8),
              Switch(
                value: dismissOnTapOutside,
                onChanged: (value) {
                  setState(() {
                    dismissOnTapOutside = value;
                  });
                },
              ),
              const SizedBox(width: 32),
              const Text('Use button size'),
              const SizedBox(width: 8),
              Switch(
                value: useButtonSize,
                onChanged: (value) {
                  setState(() {
                    useButtonSize = value;
                  });
                },
              ),
            ],
          ),
          const SizedBox(height: 32),
          Wrap(
            children: [
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    position = MenuPosition.bottomStart;
                  });
                },
                child: const Text("Bottom Left"),
              ),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    position = MenuPosition.bottomEnd;
                  });
                },
                child: const Text("Bottom Right"),
              ),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    position = MenuPosition.bottomCenter;
                  });
                },
                child: const Text("Bottom Center"),
              ),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    position = MenuPosition.topStart;
                  });
                },
                child: const Text("Top Left"),
              ),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    position = MenuPosition.topEnd;
                  });
                },
                child: const Text("Top Right"),
              ),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    position = MenuPosition.topCenter;
                  });
                },
                child: const Text("Top Center"),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class ButtonWidget extends StatelessWidget {
  const ButtonWidget({
    super.key,
    this.height = 48,
    this.width,
    this.onTap,
    this.child,
  });

  final double? height;
  final double? width;

  final VoidCallback? onTap;

  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: height,
      width: width,
      child: Material(
        color: Colors.white,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10),
          side: const BorderSide(color: Colors.black12),
        ),
        child: InkWell(
          onTap: onTap,
          borderRadius: BorderRadius.circular(10),
          child: const Center(child: Text('Button')),
        ),
      ),
    );
  }
}

class MenuWidget extends StatelessWidget {
  const MenuWidget({
    super.key,
    this.width,
    required this.onItemTap,
  });

  final double? width;
  final VoidCallback onItemTap;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: width,
      padding: const EdgeInsets.all(8),
      decoration: ShapeDecoration(
        color: Theme.of(context).colorScheme.primaryContainer,
        shape: RoundedRectangleBorder(
          side: const BorderSide(
            width: 1.5,
            color: Colors.black26,
          ),
          borderRadius: BorderRadius.circular(12),
        ),
        shadows: const [
          BoxShadow(
            color: Color(0x11000000),
            blurRadius: 32,
            offset: Offset(0, 20),
            spreadRadius: -8,
          ),
        ],
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Expanded(
                child: _FakeItemHolder(
                  text: 'Item 1',
                  onTap: onItemTap,
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: _FakeItemHolder(
                  text: 'Item 2',
                  onTap: onItemTap,
                ),
              ),
            ],
          ),
          const SizedBox(height: 8),
          Row(
            children: [
              Expanded(
                child: _FakeItemHolder(
                  text: 'Item 3',
                  onTap: onItemTap,
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: _FakeItemHolder(
                  text: 'Item 4',
                  onTap: onItemTap,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class _FakeItemHolder extends StatelessWidget {
  const _FakeItemHolder({
    required this.text,
    required this.onTap,
  });

  final String text;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return SizedBox(
      height: 80,
      child: Material(
        color: theme.colorScheme.onPrimary,
        shape: RoundedRectangleBorder(
          side: BorderSide(
            width: 1,
            color: theme.colorScheme.onSurfaceVariant,
          ),
          borderRadius: BorderRadius.circular(12),
        ),
        child: InkWell(
          borderRadius: BorderRadius.circular(12),
          onTap: onTap,
          child: Center(child: Text(text)),
        ),
      ),
    );
  }
}

额外信息

如果你遇到任何问题、有建议或想要为这个包做贡献,欢迎在GitHub仓库中提交问题或Pull Request。

我们重视社区反馈,并致力于及时回应您可能有的任何疑问或担忧。


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

1 回复

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


当然,以下是如何在Flutter中使用flex_dropdown插件来创建下拉菜单的示例代码。flex_dropdown是一个增强版的下拉菜单插件,它提供了更多的自定义选项和更好的用户体验。

首先,确保你的pubspec.yaml文件中已经添加了flex_dropdown依赖:

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

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

接下来是一个使用flex_dropdown的完整示例代码:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flex Dropdown Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String? selectedValue;

  final List<String> dropdownValues = ['Option 1', 'Option 2', 'Option 3'];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flex Dropdown Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            FlexDropdown(
              containerDecoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                border: Border.all(color: Colors.grey.shade300),
              ),
              dropdownDecoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                color: Colors.white,
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey.withOpacity(0.2),
                    spreadRadius: 5,
                    blurRadius: 7,
                    offset: Offset(0, 3), // changes position of shadow
                  ),
                ],
              ),
              buttonDecoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                color: Colors.blue.shade300,
              ),
              buttonTextStyle: TextStyle(color: Colors.white),
              dropdownTextStyle: TextStyle(color: Colors.black),
              value: selectedValue,
              hint: Text('Select an option'),
              onChanged: (value) {
                setState(() {
                  selectedValue = value;
                });
              },
              items: dropdownValues.map<DropdownMenuItem<String>>((String value) {
                return DropdownMenuItem<String>(
                  value: value,
                  child: Text(value),
                );
              }).toList(),
            ),
          ],
        ),
      ),
    );
  }
}

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

  1. 导入flex_dropdown包。
  2. 创建一个包含下拉菜单的主页面。
  3. 使用FlexDropdown小部件来创建下拉菜单。
  4. 设置下拉菜单的容器装饰、下拉菜单项的装饰、按钮装饰、按钮文本样式和下拉菜单项的文本样式。
  5. 定义下拉菜单的值和提示文本。
  6. 实现onChanged回调函数,当用户选择一个选项时更新selectedValue状态。

这个示例展示了flex_dropdown的基本用法,你可以根据需要进一步自定义和扩展它。

回到顶部