Flutter选择字段插件select_field的使用

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

Flutter选择字段插件select_field的使用

select_field 是一个简单、易用且高度可定制的输入字段插件,用于创建可选择选项的下拉列表。本文将详细介绍 select_field 插件的关键特性、限制以及如何使用它。

关键特性

使用 SelectField()MultiSelectField(),你可以:

  • 完全自定义输入字段和下拉菜单以满足你的需求。
  • 在下拉菜单中实现自定义小部件。
  • 完全控制下拉菜单的行为。
  • 轻松搜索可用选项以加快选择速度。

限制

  • 不支持下拉菜单的自定义动画。
  • 如果你提供自己的菜单控制器或文本控制器,则需要自己管理它们的行为。
  • 提供搜索选项时,每个菜单选项的高度是固定的。

示例

单选选择字段

以下是一个简单的单选选择字段示例:

const fruitOptions = <String>[
  'Apple',
  'Banana',
  'Strawberry',
  'Cherry',
  'Orange',
  'Raspberry',
];

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

  @override
  Widget build(BuildContext context) {
    final options = fruitOptions
        .map((fruit) => Option(label: fruit, value: fruit))
        .toList();

    return SelectField<String>(
      options: options,
      initialOption: Option<String>(
        label: fruitOptions[0],
        value: fruitOptions[0],
      ),
      menuPosition: MenuPosition.below,
      onTextChanged: (value) => debugPrint(value),
      onOptionSelected: (option) => debugPrint(option.toString()),
      inputStyle: const TextStyle(),
      menuDecoration: MenuDecoration(
        margin: const EdgeInsets.only(top: 8),
        height: 365,
        alignment: MenuAlignment.center,
        backgroundDecoration: BoxDecoration(
          color: Colors.green[100],
          borderRadius: BorderRadius.circular(12),
          boxShadow: [
            BoxShadow(
              offset: const Offset(1, 1),
              color: Colors.brown[300]!,
              blurRadius: 3,
            ),
          ],
        ),
        animationDuration: const Duration(milliseconds: 400),
        buttonStyle: TextButton.styleFrom(
          fixedSize: const Size(double.infinity, 60),
          backgroundColor: Colors.green[100],
          alignment: Alignment.centerLeft,
          padding: const EdgeInsets.all(16),
          shape: const RoundedRectangleBorder(),
          textStyle: const TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w600,
            color: Colors.green,
          ),
        ),
        separatorBuilder: (context, index) => Container(
          height: 1,
          width: double.infinity,
          color: Colors.green,
        ),
      ),
    );
  }
}

自定义下拉菜单项

你还可以通过 optionBuilder 函数在下拉菜单中添加自定义小部件:

SelectField<String>(
  options: options,
  optionBuilder: (context, option, isSelected, onOptionSelected) {
    return GestureDetector(
      // 如果需要菜单的默认行为(展开或折叠),则必须调用此函数
      onTap: () => onOptionSelected(option),
      child: isSelected
          ? Container(
              height: 60,
              color: Colors.deepOrange.withOpacity(0.2),
              child: Center(
                child: Text(
                  option.label,
                  style: const TextStyle(
                    color: Colors.deepOrange,
                  ),
                ),
              ),
            )
          : SizedBox(
              height: 60,
              child: Center(
                child: Text(
                  option.label,
                  style: TextStyle(
                    color: Colors.orange[700],
                  ),
                ),
              ),
            ),
    );
  },
),

多选选择字段

以下是一个多选选择字段的示例,包含自定义菜单控制:

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

class MultiSelectOptionsControl<String> extends StatefulWidget {
  final List<Option<String>> options;

  const MultiSelectOptionsControl({
    super.key,
    required this.options,
  });

  @override
  State<MultiSelectOptionsControl<String>> createState() =>
      _MultiSelectOptionsControlState<String>();
}

class _MultiSelectOptionsControlState<String>
    extends State<MultiSelectOptionsControl<String>> {
  late final List<Option<String>> initalOptions;
  late final MultiSelectFieldMenuController<String> menuController;

  void onOptionSelected(List<Option<String>> options) {
    setState(() {
      menuController.selectedOptions = options;
    });
  }

  void onOptionRemoved(Option<String> option) {
    final options = menuController.selectedOptions;
    options.remove(option);
    setState(() {
      menuController.selectedOptions = options;
    });
  }

  void onTapOutside() {
    menuController.isExpanded = false;
  }

  void onTap() {
    menuController.isExpanded = !menuController.isExpanded;
  }

  @override
  void initState() {
    super.initState();
    initalOptions = widget.options.sublist(1, 3);
    menuController = MultiSelectFieldMenuController(
      isExpanded: true,
      initalOptions: initalOptions,
    );
  }

  @override
  void dispose() {
    menuController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        MultiSelectField<String>(
          options: widget.options,
          fieldText: 'Select fruit',
          onTap: onTap,
          onTapOutside: onTapOutside,
          onOptionsSelected: onOptionSelected,
          menuController: menuController,
          menuDecoration: MenuDecoration(
            childBuilder: (context, option, isSelected) {
              return Row(
                children: [
                  Expanded(
                    child: Text(
                      option.label,
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.w600,
                        color: Theme.of(context)
                            .textButtonTheme
                            .style
                            ?.foregroundColor
                            ?.resolve({}),
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                  Icon(
                    isSelected
                        ? Icons.check_box_outlined
                        : Icons.check_box_outline_blank_outlined,
                    color: Theme.of(context)
                        .textButtonTheme
                        .style
                        ?.foregroundColor
                        ?.resolve({}),
                  ),
                ],
              );
            },
          ),
        ),
        Wrap(
          spacing: 10,
          runSpacing: 20,
          children: menuController.selectedOptions
              .map(
                (option) => Chip(
                  label: Text(option.label),
                  onDeleted: () => onOptionRemoved(option),
                  shape: const StadiumBorder(),
                ),
              )
              .toList(),
        ),
      ],
    );
  }
}

完整示例

以下是一个完整的示例,展示了如何在一个应用中使用上述选择字段组件:

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

const fruitOptions = <String>[
  'Apple',
  'Banana',
  'Strawberry',
  'Cherry',
  'Orange',
  'Raspberry',
];

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Select Field',
      theme: ThemeData.light(),
      home: const MyHomePage(),
    );
  }
}

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

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    final options = fruitOptions
        .map((fruit) => Option(label: fruit, value: fruit))
        .toList();

    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'Search and Select',
          style: TextStyle(
            color: Colors.white,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 32),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            StyledSelectField(options: options),
            const SizedBox(height: 32),
            CustomMenuSelectField(options: options),
            const SizedBox(height: 32),
            MultiSelectOptionsControl<String>(options: options),
            const SizedBox(height: 32),
            SearchSelectField(options: options),
            const SizedBox(height: 32),
            SearchMultiSelectField(options: options),
          ],
        ),
      ),
    );
  }
}

class StyledSelectField extends StatelessWidget {
  final List<Option<String>> options;

  const StyledSelectField({super.key, required this.options});

  @override
  Widget build(BuildContext context) {
    return SelectField<String>(
      options: options,
      initialOption: Option<String>(
        label: fruitOptions[0],
        value: fruitOptions[0],
      ),
      menuPosition: MenuPosition.below,
      onTextChanged: (value) => debugPrint(value),
      onOptionSelected: (option) => debugPrint(option.toString()),
      inputStyle: const TextStyle(),
      menuDecoration: MenuDecoration(
        margin: const EdgeInsets.only(top: 8),
        height: 365,
        alignment: MenuAlignment.center,
        backgroundDecoration: BoxDecoration(
          color: Colors.green[100],
          borderRadius: BorderRadius.circular(12),
          boxShadow: [
            BoxShadow(
              offset: const Offset(1, 1),
              color: Colors.brown[300]!,
              blurRadius: 3,
            ),
          ],
        ),
        animationDuration: const Duration(milliseconds: 400),
        buttonStyle: TextButton.styleFrom(
          fixedSize: const Size(double.infinity, 60),
          backgroundColor: Colors.green[100],
          alignment: Alignment.centerLeft,
          padding: const EdgeInsets.all(16),
          shape: const RoundedRectangleBorder(),
          textStyle: const TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w600,
            color: Colors.green,
          ),
        ),
        separatorBuilder: (context, index) => Container(
          height: 1,
          width: double.infinity,
          color: Colors.green,
        ),
      ),
    );
  }
}

class CustomMenuSelectField extends StatelessWidget {
  final List<Option<String>> options;

  const CustomMenuSelectField({super.key, required this.options});

  @override
  Widget build(BuildContext context) {
    return SelectField<String>(
      options: options,
      optionBuilder: (context, option, isSelected, onOptionSelected) {
        return GestureDetector(
          onTap: () => onOptionSelected(option),
          child: isSelected
              ? Container(
                  height: 60,
                  color: Colors.deepOrange.withOpacity(0.2),
                  child: Center(
                    child: Text(
                      option.label,
                      style: const TextStyle(
                        color: Colors.deepOrange,
                      ),
                    ),
                  ),
                )
              : SizedBox(
                  height: 60,
                  child: Center(
                    child: Text(
                      option.label,
                      style: TextStyle(
                        color: Colors.orange[700],
                      ),
                    ),
                  ),
                ),
        );
      },
    );
  }
}

class SearchSelectField extends StatelessWidget {
  final List<Option<String>> options;

  const SearchSelectField({super.key, required this.options});

  @override
  Widget build(BuildContext context) {
    return SelectField<String>(
      options: options,
      searchable: true,
      searchHint: 'Search fruits...',
      onTextChanged: (value) => debugPrint(value),
      onOptionSelected: (option) => debugPrint(option.toString()),
    );
  }
}

class SearchMultiSelectField extends StatelessWidget {
  final List<Option<String>> options;

  const SearchMultiSelectField({super.key, required this.options});

  @override
  Widget build(BuildContext context) {
    return MultiSelectField<String>(
      options: options,
      searchable: true,
      searchHint: 'Search fruits...',
      onOptionsSelected: (options) => debugPrint(options.toString()),
    );
  }
}

以上示例展示了如何在 Flutter 应用中使用 select_field 插件来创建单选和多选选择字段,并自定义下拉菜单的外观和行为。希望这些示例对你有所帮助!更多详细信息可以参考 GitHub 仓库


更多关于Flutter选择字段插件select_field的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter选择字段插件select_field的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter中使用select_field插件的一个简单示例。select_field是一个Flutter插件,用于创建带有搜索功能的下拉菜单(选择字段)。

首先,你需要在你的pubspec.yaml文件中添加select_field依赖项:

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

然后运行flutter pub get来获取依赖项。

以下是一个完整的Flutter应用示例,展示了如何使用select_field

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

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

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

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

class _MyHomePageState extends State<MyHomePage> {
  final List<String> options = [
    'Option 1',
    'Option 2',
    'Option 3',
    'Option 4',
    'Option 5',
  ];

  String? selectedOption;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Select Field Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            SelectField<String>(
              label: 'Choose an option',
              hint: 'Select...',
              options: options,
              onSelected: (value) {
                setState(() {
                  selectedOption = value;
                });
              },
              searchEnabled: true,
              clearable: true,
            ),
            SizedBox(height: 20),
            Text('Selected Option: ${selectedOption ?? 'None'}'),
          ],
        ),
      ),
    );
  }
}

代码解释

  1. 依赖项:在pubspec.yaml文件中添加select_field依赖项。

  2. MyApp:主应用类,设置应用的主题和主页。

  3. MyHomePage:主页,包含状态(选中的选项)。

  4. options:包含下拉选项的列表。

  5. selectedOption:存储当前选中的选项。

  6. SelectField

    • label:标签,显示在字段上方。
    • hint:提示文本,显示在没有选中任何选项时。
    • options:选项列表。
    • onSelected:当用户选择一个选项时调用的回调函数,用于更新状态。
    • searchEnabled:启用搜索功能。
    • clearable:允许用户清除选中项。
  7. Text:显示当前选中的选项。

这样,你就可以在你的Flutter应用中使用select_field插件了。你可以根据需要进一步自定义和扩展这个示例。

回到顶部