Flutter&OpenHarmony商城App地址管理组件开发
Flutter与OpenHarmony地址管理组件开发 摘要:本文介绍了电商应用中地址管理组件的开发实现,包括数据模型定义和UI组件设计。Address类定义了收货地址的核心字段,如省市区信息、详细地址和默认标记。AddressList组件通过卡片式布局展示地址列表,支持选中状态高亮显示,并集成编辑、删除等操作功能。组件采用清晰的视觉层次,突出显示收货人关键信息,同时保持操作便捷性。该设计符合国内
前言
地址管理是商城应用中订单配送的基础功能,用户需要添加、编辑、删除收货地址,并在下单时选择配送地址。一个设计良好的地址管理组件能够让用户快速完成地址操作,减少下单过程中的摩擦。本文将详细介绍如何在Flutter和OpenHarmony平台上开发地址管理相关组件,包括地址列表、地址编辑表单、地址选择器等核心模块。
地址信息的准确性直接影响订单的配送成功率,因此地址表单的设计需要引导用户填写完整准确的信息。同时,为了提升用户体验,我们还需要支持地址的快速选择、默认地址设置、地址智能解析等功能,让用户能够以最少的操作完成地址管理。
Flutter地址数据模型
首先定义地址数据的模型结构:
class Address {
final String id;
final String name;
final String phone;
final String province;
final String city;
final String district;
final String detail;
final bool isDefault;
const Address({
required this.id,
required this.name,
required this.phone,
required this.province,
required this.city,
required this.district,
required this.detail,
this.isDefault = false,
});
String get fullAddress => '$province$city$district$detail';
}
Address类包含了收货地址的所有必要字段。name是收货人姓名,phone是联系电话,province、city、district分别是省、市、区三级行政区划,detail是详细地址。isDefault标记是否为默认地址,下单时会自动选中默认地址。fullAddress是一个计算属性,将省市区和详细地址拼接成完整的地址字符串,方便在界面上展示。这种数据模型的设计符合国内电商的地址规范,便于与物流系统对接。
地址列表组件
class AddressList extends StatelessWidget {
final List<Address> addresses;
final String? selectedId;
final ValueChanged<Address>? onSelect;
final ValueChanged<Address>? onEdit;
final ValueChanged<Address>? onDelete;
const AddressList({
Key? key,
required this.addresses,
this.selectedId,
this.onSelect,
this.onEdit,
this.onDelete,
}) : super(key: key);
Widget build(BuildContext context) {
return ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: addresses.length,
separatorBuilder: (_, __) => const SizedBox(height: 12),
itemBuilder: (context, index) {
return _buildAddressCard(addresses[index]);
},
);
}
}
AddressList组件展示用户的所有收货地址。selectedId用于标记当前选中的地址,在地址选择场景下使用。onSelect、onEdit、onDelete分别是选择、编辑、删除操作的回调函数。ListView.separated使用分隔构建器在地址卡片之间添加间距,比在每个卡片上设置margin更加灵活。这种设计将列表的展示逻辑与业务操作分离,组件可以在不同场景下复用。
地址卡片的实现:
Widget _buildAddressCard(Address address) {
final isSelected = address.id == selectedId;
return GestureDetector(
onTap: () => onSelect?.call(address),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isSelected
? const Color(0xFFE53935)
: const Color(0xFFEEEEEE),
width: isSelected ? 2 : 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(address),
const SizedBox(height: 8),
_buildAddressText(address),
const SizedBox(height: 12),
_buildActions(address),
],
),
),
);
}
地址卡片根据选中状态显示不同的边框样式,选中时使用红色粗边框,未选中时使用灰色细边框。Container设置白色背景和圆角,内部使用Column垂直排列收货人信息、地址文本和操作按钮。GestureDetector包装整个卡片,点击时触发选择回调。这种视觉设计让用户能够清晰地识别当前选中的地址,同时保持界面的整洁美观。
收货人信息头部:
Widget _buildHeader(Address address) {
return Row(
children: [
Text(
address.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF333333),
),
),
const SizedBox(width: 12),
Text(
address.phone,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF666666),
),
),
const Spacer(),
if (address.isDefault)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: const Color(0xFFFFF0F0),
borderRadius: BorderRadius.circular(2),
),
child: const Text(
'默认',
style: TextStyle(
fontSize: 10,
color: Color(0xFFE53935),
),
),
),
],
);
}
头部信息包含收货人姓名、电话和默认标签。姓名使用较大字号和粗体突出显示,电话使用较小字号作为辅助信息。Spacer将默认标签推到右侧,只有当地址是默认地址时才显示标签。标签使用浅红色背景和红色文字,与主题色保持一致。这种布局将最重要的信息放在最显眼的位置,用户可以快速识别每个地址的关键信息。
地址操作按钮
Widget _buildActions(Address address) {
return Row(
children: [
_buildActionButton(
icon: Icons.edit_outlined,
label: '编辑',
onTap: () => onEdit?.call(address),
),
const SizedBox(width: 24),
_buildActionButton(
icon: Icons.delete_outline,
label: '删除',
onTap: () => onDelete?.call(address),
),
],
);
}
Widget _buildActionButton({
required IconData icon,
required String label,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Row(
children: [
Icon(icon, size: 16, color: const Color(0xFF999999)),
const SizedBox(width: 4),
Text(
label,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
],
),
);
}
操作按钮区域包含编辑和删除两个功能按钮。每个按钮由图标和文字组成,使用灰色配色表明这是次要操作,不会与主要的选择操作产生视觉冲突。_buildActionButton是一个可复用的按钮构建方法,接收图标、文字和点击回调作为参数。Row水平排列两个按钮,24像素的间距使它们保持适当的视觉分隔。这种设计让用户可以方便地对地址进行管理操作。
OpenHarmony地址列表实现
@Component
struct AddressList {
@Prop addresses: AddressInfo[] = []
@Prop selectedId: string = ''
private onSelect: (address: AddressInfo) => void = () => {}
private onEdit: (address: AddressInfo) => void = () => {}
private onDelete: (address: AddressInfo) => void = () => {}
build() {
List() {
ForEach(this.addresses, (address: AddressInfo) => {
ListItem() {
this.AddressCard(address)
}
.margin({ bottom: 12 })
})
}
.width('100%')
.height('100%')
.padding(16)
}
}
OpenHarmony的地址列表使用List组件实现。@Prop装饰的属性从父组件接收数据,包括地址列表和选中ID。ForEach遍历地址数组,为每个地址创建ListItem。每个列表项设置底部外边距实现项间分隔。List组件设置100%的宽高填充父容器,padding设置内边距。这种实现方式与Flutter版本结构一致,确保两个平台的功能和体验统一。
地址数据接口定义:
interface AddressInfo {
id: string
name: string
phone: string
province: string
city: string
district: string
detail: string
isDefault: boolean
}
TypeScript接口定义了与Flutter相同的地址数据结构。所有字段都是必需的,isDefault使用boolean类型表示是否为默认地址。接口定义为组件提供了类型安全保障,在编译时就能发现类型错误,减少运行时问题。
地址卡片ArkUI实现
@Builder
AddressCard(address: AddressInfo) {
Column() {
this.CardHeader(address)
Text(address.province + address.city + address.district + address.detail)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 8 })
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
this.CardActions(address)
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
.border({
width: this.selectedId === address.id ? 2 : 1,
color: this.selectedId === address.id ? '#E53935' : '#EEEEEE'
})
.onClick(() => {
this.onSelect(address)
})
}
@Builder装饰器定义了地址卡片的构建方法。Column垂直排列头部信息、地址文本和操作按钮。地址文本通过字符串拼接生成完整地址,限制两行显示并设置溢出省略。border属性根据选中状态设置不同的边框宽度和颜色。onClick事件处理器在用户点击卡片时触发选择回调。这种实现方式简洁高效,与Flutter版本的视觉效果完全一致。
卡片头部ArkUI实现:
@Builder
CardHeader(address: AddressInfo) {
Row() {
Text(address.name)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text(address.phone)
.fontSize(14)
.fontColor('#666666')
.margin({ left: 12 })
Blank()
if (address.isDefault) {
Text('默认')
.fontSize(10)
.fontColor('#E53935')
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor('#FFF0F0')
.borderRadius(2)
}
}
.width('100%')
}
Row水平排列收货人姓名、电话和默认标签。Blank组件占据剩余空间,将默认标签推到右侧,相当于Flutter中的Spacer。条件渲染使用if语句,只有当isDefault为true时才显示默认标签。样式设置与Flutter版本保持一致,确保跨平台的视觉统一性。
地址编辑表单
class AddressForm extends StatefulWidget {
final Address? address;
final ValueChanged<Address>? onSave;
const AddressForm({
Key? key,
this.address,
this.onSave,
}) : super(key: key);
State<AddressForm> createState() => _AddressFormState();
}
class _AddressFormState extends State<AddressForm> {
final _nameController = TextEditingController();
final _phoneController = TextEditingController();
final _detailController = TextEditingController();
String _selectedRegion = '';
bool _isDefault = false;
void initState() {
super.initState();
if (widget.address != null) {
_nameController.text = widget.address!.name;
_phoneController.text = widget.address!.phone;
_detailController.text = widget.address!.detail;
_selectedRegion = '${widget.address!.province}${widget.address!.city}${widget.address!.district}';
_isDefault = widget.address!.isDefault;
}
}
}
AddressForm组件用于添加和编辑地址。当address参数不为空时,表示编辑模式,需要将现有地址数据填充到表单中。组件使用多个TextEditingController管理输入框的文本内容,_selectedRegion存储选择的省市区,_isDefault存储是否设为默认地址。initState中根据传入的地址数据初始化表单状态,这种设计使同一个组件可以同时支持添加和编辑两种场景。
表单输入项:
Widget _buildFormField({
required String label,
required TextEditingController controller,
String? hintText,
TextInputType? keyboardType,
int maxLines = 1,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF333333),
),
),
const SizedBox(height: 8),
TextField(
controller: controller,
keyboardType: keyboardType,
maxLines: maxLines,
decoration: InputDecoration(
hintText: hintText,
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xFFCCCCCC),
),
contentPadding: const EdgeInsets.all(12),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: const BorderSide(color: Color(0xFFEEEEEE)),
),
),
),
],
),
);
}
_buildFormField是一个可复用的表单项构建方法,接收标签、控制器、提示文字、键盘类型和行数作为参数。Column垂直排列标签和输入框,标签在上方清晰标识输入内容。TextField使用OutlineInputBorder显示边框样式,contentPadding设置内边距使输入区域更加舒适。这种封装方式减少了重复代码,使表单的构建更加简洁。
总结
本文详细介绍了Flutter和OpenHarmony平台上地址管理组件的开发过程。地址管理作为商城应用的基础功能,其设计质量直接影响用户的下单体验。通过地址列表、地址卡片、地址表单等组件的合理设计,我们为用户提供了便捷的地址管理功能。在实际项目中,还可以进一步添加地址智能解析、地图定位选址、地址收藏等高级功能,提升用户体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)