Flutter for OpenHarmony轻量级开源记事本app实战:创建分类对话框
设计理念
分类是组织笔记的重要方式,用户可以将不同主题的笔记放在不同的分类中。创建分类对话框需要让用户输入分类名称并选择颜色,这个过程应该简单直观。本文将详细介绍如何实现一个功能完整的创建分类对话框。
对话框的基础结构
首先定义对话框的基本类结构和依赖导入。
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CreateCategoryDialog extends StatelessWidget {
final Function(String, String) onCategoryCreated;
const CreateCategoryDialog({
super.key,
required this.onCategoryCreated,
});
CreateCategoryDialog采用StatelessWidget设计,因为对话框本身不需要维护复杂的内部状态。onCategoryCreated回调函数接收两个参数:分类名称和颜色值,用于在用户完成创建操作后通知父组件。这种基于回调的设计模式符合Flutter的单向数据流原则,使得组件间的通信清晰明确。flutter_screenutil用于实现响应式布局,确保对话框在不同屏幕尺寸下都能正常显示。
接下来定义可供用户选择的颜色列表。
static const List<String> colors = [
'#2196F3', '#4CAF50', '#FF9800', '#F44336',
'#9C27B0', '#00BCD4', '#FFEB3B', '#795548',
];
颜色列表包含8种精心挑选的Material Design标准色,涵盖了蓝、绿、橙、红、紫、青、黄、棕等常用色系。使用十六进制字符串格式存储颜色值,便于在数据库中持久化存储,同时也方便通过Color类的解析方法转换为Flutter可用的颜色对象。这种设计既保证了视觉美观性,又提供了足够的选择空间,让用户能够根据个人喜好为不同分类设置独特的标识色。
对话框的UI实现
构建对话框的主体结构,包括标题和内容区域。
Widget build(BuildContext context) {
final nameController = TextEditingController();
String selectedColor = colors.first;
return AlertDialog(
title: const Text('新建分类'),
content: SizedBox(
width: 300.w,
build方法中创建了TextEditingController用于管理输入框的文本内容,selectedColor变量初始化为颜色列表的第一个值作为默认选择。AlertDialog是Flutter提供的标准对话框组件,符合Material Design规范。SizedBox设置固定宽度300.w,使用ScreenUtil的响应式单位确保在不同设备上保持一致的视觉比例。这种固定宽度的设计避免了对话框在大屏设备上过度拉伸,保持了良好的视觉效果。
继续构建内容区域的输入组件。
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: '分类名称',
border: OutlineInputBorder(),
),
autofocus: true,
),
Column使用MainAxisSize.min确保对话框高度根据内容自适应,不会占用过多空间。TextField配置了OutlineInputBorder边框样式,提供清晰的输入区域视觉反馈。autofocus设置为true,使对话框弹出时输入框自动获得焦点,用户可以立即开始输入,无需额外点击,这种细节优化显著提升了交互效率。labelText提供了明确的输入提示,符合Material Design的表单设计规范。
添加颜色选择器组件和间距控制。
SizedBox(height: 16.h),
_buildColorPicker(selectedColor, (color) {
selectedColor = color;
}),
],
),
),
SizedBox提供16个逻辑像素的垂直间距,使输入框和颜色选择器之间有适当的视觉分隔。_buildColorPicker方法接收当前选中的颜色和一个回调函数,当用户选择新颜色时,回调函数会更新selectedColor变量。这种设计将颜色选择的UI逻辑封装在独立方法中,提高了代码的可维护性和可读性。回调函数的使用使得颜色选择器成为一个可复用的组件。
实现对话框的操作按钮区域。
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
if (nameController.text.isNotEmpty) {
onCategoryCreated(nameController.text, selectedColor);
Navigator.pop(context);
}
},
child: const Text('创建'),
),
],
);
}
}
actions区域包含两个按钮,遵循Material Design的对话框操作规范。TextButton用于次要操作(取消),ElevatedButton用于主要操作(创建),通过视觉层次引导用户关注主要操作。创建按钮在执行前会验证输入框是否为空,只有在有效输入的情况下才会调用onCategoryCreated回调并关闭对话框。这种简单的验证机制防止了创建空名称的分类,保证了数据的基本完整性。
颜色选择器实现
构建颜色选择器的基础结构和标题。
Widget _buildColorPicker(String selectedColor,
Function(String) onColorSelected) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('选择颜色'),
SizedBox(height: 8.h),
_buildColorPicker方法接收当前选中的颜色和颜色变化回调函数作为参数,返回一个完整的颜色选择器Widget。Column使用CrossAxisAlignment.start使子组件左对齐,保持与输入框的视觉一致性。标题文本"选择颜色"为用户提供明确的操作指引,SizedBox提供8个逻辑像素的间距,使标题与颜色选项之间有适当的视觉分隔。这种清晰的层次结构让用户能够快速理解界面功能。
使用Wrap布局展示颜色选项网格。
Wrap(
spacing: 8.w,
runSpacing: 8.h,
children: colors.map((color) {
final isSelected = color == selectedColor;
return GestureDetector(
onTap: () => onColorSelected(color),
Wrap组件自动处理子元素的换行布局,spacing和runSpacing分别设置水平和垂直间距为8个逻辑像素,确保颜色块之间有适当的间隔。通过map方法遍历colors列表,为每个颜色创建一个可点击的视觉元素。isSelected变量判断当前颜色是否被选中,用于控制视觉反馈。GestureDetector包裹每个颜色块,处理点击事件,当用户点击时调用onColorSelected回调更新选中状态。这种响应式布局设计使颜色选择器能够适应不同的屏幕宽度。
实现颜色块的视觉样式和选中状态。
child: Container(
width: 32.w,
height: 32.h,
decoration: BoxDecoration(
color: Color(int.parse(
color.replaceFirst('#', '0xFF'))),
borderRadius: BorderRadius.circular(16),
border: isSelected
? Border.all(color: Colors.black, width: 2)
: null,
),
Container设置固定尺寸32x32逻辑像素,确保所有颜色块大小一致。BoxDecoration中,通过字符串解析将十六进制颜色值转换为Color对象,replaceFirst方法将’#'替换为’0xFF’以符合Flutter的颜色格式。borderRadius设置为16使颜色块呈现圆形,提供更柔和的视觉效果。当颜色被选中时,添加2像素宽的黑色边框作为视觉反馈,未选中时border为null不显示边框。这种设计清晰地向用户展示当前选择状态。
添加选中状态的图标指示器。
child: isSelected
? const Icon(Icons.check,
color: Colors.white, size: 16)
: null,
),
);
}).toList(),
),
],
);
}
在选中的颜色块中心显示白色的勾选图标,图标大小设置为16,与32像素的容器形成良好的比例关系。使用三元运算符,只有在isSelected为true时才显示图标,未选中时child为null不显示任何内容。白色图标在各种颜色背景上都能保持良好的可见性,即使在浅色背景上也能通过黑色边框区分。toList()将map操作的结果转换为列表传递给Wrap组件。这种双重视觉反馈(边框+图标)确保用户能够清晰地识别当前选择。
带验证的对话框
为了提供更好的用户体验,需要添加完善的输入验证功能。首先定义带验证的对话框类。
class ValidatedCreateDialog extends StatefulWidget {
final Function(String, String) onCategoryCreated;
const ValidatedCreateDialog({
super.key,
required this.onCategoryCreated,
});
State<ValidatedCreateDialog> createState() =>
_ValidatedCreateDialogState();
}
ValidatedCreateDialog使用StatefulWidget而非StatelessWidget,因为需要管理表单验证状态、错误信息显示等动态变化的UI状态。通过State对象可以在用户输入过程中实时更新验证结果和错误提示。这种设计使得对话框能够提供即时的输入反馈,帮助用户及时发现和纠正输入错误。createState方法创建对应的State对象来管理这些可变状态。
创建State类并初始化表单相关的控制器和状态变量。
class _ValidatedCreateDialogState
extends State<ValidatedCreateDialog> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
String _selectedColor = CreateCategoryDialog.colors.first;
String? _errorMessage;
_formKey是Form组件的全局键,用于访问表单状态并触发验证。_nameController管理输入框的文本内容,_selectedColor存储用户选择的颜色,初始化为颜色列表的第一个值。_errorMessage用于存储自定义的错误信息,类型为String?表示可以为null。这些状态变量的合理组织使得表单验证逻辑清晰可控,便于在不同场景下展示相应的验证反馈。
构建对话框的基本结构和Form包裹。
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('新建分类'),
content: SizedBox(
width: 300.w,
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
AlertDialog提供标准的对话框容器,Form组件通过_formKey关联,使得可以统一管理内部所有表单字段的验证。Form的使用是Flutter表单验证的标准做法,它能够协调多个FormField的验证状态,提供统一的验证触发机制。Column使用MainAxisSize.min确保对话框高度根据内容自适应,避免不必要的空白空间。这种结构化的布局为后续添加更多表单字段提供了良好的扩展性。
实现带验证功能的文本输入框。
children: [
TextFormField(
controller: _nameController,
decoration: InputDecoration(
labelText: '分类名称',
border: const OutlineInputBorder(),
errorText: _errorMessage,
),
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入分类名称';
}
if (value.length > 20) {
return '分类名称不能超过20个字符';
}
return null;
},
TextFormField是支持验证的文本输入组件,validator函数定义了验证规则。首先检查输入是否为空,然后验证长度是否超过20个字符。返回null表示验证通过,返回字符串则作为错误信息显示。errorText属性显示自定义的错误信息,与validator的返回值配合使用。这种多层次的验证机制既保证了数据的基本完整性,又限制了输入长度,防止过长的分类名称影响UI显示效果。
添加输入变化监听和自动聚焦。
autofocus: true,
onChanged: (value) {
setState(() {
_errorMessage = null;
});
},
),
SizedBox(height: 16.h),
_buildColorPicker(_selectedColor, (color) {
setState(() {
_selectedColor = color;
});
}),
],
autofocus确保对话框打开时输入框自动获得焦点,提升输入效率。onChanged回调在用户输入时触发,通过setState清除之前的错误信息,给用户一个"重新开始"的感觉,避免错误提示一直显示影响体验。颜色选择器的回调同样使用setState更新_selectedColor状态,确保UI能够实时反映用户的选择。这种即时反馈的设计让用户能够清楚地看到自己的操作结果。
实现对话框的操作按钮和验证触发。
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: _validateAndCreate,
child: const Text('创建'),
),
],
);
}
actions区域包含取消和创建两个按钮,取消按钮直接关闭对话框,创建按钮调用_validateAndCreate方法。将验证和创建逻辑封装在独立方法中,使代码结构更清晰,也便于后续扩展更复杂的验证逻辑。ElevatedButton的视觉突出性引导用户关注主要操作,符合Material Design的交互设计原则。这种按钮布局是对话框的标准模式,用户已经形成了操作习惯。
实现验证和创建的核心逻辑方法。
void _validateAndCreate() {
if (_formKey.currentState!.validate()) {
widget.onCategoryCreated(
_nameController.text, _selectedColor);
Navigator.pop(context);
}
}
}
_validateAndCreate方法通过_formKey.currentState.validate()触发表单验证,该方法会调用所有FormField的validator函数。只有当所有验证都通过(返回null)时,validate()才返回true。验证通过后,调用widget.onCategoryCreated回调传递分类名称和颜色,然后关闭对话框。这种先验证后执行的流程确保了只有合法的数据才能被提交,有效防止了无效数据的产生,提高了应用的数据质量和稳定性。
重复名称检查
在实际应用中,需要防止用户创建重名的分类。首先定义带重复检查的对话框类。
class UniqueCreateDialog extends StatefulWidget {
final Function(String, String) onCategoryCreated;
final List<String> existingNames;
const UniqueCreateDialog({
super.key,
required this.onCategoryCreated,
required this.existingNames,
});
UniqueCreateDialog在ValidatedCreateDialog的基础上增加了existingNames参数,用于传入已存在的分类名称列表。通过这个列表可以在用户输入时检查名称是否重复,避免创建同名分类导致的数据混淆。这种设计将重复检查的逻辑封装在对话框内部,调用方只需提供现有名称列表即可,降低了使用复杂度。existingNames作为必需参数,确保每次创建分类时都会进行重复检查。
创建State类并初始化状态变量。
State<UniqueCreateDialog> createState() =>
_UniqueCreateDialogState();
}
class _UniqueCreateDialogState extends State<UniqueCreateDialog> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
String _selectedColor = CreateCategoryDialog.colors.first;
bool _isChecking = false;
_UniqueCreateDialogState继承自State类,管理对话框的可变状态。除了表单键、名称控制器和选中颜色这些基本状态外,新增了_isChecking布尔变量用于标识是否正在进行重复检查。这个状态变量用于控制加载指示器的显示和按钮的禁用状态,防止用户在检查过程中重复提交。通过合理的状态管理,可以为用户提供清晰的操作反馈,提升交互体验。
构建对话框的基本结构。
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('新建分类'),
content: SizedBox(
width: 300.w,
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AlertDialog和Form的结构与之前的验证对话框保持一致,这种一致性使得代码易于理解和维护。Form组件通过_formKey关联,便于统一触发验证。Column使用MainAxisSize.min确保对话框高度自适应内容,不会出现多余的空白区域。这种标准化的结构为后续添加更多功能提供了良好的基础,也符合Flutter的组件组合设计理念。
实现带加载指示器的输入框。
TextFormField(
controller: _nameController,
decoration: InputDecoration(
labelText: '分类名称',
border: const OutlineInputBorder(),
suffixIcon: _isChecking
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2),
)
: null,
),
TextFormField的decoration中添加了suffixIcon属性,根据_isChecking状态动态显示加载指示器。当正在检查重复时,在输入框右侧显示一个小型的CircularProgressIndicator,strokeWidth设置为2使其更精致。SizedBox限制指示器的尺寸为16x16,避免占用过多空间。这种即时的视觉反馈让用户知道系统正在处理他们的输入,减少了等待的焦虑感,提升了用户体验。
添加重复名称的验证逻辑。
validator: (value) {
if (value == null || value.isEmpty) {
return '请输入分类名称';
}
if (widget.existingNames.contains(value)) {
return '分类名称已存在';
}
return null;
},
onChanged: (value) {
_validateUniqueness(value);
},
autofocus: true,
),
validator函数在基本的空值检查之后,增加了重复名称检查。通过widget.existingNames.contains(value)判断输入的名称是否已存在,如果存在则返回错误提示"分类名称已存在"。onChanged回调在用户每次输入时触发_validateUniqueness方法,实现实时的重复检查。这种即时验证的设计让用户能够在输入过程中就发现问题,而不是等到点击创建按钮时才得到反馈,大大提升了交互的流畅性。
添加颜色选择器和完成Form结构。
SizedBox(height: 16.h),
_buildColorPicker(_selectedColor, (color) {
setState(() {
_selectedColor = color;
});
}),
],
),
),
),
SizedBox提供垂直间距,_buildColorPicker创建颜色选择器组件。颜色选择的回调使用setState更新_selectedColor状态,确保UI能够实时反映用户的选择。这部分代码与之前的实现保持一致,体现了组件复用的优势。通过将颜色选择器封装为独立方法,可以在不同的对话框中重复使用,减少代码重复,提高可维护性。
实现操作按钮区域。
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: _isChecking ? null : _validateAndCreate,
child: const Text('创建'),
),
],
);
}
actions区域的创建按钮根据_isChecking状态动态设置onPressed回调。当_isChecking为true时,onPressed设置为null,按钮自动变为禁用状态,防止用户在检查过程中重复点击。这种设计避免了并发操作可能导致的问题,确保了操作的原子性。取消按钮始终保持可用,让用户在任何时候都能退出对话框。这种细致的交互控制体现了对用户体验的深入考虑。
实现唯一性检查的异步方法。
void _validateUniqueness(String value) async {
if (value.isEmpty) return;
setState(() {
_isChecking = true;
});
await Future.delayed(const Duration(milliseconds: 500));
setState(() {
_isChecking = false;
});
}
_validateUniqueness方法模拟异步的重复检查过程。首先判断输入是否为空,空值直接返回不进行检查。然后通过setState设置_isChecking为true,触发加载指示器显示。Future.delayed模拟网络请求或数据库查询的延迟,实际应用中应替换为真实的检查逻辑。检查完成后再次调用setState将_isChecking设置为false,隐藏加载指示器并启用创建按钮。这种异步处理模式是Flutter中处理耗时操作的标准做法。
实现验证和创建的方法。
void _validateAndCreate() {
if (_formKey.currentState!.validate()) {
widget.onCategoryCreated(
_nameController.text, _selectedColor);
Navigator.pop(context);
}
}
}
_validateAndCreate方法的实现与之前的验证对话框相同,通过表单验证确保所有输入都符合要求后,调用回调函数并关闭对话框。由于重复检查已经集成在validator中,这里不需要额外的检查逻辑。这种设计将验证逻辑集中管理,使代码结构清晰,易于维护。表单验证机制的统一使用保证了验证逻辑的一致性和可靠性。
智能建议功能
为了进一步提升用户体验,可以添加智能建议功能,根据用户输入提供相关的分类名称建议。首先定义带智能建议的对话框类。
class SmartCreateDialog extends StatefulWidget {
final Function(String, String) onCategoryCreated;
final List<String> existingNames;
const SmartCreateDialog({
super.key,
required this.onCategoryCreated,
required this.existingNames,
});
SmartCreateDialog在UniqueCreateDialog的基础上增加了智能建议功能。existingNames列表不仅用于重复检查,还用于生成智能建议。当用户输入时,系统会从现有分类中搜索相关的名称作为建议,帮助用户快速选择或参考。这种设计充分利用了现有数据,为用户提供了更智能的输入辅助,特别适合分类数量较多的场景。
创建State类并初始化建议相关的状态。
State<SmartCreateDialog> createState() =>
_SmartCreateDialogState();
}
class _SmartCreateDialogState extends State<SmartCreateDialog> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
String _selectedColor = CreateCategoryDialog.colors.first;
List<String> _suggestions = [];
bool _showSuggestions = false;
_SmartCreateDialogState在之前的基础上新增了_suggestions列表和_showSuggestions布尔变量。_suggestions存储根据用户输入生成的建议列表,_showSuggestions控制建议菜单的显示状态。这两个状态变量配合使用,实现了动态的建议显示逻辑。当用户输入时,系统会更新建议列表并显示菜单;当输入为空或没有匹配项时,隐藏菜单。这种状态管理方式使得建议功能的交互逻辑清晰可控。
构建对话框的基本结构。
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('新建分类'),
content: SizedBox(
width: 300.w,
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AlertDialog、Form和Column的结构保持一致,这种标准化的结构使得代码易于理解和维护。通过复用相同的基础结构,可以专注于实现新功能(智能建议)而不需要重新设计整体布局。这种渐进式的功能增强方式是软件开发的最佳实践,既保证了代码的一致性,又便于功能的扩展和维护。
实现带建议下拉菜单的输入框。
TextFormField(
controller: _nameController,
decoration: InputDecoration(
labelText: '分类名称',
border: const OutlineInputBorder(),
suffixIcon: _showSuggestions &&
_suggestions.isNotEmpty
? PopupMenuButton<String>(
icon: const Icon(Icons.arrow_drop_down),
onSelected: (value) {
_nameController.text = value;
_updateSuggestions(value);
},
TextFormField的suffixIcon根据建议状态动态显示PopupMenuButton。只有当_showSuggestions为true且_suggestions不为空时才显示下拉按钮,避免在没有建议时显示无用的按钮。PopupMenuButton使用arrow_drop_down图标,符合下拉菜单的视觉习惯。onSelected回调在用户选择建议时触发,将选中的值填充到输入框,并调用_updateSuggestions更新建议状态。这种设计让用户可以快速选择建议的分类名称,大大提升了输入效率。
构建建议菜单的选项列表。
itemBuilder: (context) => _suggestions
.map((suggestion) => PopupMenuItem(
value: suggestion,
child: Text(suggestion),
))
.toList(),
)
: null,
),
onChanged: (value) {
_updateSuggestions(value);
},
autofocus: true,
),
itemBuilder通过map方法将_suggestions列表转换为PopupMenuItem列表。每个建议项显示为一个简单的文本菜单项,点击后会触发onSelected回调。onChanged回调在用户每次输入时调用_updateSuggestions方法,实时更新建议列表。这种即时响应的设计让建议功能更加智能和实用。autofocus确保对话框打开时输入框自动获得焦点,用户可以立即开始输入并看到相关建议。
添加颜色选择器和完成表单结构。
SizedBox(height: 16.h),
_buildColorPicker(_selectedColor, (color) {
setState(() {
_selectedColor = color;
});
}),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: _validateAndCreate,
child: const Text('创建'),
),
],
);
}
颜色选择器和操作按钮的实现与之前保持一致,体现了组件复用的优势。通过将通用功能封装为独立方法,可以在不同的对话框变体中重复使用,减少代码重复。actions区域的按钮布局遵循Material Design规范,为用户提供一致的操作体验。这种模块化的设计使得代码结构清晰,便于维护和扩展。
实现智能建议的更新逻辑。
void _updateSuggestions(String value) {
if (value.isEmpty) {
setState(() {
_suggestions.clear();
_showSuggestions = false;
});
return;
}
final suggestions = widget.existingNames
.where((name) => name.toLowerCase()
.contains(value.toLowerCase()))
.take(5)
.toList();
_updateSuggestions方法根据用户输入动态生成建议列表。首先检查输入是否为空,空输入时清空建议列表并隐藏菜单。对于非空输入,使用where方法过滤existingNames,查找包含输入文本的分类名称。toLowerCase()确保搜索不区分大小写,提供更宽松的匹配。take(5)限制建议数量为5个,避免列表过长影响用户选择。这种模糊匹配的搜索算法简单高效,能够快速找到相关的分类名称。
更新建议状态并触发UI刷新。
setState(() {
_suggestions = suggestions;
_showSuggestions = true;
});
}
void _validateAndCreate() {
if (_formKey.currentState!.validate()) {
widget.onCategoryCreated(
_nameController.text, _selectedColor);
Navigator.pop(context);
}
}
}
setState更新_suggestions和_showSuggestions状态,触发UI重新构建,显示最新的建议列表。_validateAndCreate方法的实现与之前相同,通过表单验证确保输入合法后执行创建操作。这种一致的验证和创建流程保证了不同对话框变体的行为一致性。智能建议功能的加入使得对话框更加智能和易用,特别是在分类数量较多时,能够显著提升用户的输入效率和体验。
使用示例
下面展示如何在实际应用中使用这些创建分类对话框。首先定义一个分类管理页面。
class CategoryPage extends StatelessWidget {
final List<String> existingCategories = [
'工作', '学习', '生活', '娱乐', '健康'
];
void _showCreateDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => CreateCategoryDialog(
CategoryPage是一个简单的分类管理页面示例,existingCategories列表存储了已存在的分类名称。_showCreateDialog方法演示了如何使用最基础的CreateCategoryDialog。showDialog是Flutter提供的标准方法,用于显示对话框。builder参数返回要显示的对话框组件。这种使用方式简单直接,适合不需要复杂验证的场景。
实现基础对话框的回调处理。
onCategoryCreated: (name, color) {
// 处理分类创建逻辑
print('创建分类: $name, 颜色: $color');
// 实际应用中应该:
// 1. 保存到数据库
// 2. 更新UI状态
// 3. 显示成功提示
},
),
);
}
onCategoryCreated回调接收分类名称和颜色两个参数,在这里处理分类创建的业务逻辑。示例中使用print输出,实际应用中应该将数据保存到数据库,更新应用状态,并向用户显示创建成功的提示。这种基于回调的设计将UI逻辑和业务逻辑分离,使得对话框组件可以在不同的场景中复用。注释说明了实际应用中应该执行的操作,为开发者提供了清晰的实现指引。
展示如何使用带重复检查的对话框。
void _showUniqueDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => UniqueCreateDialog(
onCategoryCreated: (name, color) {
print('创建唯一分类: $name, 颜色: $color');
// 保存分类到数据库
// 更新existingCategories列表
},
existingNames: existingCategories,
),
);
}
_showUniqueDialog方法展示了UniqueCreateDialog的使用方式。与基础对话框相比,需要额外传入existingNames参数,用于重复检查。这个参数应该包含当前所有已存在的分类名称。回调函数中除了保存新分类外,还应该更新existingCategories列表,确保后续的重复检查能够包含新创建的分类。这种设计确保了分类名称的唯一性,避免了数据冲突。
展示如何使用带智能建议的对话框。
void _showSmartDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => SmartCreateDialog(
onCategoryCreated: (name, color) {
print('创建智能分类: $name, 颜色: $color');
// 保存分类
// 更新分类列表
// 刷新UI
},
existingNames: existingCategories,
),
);
}
}
_showSmartDialog方法展示了SmartCreateDialog的使用方式。参数和回调与UniqueCreateDialog相同,但提供了额外的智能建议功能。existingNames不仅用于重复检查,还用于生成输入建议,充分利用了现有数据。这三个示例方法展示了从基础到高级的不同对话框使用场景,开发者可以根据实际需求选择合适的对话框类型。所有对话框都使用相同的回调接口,保证了API的一致性。
完整的颜色选择器实现
为了确保代码的完整性,这里提供颜色选择器的完整实现代码。
Widget _buildColorPicker(String selectedColor,
Function(String) onColorSelected) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('选择颜色',
style: TextStyle(fontSize: 14,
fontWeight: FontWeight.w500)),
SizedBox(height: 8.h),
_buildColorPicker是一个独立的方法,可以在所有对话框变体中复用。方法接收当前选中的颜色和颜色变化回调作为参数,返回一个完整的颜色选择器Widget。Column布局使用CrossAxisAlignment.start实现左对齐,标题文本使用14号字体和中等粗细,提供清晰的视觉层次。这种封装方式使得颜色选择功能可以轻松地在不同组件中重用,提高了代码的可维护性。
实现颜色网格布局。
Wrap(
spacing: 8.w,
runSpacing: 8.h,
children: CreateCategoryDialog.colors.map((color) {
final isSelected = color == selectedColor;
return GestureDetector(
onTap: () => onColorSelected(color),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: 32.w,
height: 32.h,
Wrap组件提供自动换行的网格布局,spacing和runSpacing设置颜色块之间的间距。使用AnimatedContainer替代普通Container,为选中状态的变化添加平滑的动画效果,duration设置为200毫秒,提供流畅的视觉反馈。遍历颜色列表,为每个颜色创建一个可点击的圆形色块。isSelected判断当前颜色是否被选中,用于控制边框和图标的显示。这种动画效果的加入使得交互更加生动自然。
完成颜色块的装饰和选中指示。
decoration: BoxDecoration(
color: Color(int.parse(
color.replaceFirst('#', '0xFF'))),
borderRadius: BorderRadius.circular(16),
border: isSelected
? Border.all(color: Colors.black, width: 2)
: null,
boxShadow: isSelected
? [BoxShadow(
color: Colors.black26,
blurRadius: 4,
offset: Offset(0, 2),
)]
: null,
),
BoxDecoration定义颜色块的视觉样式。color属性通过字符串解析将十六进制颜色值转换为Color对象。borderRadius设置为16使色块呈现圆形。选中状态下添加2像素宽的黑色边框和轻微的阴影效果,增强视觉反馈。boxShadow使用黑色半透明阴影,blurRadius为4提供柔和的模糊效果,offset设置为(0, 2)使阴影向下偏移,营造轻微的浮起效果。这些细节设计提升了整体的视觉质量。
添加选中图标并完成组件。
child: isSelected
? Icon(Icons.check,
color: Colors.white,
size: 16)
: null,
),
);
}).toList(),
),
],
);
}
在选中的颜色块中心显示白色的勾选图标,图标大小为16,与32像素的容器形成1:2的比例关系,视觉上更加协调。只有选中状态才显示图标,未选中时child为null。白色图标在各种颜色背景上都能保持良好的可见性。toList()将map操作的结果转换为列表。这个完整的颜色选择器实现了清晰的视觉反馈、流畅的动画效果和良好的用户体验,是一个可以直接在生产环境中使用的高质量组件。
总结
本文详细介绍了如何在Flutter for OpenHarmony应用中实现功能完整的创建分类对话框。从最基础的对话框开始,逐步添加了输入验证、重复检查和智能建议等高级功能。
每个对话框变体都针对不同的使用场景进行了优化。基础对话框提供了简单直接的创建功能,适合快速原型开发。带验证的对话框确保了输入数据的合法性,防止了无效数据的产生。带重复检查的对话框保证了分类名称的唯一性,避免了数据冲突。带智能建议的对话框提供了最佳的用户体验,特别适合分类数量较多的场景。
这些对话框的实现展示了Flutter的强大功能和灵活性。通过合理的组件设计和状态管理,可以创建出既美观又实用的用户界面。颜色选择器的封装体现了代码复用的重要性,统一的回调接口保证了API的一致性。这些设计原则和实现技巧可以应用到其他类似的功能开发中,帮助开发者构建高质量的Flutter应用。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)