Flutter for OpenHarmony 社团管理App实战 - 编辑资料实现
本文介绍了Flutter编辑资料页面的实现,主要包括:1) 使用StatefulWidget管理用户输入状态;2) 初始化并释放TextEditingController;3) 页面布局包含头像区域和表单字段;4) 区分可编辑字段(姓名、手机号等)和只读字段(学号、学院等);5) 提供保存按钮提交修改。页面采用Material设计风格,通过Provider进行状态管理,实现用户个人信息的展示与编辑

编辑资料页面允许用户修改个人信息,包括姓名、手机号、邮箱和个性签名,部分字段为只读。
依赖导入
导入页面所需的依赖包:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../providers/app_provider.dart';
Material提供基础UI组件。
Provider用于获取和更新用户数据。
页面需要状态管理控制输入框。
页面类定义
定义编辑资料页面类:
class EditProfilePage extends StatefulWidget {
const EditProfilePage({super.key});
State<EditProfilePage> createState() => _EditProfilePageState();
}
StatefulWidget管理输入框状态。
输入框需要TextEditingController。
页面有可变状态需要StatefulWidget。
状态初始化
初始化输入框控制器:
class _EditProfilePageState extends State<EditProfilePage> {
late TextEditingController _nameController;
late TextEditingController _phoneController;
late TextEditingController _emailController;
late TextEditingController _signatureController;
void initState() {
super.initState();
final user = Provider.of<AppProvider>(
context,
listen: false
).currentUser;
_nameController = TextEditingController(text: user.name);
_phoneController = TextEditingController(text: user.phone);
_emailController = TextEditingController(text: user.email);
_signatureController = TextEditingController(text: user.signature);
}
late关键字延迟初始化。
initState中获取用户数据。
控制器初始值为当前用户信息。
资源释放
释放控制器资源:
void dispose() {
_nameController.dispose();
_phoneController.dispose();
_emailController.dispose();
_signatureController.dispose();
super.dispose();
}
dispose中释放所有控制器。
避免内存泄漏。
良好的资源管理习惯。
页面框架搭建
构建页面基本结构:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('编辑资料'),
actions: [
TextButton(
onPressed: _saveProfile,
child: const Text(
'保存',
style: TextStyle(color: Colors.white)
),
),
],
),
AppBar右侧添加保存按钮。
白色文字与AppBar背景对比。
点击保存调用_saveProfile方法。
用户头像区域
显示用户头像:
body: Consumer<AppProvider>(
builder: (context, provider, _) {
final user = provider.currentUser;
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Center(
child: Stack(
children: [
CircleAvatar(
radius: 50,
backgroundColor: const Color(0xFF4A90E2)
.withOpacity(0.1),
child: Text(
user.name[0],
style: const TextStyle(
fontSize: 40,
color: Color(0xFF4A90E2),
fontWeight: FontWeight.bold
)
),
),
大号头像居中显示。
用户名首字作为头像内容。
蓝色主题色保持统一。
相机图标
添加相机图标:
Positioned(
bottom: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Color(0xFF4A90E2),
shape: BoxShape.circle
),
child: const Icon(
Icons.camera_alt,
color: Colors.white,
size: 20
),
),
),
],
),
),
Positioned定位在右下角。
蓝色圆形背景包裹相机图标。
提示用户可以更换头像。
可编辑字段
添加可编辑的输入框:
const SizedBox(height: 32),
_buildTextField(
'姓名',
_nameController,
Icons.person
),
const SizedBox(height: 16),
姓名字段可以编辑。
图标与字段语义对应。
统一的间距布局。
只读字段
添加只读字段:
_buildReadOnlyField(
'学号',
user.studentId,
Icons.badge
),
const SizedBox(height: 16),
_buildReadOnlyField(
'学院',
user.college,
Icons.school
),
const SizedBox(height: 16),
_buildReadOnlyField(
'专业',
user.major,
Icons.book
),
学号、学院、专业不可编辑。
灰色背景表示只读状态。
这些信息由系统管理。
更多可编辑字段
添加手机号、邮箱和签名:
const SizedBox(height: 16),
_buildTextField(
'手机号',
_phoneController,
Icons.phone
),
const SizedBox(height: 16),
_buildTextField(
'邮箱',
_emailController,
Icons.email
),
const SizedBox(height: 16),
_buildTextField(
'个性签名',
_signatureController,
Icons.edit,
maxLines: 3
),
],
),
);
},
),
);
}
手机号和邮箱可以编辑。
个性签名支持多行输入。
maxLines参数控制行数。
可编辑输入框组件
实现可编辑输入框组件:
Widget _buildTextField(
String label,
TextEditingController controller,
IconData icon,
{int maxLines = 1}
) {
return TextField(
controller: controller,
maxLines: maxLines,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(
icon,
color: const Color(0xFF4A90E2)
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8)
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFF4A90E2)),
),
),
);
}
蓝色图标作为前缀。
圆角边框更美观。
聚焦时边框变蓝色。
只读输入框组件
实现只读输入框组件:
Widget _buildReadOnlyField(
String label,
String value,
IconData icon
) {
return TextField(
controller: TextEditingController(text: value),
readOnly: true,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(icon, color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8)
),
filled: true,
fillColor: Colors.grey[100],
),
);
}
readOnly设为true禁止编辑。
灰色图标表示不可编辑。
灰色背景区分只读状态。
保存方法
实现保存方法:
void _saveProfile() {
final provider = Provider.of<AppProvider>(
context,
listen: false
);
provider.updateUser(provider.currentUser.copyWith(
name: _nameController.text,
phone: _phoneController.text,
email: _emailController.text,
signature: _signatureController.text,
));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('保存成功'))
);
Navigator.pop(context);
}
}
copyWith创建更新后的用户对象。
调用provider方法更新数据。
SnackBar提示保存成功。
完整代码整合
整合后的完整页面代码:
class EditProfilePage extends StatefulWidget {
const EditProfilePage({super.key});
State<EditProfilePage> createState() => _EditProfilePageState();
}
class _EditProfilePageState extends State<EditProfilePage> {
late TextEditingController _nameController;
late TextEditingController _phoneController;
late TextEditingController _emailController;
late TextEditingController _signatureController;
void initState() {
super.initState();
final user = Provider.of<AppProvider>(context, listen: false).currentUser;
_nameController = TextEditingController(text: user.name);
_phoneController = TextEditingController(text: user.phone);
_emailController = TextEditingController(text: user.email);
_signatureController = TextEditingController(text: user.signature);
}
void dispose() {
_nameController.dispose();
_phoneController.dispose();
_emailController.dispose();
_signatureController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('编辑资料'),
actions: [
TextButton(
onPressed: _saveProfile,
child: const Text('保存',
style: TextStyle(color: Colors.white)),
),
],
),
body: Consumer<AppProvider>(
builder: (context, provider, _) {
final user = provider.currentUser;
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Center(
child: Stack(
children: [
CircleAvatar(
radius: 50,
backgroundColor: const Color(0xFF4A90E2).withOpacity(0.1),
child: Text(user.name[0],
style: const TextStyle(
fontSize: 40,
color: Color(0xFF4A90E2),
fontWeight: FontWeight.bold)),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Color(0xFF4A90E2),
shape: BoxShape.circle),
child: const Icon(Icons.camera_alt,
color: Colors.white, size: 20),
),
),
],
),
),
const SizedBox(height: 32),
_buildTextField('姓名', _nameController, Icons.person),
const SizedBox(height: 16),
_buildReadOnlyField('学号', user.studentId, Icons.badge),
const SizedBox(height: 16),
_buildReadOnlyField('学院', user.college, Icons.school),
const SizedBox(height: 16),
_buildReadOnlyField('专业', user.major, Icons.book),
const SizedBox(height: 16),
_buildTextField('手机号', _phoneController, Icons.phone),
const SizedBox(height: 16),
_buildTextField('邮箱', _emailController, Icons.email),
const SizedBox(height: 16),
_buildTextField('个性签名', _signatureController,
Icons.edit, maxLines: 3),
],
),
);
},
),
);
}
Widget _buildTextField(String label, TextEditingController controller,
IconData icon, {int maxLines = 1}) {
return TextField(
controller: controller,
maxLines: maxLines,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(icon, color: const Color(0xFF4A90E2)),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFF4A90E2)),
),
),
);
}
Widget _buildReadOnlyField(String label, String value, IconData icon) {
return TextField(
controller: TextEditingController(text: value),
readOnly: true,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(icon, color: Colors.grey),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
filled: true,
fillColor: Colors.grey[100],
),
);
}
void _saveProfile() {
final provider = Provider.of<AppProvider>(context, listen: false);
provider.updateUser(provider.currentUser.copyWith(
name: _nameController.text,
phone: _phoneController.text,
email: _emailController.text,
signature: _signatureController.text,
));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('保存成功')));
Navigator.pop(context);
}
}
代码结构清晰,功能完整。
可编辑和只读字段区分明显。
保存功能完整可用。
输入验证
添加输入验证逻辑:
String? _validatePhone(String? value) {
if (value == null || value.isEmpty) {
return '请输入手机号';
}
if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(value)) {
return '请输入正确的手机号';
}
return null;
}
String? _validateEmail(String? value) {
if (value == null || value.isEmpty) {
return '请输入邮箱';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return '请输入正确的邮箱格式';
}
return null;
}
手机号验证确保格式正确。
邮箱验证使用正则表达式。
返回null表示验证通过。
表单验证集成
将验证集成到保存方法:
void _saveProfile() {
// 验证手机号
final phoneError = _validatePhone(_phoneController.text);
if (phoneError != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(phoneError))
);
return;
}
// 验证邮箱
final emailError = _validateEmail(_emailController.text);
if (emailError != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(emailError))
);
return;
}
final provider = Provider.of<AppProvider>(context, listen: false);
provider.updateUser(provider.currentUser.copyWith(
name: _nameController.text,
phone: _phoneController.text,
email: _emailController.text,
signature: _signatureController.text,
));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('保存成功'))
);
Navigator.pop(context);
}
保存前先验证所有输入。
验证失败显示错误提示。
验证通过才执行保存操作。
小结
编辑资料页面允许用户修改姓名、手机号、邮箱和个性签名,学号、学院、专业为只读字段。可编辑字段用蓝色图标,只读字段用灰色图标和背景。顶部显示用户头像和相机图标,AppBar右侧有保存按钮。
页面使用TextEditingController管理输入框状态,在initState中初始化为当前用户信息,在dispose中释放资源。可编辑输入框支持聚焦时边框变色,只读输入框使用灰色背景区分。保存时会验证手机号和邮箱格式,验证通过后更新用户数据并返回上一页。整个页面交互流畅,用户体验良好。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)