在这里插入图片描述

编辑资料页面允许用户修改个人信息,包括姓名、手机号、邮箱和个性签名,部分字段为只读。

依赖导入

导入页面所需的依赖包:

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

Logo

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

更多推荐