在这里插入图片描述

前言

标签选择是社交应用中用于内容分类和用户兴趣标记的常用功能。一个优秀的标签选择组件需要支持多选、搜索过滤、自定义标签以及流式布局等功能。本文将详细讲解如何在Flutter和OpenHarmony平台上构建功能完善的标签选择组件。

Flutter标签选择实现

首先实现基础的标签组件。

class Tag extends StatelessWidget {
  final String label;
  final bool isSelected;
  final VoidCallback onTap;
  
  const Tag({
    Key? key,
    required this.label,
    required this.isSelected,
    required this.onTap,
  }) : super(key: key);
  
  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        decoration: BoxDecoration(
          color: isSelected ? Colors.blue : Colors.grey[200],
          borderRadius: BorderRadius.circular(20),
        ),
        child: Text(
          label,
          style: TextStyle(
            color: isSelected ? Colors.white : Colors.black87,
            fontSize: 14,
          ),
        ),
      ),
    );
  }
}

Tag组件显示单个标签,根据选中状态切换背景色和文字颜色。圆角胶囊形状是标签的标准设计。

class TagSelector extends StatefulWidget {
  final List<String> availableTags;
  final List<String> selectedTags;
  final Function(List<String>) onSelectionChanged;
  final int? maxSelection;
  
  const TagSelector({
    Key? key,
    required this.availableTags,
    required this.selectedTags,
    required this.onSelectionChanged,
    this.maxSelection,
  }) : super(key: key);
  
  
  State<TagSelector> createState() => _TagSelectorState();
}

TagSelector组件管理标签选择状态。availableTags是可选标签列表,selectedTags是已选标签,maxSelection限制最大选择数量。

class _TagSelectorState extends State<TagSelector> {
  late List<String> _selected;
  
  
  void initState() {
    super.initState();
    _selected = List.from(widget.selectedTags);
  }
  
  
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 8,
      runSpacing: 8,
      children: widget.availableTags.map((tag) {
        final isSelected = _selected.contains(tag);
        return Tag(
          label: tag,
          isSelected: isSelected,
          onTap: () => _toggleTag(tag),
        );
      }).toList(),
    );
  }

Wrap组件实现流式布局,标签自动换行。spacing和runSpacing控制水平和垂直间距。map方法将标签数据转换为Tag组件列表。

  void _toggleTag(String tag) {
    setState(() {
      if (_selected.contains(tag)) {
        _selected.remove(tag);
      } else {
        if (widget.maxSelection == null || 
            _selected.length < widget.maxSelection!) {
          _selected.add(tag);
        }
      }
    });
    widget.onSelectionChanged(_selected);
  }
}

_toggleTag方法处理标签切换逻辑。如果已选中则取消,未选中则添加(需检查是否超过最大数量)。状态变化后通过回调通知父组件。

OpenHarmony ArkTS实现

鸿蒙系统上的标签选择器实现。

@Component
struct TagSelector {
  @Prop availableTags: string[] = []
  @State selectedTags: string[] = []
  maxSelection: number = 0
  onSelectionChanged: (tags: string[]) => void = () => {}
  
  build() {
    Flex({ wrap: FlexWrap.Wrap }) {
      ForEach(this.availableTags, (tag: string) => {
        this.TagItem(tag)
      })
    }
    .width('100%')
  }

Flex组件配合FlexWrap.Wrap实现流式布局。ForEach遍历标签列表生成标签项。

  @Builder
  TagItem(tag: string) {
    Text(tag)
      .fontSize(14)
      .fontColor(this.isSelected(tag) ? Color.White : '#1C1C1E')
      .backgroundColor(this.isSelected(tag) ? '#007AFF' : '#E5E5EA')
      .borderRadius(20)
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .margin({ right: 8, bottom: 8 })
      .onClick(() => this.toggleTag(tag))
  }
  
  isSelected(tag: string): boolean {
    return this.selectedTags.includes(tag)
  }

@Builder定义标签项构建方法。样式根据选中状态变化,选中显示蓝色背景白色文字,未选中显示灰色背景黑色文字。

  toggleTag(tag: string) {
    if (this.isSelected(tag)) {
      this.selectedTags = this.selectedTags.filter(t => t !== tag)
    } else {
      if (this.maxSelection === 0 || this.selectedTags.length < this.maxSelection) {
        this.selectedTags = [...this.selectedTags, tag]
      }
    }
    this.onSelectionChanged(this.selectedTags)
  }
}

toggleTag方法处理标签切换。使用filter移除标签,使用展开运算符添加标签。maxSelection为0表示不限制数量。

可搜索标签选择器

在Flutter中实现带搜索功能的标签选择器:

class SearchableTagSelector extends StatefulWidget {
  final List<String> availableTags;
  final Function(List<String>) onSelectionChanged;
  
  const SearchableTagSelector({
    Key? key,
    required this.availableTags,
    required this.onSelectionChanged,
  }) : super(key: key);
  
  
  State<SearchableTagSelector> createState() => _SearchableTagSelectorState();
}

class _SearchableTagSelectorState extends State<SearchableTagSelector> {
  String _searchQuery = '';
  List<String> _selected = [];
  
  List<String> get _filteredTags {
    if (_searchQuery.isEmpty) return widget.availableTags;
    return widget.availableTags
      .where((tag) => tag.toLowerCase().contains(_searchQuery.toLowerCase()))
      .toList();
  }
  
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          decoration: InputDecoration(
            hintText: '搜索标签',
            prefixIcon: Icon(Icons.search),
          ),
          onChanged: (value) => setState(() => _searchQuery = value),
        ),
        SizedBox(height: 16),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: _filteredTags.map((tag) {
            return Tag(
              label: tag,
              isSelected: _selected.contains(tag),
              onTap: () {
                setState(() {
                  if (_selected.contains(tag)) {
                    _selected.remove(tag);
                  } else {
                    _selected.add(tag);
                  }
                });
                widget.onSelectionChanged(_selected);
              },
            );
          }).toList(),
        ),
      ],
    );
  }
}

SearchableTagSelector在标签列表上方添加搜索框。_filteredTags根据搜索关键词过滤标签列表,实现实时搜索功能。这种设计在标签数量较多时非常有用。

自定义标签输入

在Flutter中实现自定义标签输入:

class CustomTagInput extends StatefulWidget {
  final Function(String) onTagAdded;
  
  const CustomTagInput({
    Key? key,
    required this.onTagAdded,
  }) : super(key: key);
  
  
  State<CustomTagInput> createState() => _CustomTagInputState();
}

class _CustomTagInputState extends State<CustomTagInput> {
  final TextEditingController _controller = TextEditingController();
  
  
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: TextField(
            controller: _controller,
            decoration: InputDecoration(
              hintText: '输入自定义标签',
            ),
            onSubmitted: _addTag,
          ),
        ),
        IconButton(
          icon: Icon(Icons.add),
          onPressed: () => _addTag(_controller.text),
        ),
      ],
    );
  }
  
  void _addTag(String tag) {
    if (tag.trim().isNotEmpty) {
      widget.onTagAdded(tag.trim());
      _controller.clear();
    }
  }
}

CustomTagInput允许用户输入自定义标签。按回车或点击添加按钮将标签添加到列表。trim()去除首尾空格,确保标签有效。

总结

本文详细介绍了标签选择组件在Flutter和OpenHarmony两个平台上的实现。标签选择是社交应用内容分类的常用功能,需要支持多选和流式布局。两个平台都使用Wrap/Flex实现自动换行。在实际项目中,还可以扩展标签推荐、热门标签、标签分组等功能。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐