云计算百科
云计算领域专业知识百科平台

Flutter for OpenHarmony 健康管理App应用实战 - 个人中心实现

请添加图片描述

写在前面

个人中心页面是健康管理App的重要组成部分,用户可以在这里设置目标、管理数据、定制主题。和首页的数据展示不同,个人中心更侧重于功能入口和个性化设置。

我们这个页面包含了目标设置、数据同步、主题定制、自动追踪、小组件添加、营养报告等功能模块。每个模块都用卡片形式呈现,视觉上清晰分明。这篇文章会详细讲解如何构建这样一个功能丰富的个人中心页面。


导入依赖

import 'package:flutter/material.dart';
import '../../utils/colors.dart';
import '../../l10n/app_localizations.dart';
import 'weight_goal_page.dart';
import 'nutrition_goal_page.dart';
import 'activity_goal_page.dart';
import 'settings_page.dart';

导入了颜色工具类、国际化模块,以及几个子页面:体重目标、营养目标、活动目标、设置页面。


创建页面

个人中心页面不需要管理状态,用 StatelessWidget:

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

构建主体


Widget build(BuildContext context) {
final colors = context.appColors;
final l10n = context.l10n;
final isDark = Theme.of(context).brightness == Brightness.dark;
final primaryColor = isDark ? AppColors.primaryLight : AppColors.primary;

return SingleChildScrollView(
child: Column(
children: [
_buildHeader(context, l10n, primaryColor),
const SizedBox(height: 16),
_buildGoalsCard(context, colors, l10n),
const SizedBox(height: 16),
_buildSettingsCard(colors, l10n, primaryColor),
const SizedBox(height: 16),
_buildAutoTrackCard(colors, l10n),
const SizedBox(height: 16),
_buildWidgetCard(colors, l10n, primaryColor),
const SizedBox(height: 16),
_buildNutritionReportCard(colors, l10n),
const SizedBox(height: 100),
],
),
);
}

页面由多个卡片组成,用 SingleChildScrollView 包裹让内容可以滚动。底部留100像素的空白,避免被底部导航栏遮挡。

primaryColor 根据深色模式选择不同的主题色,深色模式下用浅色版本,看起来更舒服。


顶部标题栏

Widget _buildHeader(BuildContext context, AppLocalizations l10n, Color primaryColor) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(color: primaryColor),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(Icons.search, color: Colors.white, size: 24),
Text(
l10n.betterMe,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),

标题栏用主题色背景,左边是搜索图标,中间是"Better Me"标题。

GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SettingsPage()),
);
},
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: const Icon(Icons.settings, color: Colors.white, size: 20),
),
),
],
),
);
}

右边是设置按钮,点击跳转到设置页面。按钮用半透明白色圆形背景,和标题栏融为一体。


目标卡片

目标卡片展示三个核心目标入口:体重、营养、活动。

Widget _buildGoalsCard(BuildContext context, AppColorsExtension colors, AppLocalizations l10n) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: colors.cardBackground,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),

卡片有圆角和轻微阴影,看起来有层次感。

child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.myGoals,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colors.textPrimary,
),
),
const SizedBox(height: 20),

顶部是"我的目标"标题。

Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildGoalItem(context, l10n.weight, Icons.fitness_center, colors, onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const WeightGoalPage()));
}),
_buildGoalItem(context, l10n.nutrition, Icons.restaurant, colors, onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const NutritionGoalPage()));
}),
_buildGoalItem(context, l10n.activity, Icons.directions_run, colors, onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const ActivityGoalPage()));
}),
],
),
],
),
);
}

三个目标项横向排列,点击分别跳转到对应的目标设置页面。

目标项组件

Widget _buildGoalItem(BuildContext context, String label, IconData icon, AppColorsExtension colors, {VoidCallback? onTap}) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final primaryColor = isDark ? AppColors.primaryLight : AppColors.primary;

return GestureDetector(
onTap: onTap,
child: Column(
children: [
Container(
width: 70,
height: 70,
decoration: BoxDecoration(
color: colors.inputBackground,
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, size: 36, color: primaryColor),
),
const SizedBox(height: 8),
Text(label, style: TextStyle(fontSize: 14, color: colors.textSecondary)),
],
),
);
}

每个目标项是一个方形图标加文字标签。图标背景用 inputBackground 颜色,深色模式下会自动变深。


设置功能卡片

这个卡片包含备份同步、主题、饮食模式、下载CSV等功能入口:

Widget _buildSettingsCard(AppColorsExtension colors, AppLocalizations l10n, Color primaryColor) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: colors.cardBackground,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),

卡片样式和目标卡片一致。

child: Column(
children: [
_buildSettingItem(
icon: Icons.cloud_sync,
iconColor: primaryColor,
title: l10n.backupSync,
subtitle: l10n.backupSyncDesc,
hasSwitch: true,
switchValue: true,
colors: colors,
primaryColor: primaryColor,
),
_buildDivider(colors),

第一项是备份同步,带开关和描述文字。

_buildSettingItem(
icon: Icons.palette,
iconColor: Colors.purple,
title: l10n.themes,
hasArrow: true,
colors: colors,
primaryColor: primaryColor,
),
_buildDivider(colors),
_buildSettingItem(
icon: Icons.restaurant_menu,
iconColor: Colors.orange,
title: l10n.dietPattern,
hasArrow: true,
colors: colors,
primaryColor: primaryColor,
),
_buildDivider(colors),
_buildSettingItem(
icon: Icons.download,
iconColor: primaryColor,
title: l10n.downloadCsv,
hasArrow: true,
colors: colors,
primaryColor: primaryColor,
),
],
),
);
}

后面三项是主题、饮食模式、下载CSV,都带右箭头表示可以点击进入。

设置项组件

Widget _buildSettingItem({
required IconData icon,
required Color iconColor,
required String title,
String? subtitle,
bool hasSwitch = false,
bool switchValue = false,
bool hasArrow = false,
required AppColorsExtension colors,
required Color primaryColor,
}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Row(
children: [
Icon(icon, color: iconColor, size: 24),
const SizedBox(width: 16),

左边是彩色图标,每个功能用不同颜色区分。

Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: colors.textPrimary)),
if (subtitle != null) ...[
const SizedBox(height: 4),
Text(subtitle, style: TextStyle(fontSize: 12, color: colors.textSecondary)),
],
],
),
),

中间是标题和可选的副标题。

if (hasSwitch) Switch(value: switchValue, onChanged: (value) {}, activeColor: primaryColor),
if (hasArrow) Icon(Icons.chevron_right, color: colors.textSecondary),
],
),
);
}

右边根据参数显示开关或箭头。

分割线

Widget _buildDivider(AppColorsExtension colors) {
return Divider(height: 1, indent: 56, endIndent: 16, color: colors.divider);
}

分割线左边缩进56像素,避开图标区域。


自动追踪卡片

这个卡片展示支持的健康平台:

Widget _buildAutoTrackCard(AppColorsExtension colors, AppLocalizations l10n) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: colors.cardBackground,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.autoTrackSteps, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: colors.textPrimary)),
const SizedBox(height: 8),
Text(l10n.autoTrackDesc, style: TextStyle(fontSize: 13, color: colors.textSecondary)),
const SizedBox(height: 20),

顶部是标题和描述文字。

Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildPlatformItem(Icons.favorite, Colors.red, 'Health', colors),
_buildPlatformItem(Icons.circle, Colors.red.shade800, 'Polar', colors),
_buildPlatformItem(Icons.grid_view, AppColors.primary, 'Garmin', colors),
_buildPlatformItem(Icons.watch, Colors.blue, 'Fitbit', colors),
],
),
],
),
);
}

四个平台图标横向排列:Apple Health、Polar、Garmin、Fitbit。

平台项组件

Widget _buildPlatformItem(IconData icon, Color color, String label, AppColorsExtension colors) {
return Column(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: color, size: 28),
),
const SizedBox(height: 8),
Text(label, style: TextStyle(fontSize: 12, color: colors.textSecondary)),
],
);
}

每个平台用对应的品牌色,图标背景是品牌色的透明版本。


小组件卡片

这个卡片展示可以添加到主屏幕的小组件预览:

Widget _buildWidgetCard(AppColorsExtension colors, AppLocalizations l10n, Color primaryColor) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [primaryColor.withOpacity(0.15), Colors.green.withOpacity(0.1)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
),

这个卡片用渐变背景,从主题色到绿色,看起来更活泼。

child: Column(
children: [
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: primaryColor,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Calowise', style: TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500)),
const SizedBox(height: 12),
const Text('1700', style: TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold)),
Text(l10n.remaining, style: const TextStyle(color: Colors.white70, fontSize: 12)),
],
),
),
),

左边是主小组件预览,显示剩余卡路里。

const SizedBox(width: 12),
Expanded(
child: Column(
children: [
Row(
children: [
_buildMiniWidget(Icons.access_time, colors.inputBackground),
const SizedBox(width: 8),
_buildMiniWidget(Icons.settings, colors.inputBackground),
],
),
const SizedBox(height: 8),
Row(
children: [
_buildMiniWidget(Icons.wb_sunny, Colors.amber.shade100),
const SizedBox(width: 8),
_buildMiniWidget(Icons.notes, Colors.yellow.shade100),
],
),
],
),
),
],
),

右边是四个小型组件预览,2×2排列。

const SizedBox(height: 16),
TextButton.icon(
onPressed: () {},
icon: Icon(Icons.add, color: primaryColor),
label: Text(l10n.addWidgetToHome, style: TextStyle(color: primaryColor, fontWeight: FontWeight.w600)),
),
],
),
);
}

底部是添加按钮。

迷你组件

Widget _buildMiniWidget(IconData icon, Color bgColor) {
return Expanded(
child: Container(
height: 50,
decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(10)),
child: Icon(icon, color: Colors.grey.shade600, size: 24),
),
);
}

迷你组件就是一个带图标的小方块。


营养报告卡片

最后一个卡片是每周营养报告入口:

Widget _buildNutritionReportCard(AppColorsExtension colors, AppLocalizations l10n) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.green.shade50, Colors.teal.shade50],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
),

用绿色系渐变背景,和健康主题呼应。

child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(4)),
child: const Text('NEW', style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
),
const SizedBox(height: 12),
Text(l10n.weeklyNutritionReport, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: colors.textPrimary)),
],
),
),

左边是NEW标签和标题。

Container(
width: 100,
height: 80,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(Icons.analytics, size: 40, color: AppColors.primary),
),
],
),
);
}
}

右边是一个图表图标,暗示这是数据分析功能。


深色模式适配

个人中心页面的深色模式适配主要体现在:

第一,主题色根据模式切换:

final isDark = Theme.of(context).brightness == Brightness.dark;
final primaryColor = isDark ? AppColors.primaryLight : AppColors.primary;

第二,卡片背景使用 colors.cardBackground:

color: colors.cardBackground,

第三,文字颜色使用 colors.textPrimary 和 colors.textSecondary:

style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: colors.textPrimary),

第四,输入框背景使用 colors.inputBackground:

color: colors.inputBackground,

第五,分割线使用 colors.divider:

color: colors.divider,


渐变背景的使用

个人中心页面用了两处渐变背景,让页面更有层次感:

小组件卡片的渐变:

gradient: LinearGradient(
colors: [primaryColor.withOpacity(0.15), Colors.green.withOpacity(0.1)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),

从左上到右下,主题色渐变到绿色。

营养报告卡片的渐变:

gradient: LinearGradient(
colors: [Colors.green.shade50, Colors.teal.shade50],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),

从浅绿到浅青,都是健康相关的颜色。

渐变背景比纯色背景更有设计感,但要注意颜色不能太鲜艳,否则会喧宾夺主。


小结

这篇文章我们实现了个人中心页面,包含了目标设置、功能入口、平台集成、小组件预览、营养报告等模块。

页面采用卡片式布局,每个功能模块独立成卡,视觉上清晰分明。渐变背景和阴影效果让页面更有层次感。

深色模式适配通过 context.appColors 统一处理,代码简洁易维护。


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

赞(0)
未经允许不得转载:网硕互联帮助中心 » Flutter for OpenHarmony 健康管理App应用实战 - 个人中心实现
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!