在这里插入图片描述

个人中心是每个App都会有的模块,用来展示用户信息和提供各种入口。今天来实现猫咪管家的个人中心页面,包括用户头像、数据统计、功能菜单等内容。

一、页面整体架构

个人中心不需要复杂的状态管理,用 StatelessWidget 就够了:

class ProfileTab extends StatelessWidget {
  const ProfileTab({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('我的'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const SettingsScreen()),
            ),
          ),
        ],
      ),

标题栏右边放了个设置按钮,点击跳转到设置页面。
这是很常见的设计模式,用户一眼就知道在哪里找设置。

页面主体用 SingleChildScrollView 包裹:

body: SingleChildScrollView(
  child: Column(
    children: [
      _buildProfileHeader(context),
      SizedBox(height: 16.h),
      _buildStatisticsCard(context),
      SizedBox(height: 16.h),
      _buildMenuSection(context),
    ],
  ),
),

三个主要区块:头部信息、数据统计、功能菜单。
用 SizedBox 控制间距,比 Padding 更直观。

二、用户头部区域

头部用渐变背景,视觉效果更好:

Widget _buildProfileHeader(BuildContext context) {
  return Container(
    padding: EdgeInsets.all(20.w),
    decoration: BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.topCenter,
        end: Alignment.bottomCenter,
        colors: [Colors.orange, Colors.orange.shade300],
      ),
    ),

LinearGradient 创建从上到下的渐变效果。
橙色系和App主题保持一致,品牌感强。

头像和用户名:

child: Column(
  children: [
    CircleAvatar(
      radius: 45.r,
      backgroundColor: Colors.white,
      child: Icon(Icons.person, size: 50.sp, color: Colors.orange),
    ),
    SizedBox(height: 12.h),
    Text(
      '猫咪管家用户',
      style: TextStyle(
        fontSize: 20.sp,
        fontWeight: FontWeight.bold,
        color: Colors.white,
      ),
    ),

圆形头像用白色背景,和橙色渐变形成对比。
暂时用图标代替真实头像,后续可以加上传功能。

猫咪数量统计:

Consumer<CatProvider>(
  builder: (context, provider, child) {
    return Text(
      '已记录 ${provider.cats.length} 只猫咪',
      style: TextStyle(
        fontSize: 14.sp,
        color: Colors.white.withOpacity(0.9),
      ),
    );
  },
),

用 Consumer 监听数据变化,猫咪数量会实时更新。
文字用 90% 透明度的白色,比纯白柔和一些。

三、数据统计卡片

统计卡片展示关键数据:

Widget _buildStatisticsCard(BuildContext context) {
  return Consumer<CatProvider>(
    builder: (context, provider, child) {
      return Card(
        margin: EdgeInsets.symmetric(horizontal: 16.w),
        child: Padding(
          padding: EdgeInsets.all(16.w),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,

Card 自带圆角和阴影,不用额外设置。
spaceAround 让四个统计项均匀分布。

四个统计项:

children: [
  _buildStatItem('猫咪', '${provider.cats.length}', Icons.pets),
  Container(width: 1, height: 40.h, color: Colors.grey[300]),
  _buildStatItem('日记', '${provider.diaryEntries.length}', Icons.book),
  Container(width: 1, height: 40.h, color: Colors.grey[300]),
  _buildStatItem('喂食', '${provider.feedingRecords.length}', Icons.restaurant),
  Container(width: 1, height: 40.h, color: Colors.grey[300]),
  _buildStatItem('支出', '${provider.expenseRecords.length}', Icons.receipt),
],

用竖线分隔各个统计项,视觉上更清晰。
数据都从 Provider 获取,保证实时性。

单个统计项组件:

Widget _buildStatItem(String label, String value, IconData icon) {
  return Column(
    children: [
      Icon(icon, color: Colors.orange, size: 24.sp),
      SizedBox(height: 4.h),
      Text(
        value,
        style: TextStyle(
          fontSize: 18.sp,
          fontWeight: FontWeight.bold,
        ),
      ),
      Text(
        label,
        style: TextStyle(
          fontSize: 12.sp,
          color: Colors.grey[600],
        ),
      ),
    ],
  );
}

图标在上,数字在中,标签在下,层次分明。
数字加粗突出显示,是用户最关心的信息。

四、功能菜单区域

菜单分组展示:

Widget _buildMenuSection(BuildContext context) {
  return Padding(
    padding: EdgeInsets.symmetric(horizontal: 16.w),
    child: Column(
      children: [
        _buildMenuCard(
          '我的猫咪',
          [
            _MenuItem(
              icon: Icons.pets,
              title: '猫咪管理',
              color: Colors.orange,
              onTap: () => Navigator.push(context, MaterialPageRoute(
                builder: (_) => const CatListScreen(),
              )),
            ),

菜单按功能分组,"我的猫咪"和"设置与帮助"两组。
每个菜单项都有图标、标题、颜色和点击回调。

日记本入口:

_MenuItem(
  icon: Icons.book,
  title: '日记本',
  color: Colors.blue,
  onTap: () => Navigator.push(context, MaterialPageRoute(
    builder: (_) => const DiaryListScreen(),
  )),
),

日记本用蓝色图标,和猫咪管理的橙色区分开。
颜色的选择要有一定的逻辑,不能太随意。

设置与帮助组:

_buildMenuCard(
  '设置与帮助',
  [
    _MenuItem(
      icon: Icons.settings,
      title: '设置',
      color: Colors.grey,
      onTap: () => Navigator.push(context, MaterialPageRoute(
        builder: (_) => const SettingsScreen(),
      )),
    ),
    _MenuItem(
      icon: Icons.info,
      title: '关于我们',
      color: Colors.teal,
      onTap: () => Navigator.push(context, MaterialPageRoute(
        builder: (_) => const AboutScreen(),
      )),
    ),
  ],
),

设置用灰色,表示系统功能。
关于用青色,和其他颜色都不重复。

五、菜单卡片组件

卡片容器:

Widget _buildMenuCard(String title, List<_MenuItem> items) {
  return Card(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: EdgeInsets.fromLTRB(16.w, 12.h, 16.w, 8.h),
          child: Text(
            title,
            style: TextStyle(
              fontSize: 14.sp,
              fontWeight: FontWeight.bold,
              color: Colors.grey[700],
            ),
          ),
        ),

分组标题放在卡片内部顶端。
灰色加粗,不抢菜单项的视觉。

菜单项渲染:

...items.map((item) => ListTile(
  leading: Container(
    padding: EdgeInsets.all(8.w),
    decoration: BoxDecoration(
      color: item.color.withOpacity(0.1),
      borderRadius: BorderRadius.circular(8.r),
    ),
    child: Icon(item.icon, color: item.color, size: 20.sp),
  ),
  title: Text(item.title),
  trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
  onTap: item.onTap,
)),

用展开运算符 ... 把 map 结果展开到 Column 中。
leading 用带背景色的图标,比纯图标好看。

六、菜单项数据类

定义菜单项结构:

class _MenuItem {
  final IconData icon;
  final String title;
  final Color color;
  final VoidCallback onTap;

  _MenuItem({
    required this.icon,
    required this.title,
    required this.color,
    required this.onTap,
  });
}

用私有类封装菜单项数据,只在当前文件使用。
required 确保所有字段都必须传值。

为什么用类而不是 Map:

类有类型检查,IDE 能自动补全。
Map 虽然灵活,但容易写错 key。

七、图标背景的设计

图标背景用主色的 10% 透明度:

Container(
  padding: EdgeInsets.all(8.w),
  decoration: BoxDecoration(
    color: item.color.withOpacity(0.1),
    borderRadius: BorderRadius.circular(8.r),
  ),
  child: Icon(item.icon, color: item.color, size: 20.sp),
),

背景色和图标色是同一个颜色,只是透明度不同。
这种设计让图标更突出,同时不会太刺眼。

圆角的选择:

8.r 的圆角比较柔和,和整体风格一致。
太大会显得圆滚滚,太小又没效果。

八、箭头图标的作用

trailing 放右箭头:

trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),

右箭头暗示这是可点击的,会跳转到新页面。
灰色不抢视觉,但能起到引导作用。

这是 Material Design 的常见模式:

用户看到箭头就知道点击会有反应。
没有箭头的 ListTile 通常是纯展示或开关。

九、Consumer 的使用

为什么用 Consumer 而不是 Provider.of:

Consumer<CatProvider>(
  builder: (context, provider, child) {
    return Text('已记录 ${provider.cats.length} 只猫咪');
  },
),

Consumer 只重建它包裹的部分,性能更好。
Provider.of 会导致整个 build 方法重新执行。

child 参数的作用:

如果有不依赖 provider 的子组件,可以放在 child 里。
这样数据变化时,child 不会重建。

十、渐变背景的实现

LinearGradient 的参数:

gradient: LinearGradient(
  begin: Alignment.topCenter,
  end: Alignment.bottomCenter,
  colors: [Colors.orange, Colors.orange.shade300],
),

begin 和 end 定义渐变方向,这里是从上到下。
colors 数组定义渐变的颜色,可以有多个。

为什么用 shade300:

shade300 比原色浅,形成自然的过渡。
如果两个颜色差太多,渐变会很突兀。

十一、间距的统一

页面中的间距:

SizedBox(height: 16.h),
// 或
padding: EdgeInsets.all(16.w),

统一用 16 作为基础间距单位。
需要更大间距时用 20 或 24,保持倍数关系。

为什么用 .h 和 .w:

这是 ScreenUtil 的扩展方法。
.h 是高度适配,.w 是宽度适配,不同屏幕等比缩放。

十二、页面底部留白

菜单区域最后:

SizedBox(height: 20.h),

底部留一些空白,滚动到底时不会太挤。
这是个小细节,但能提升用户体验。

小结

个人中心页面虽然功能简单,但涉及的知识点不少。渐变背景让头部更有层次,Consumer 实现数据实时更新,菜单分组让功能一目了然。代码上用私有类封装菜单项数据,用展开运算符简化列表渲染。这些都是实际开发中常用的技巧,掌握了能提高开发效率。


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

Logo

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

更多推荐