Files
AI-Health/health_app/lib/pages/remaining_pages.dart

689 lines
28 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/navigation_provider.dart';
import '../providers/data_providers.dart';
/// 饮食记录列表
class DietRecordListPage extends ConsumerWidget {
const DietRecordListPage({super.key});
@override Widget build(BuildContext context, WidgetRef ref) {
final service = ref.watch(dietServiceProvider);
return Scaffold(
appBar: AppBar(title: const Text('饮食记录')),
body: FutureBuilder<List<Map<String, dynamic>>>(
future: service.getRecords(),
builder: (ctx, snap) {
if (snap.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator());
if (!snap.hasData || snap.data!.isEmpty) return _empty(context, '饮食记录', '暂无饮食记录,可通过「拍饮食」录入');
return ListView.builder(
itemCount: snap.data!.length,
itemBuilder: (ctx, i) {
final d = snap.data![i];
final items = (d['foodItems'] as List?)?.cast<Map<String, dynamic>>() ?? [];
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
title: Text('${d['mealType'] ?? ''} ${d['totalCalories'] ?? 0}千卡'),
subtitle: Text(items.map((f) => f['name']).join(' | ')),
trailing: _starWidget(d['healthScore']),
),
);
},
);
},
),
);
}
Widget _starWidget(dynamic score) {
final s = score is int ? score : 3;
return Row(mainAxisSize: MainAxisSize.min, children: List.generate(5, (i) => Icon(Icons.star, size: 16, color: i < s ? const Color(0xFFF9A825) : Colors.grey[300])));
}
}
/// 运动计划页
class ExercisePlanPage extends ConsumerWidget {
const ExercisePlanPage({super.key});
@override Widget build(BuildContext context, WidgetRef ref) {
final plan = ref.watch(currentExercisePlanProvider);
return Scaffold(
appBar: AppBar(title: const Text('运动计划'), centerTitle: true),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => _createDefaultPlan(ref, context),
icon: const Icon(Icons.add),
label: const Text('创建本周计划'),
backgroundColor: const Color(0xFF635BFF),
),
body: plan.when(
data: (data) {
if (data == null || data.isEmpty) return _empty(context, '运动计划', '暂无运动计划,点击右下角创建');
final items = (data['items'] as List?)?.cast<Map<String, dynamic>>() ?? [];
final weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
final completedCount = items.where((i) => i['isCompleted'] == true).length;
final totalCount = items.where((i) => i['isRestDay'] != true).length;
return ListView(children: [
_buildProgressCard(completedCount, totalCount),
const SizedBox(height: 16),
...items.asMap().entries.map((entry) {
final i = entry.key;
final item = entry.value;
final day = item['dayOfWeek'] is int ? item['dayOfWeek'] as int : i;
final isRest = item['isRestDay'] == true;
final isDone = item['isCompleted'] == true;
return _ExercisePlanItem(
day: weekDays[day],
dayIndex: day,
isRest: isRest,
isDone: isDone,
exerciseType: item['exerciseType']?.toString() ?? '',
duration: item['durationMinutes'] is int ? item['durationMinutes'] as int : 0,
onCheckIn: () => _checkIn(ref, item['id'], context),
);
}),
]);
},
loading: () => const Center(child: CircularProgressIndicator(color: Color(0xFF635BFF))),
error: (_, __) => _empty(context, '运动计划', '暂无运动计划,点击右下角创建'),
),
);
}
Widget _buildProgressCard(int completed, int total) {
final progress = total > 0 ? (completed / total * 100).toInt() : 0;
return Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF5F3FF),
borderRadius: BorderRadius.circular(20),
),
child: Column(children: [
const Text('🏃 本周运动进度', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
Row(children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: const Color(0xFF635BFF),
borderRadius: BorderRadius.circular(30),
),
child: Center(
child: Text('$progress%', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white)),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text('已完成 $completed/$total', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
const SizedBox(height: 8),
Container(
height: 8,
decoration: BoxDecoration(color: Colors.grey[200], borderRadius: BorderRadius.circular(4)),
child: FractionallySizedBox(
widthFactor: progress / 100,
child: Container(
decoration: BoxDecoration(color: const Color(0xFF635BFF), borderRadius: BorderRadius.circular(4)),
),
),
),
]),
),
]),
]),
);
}
void _createDefaultPlan(WidgetRef ref, BuildContext context) async {
final service = ref.read(exerciseServiceProvider);
final today = DateTime.now();
final monday = today.subtract(Duration(days: today.weekday - 1));
final items = List.generate(7, (i) => {
'dayOfWeek': i,
'exerciseType': i == 2 || i == 5 ? '休息' : '散步',
'durationMinutes': i == 2 || i == 5 ? 0 : 30,
'isRestDay': i == 2 || i == 5,
});
await service.createPlan({
'weekStartDate': '${monday.year}-${monday.month.toString().padLeft(2, '0')}-${monday.day.toString().padLeft(2, '0')}',
'items': items,
});
ref.invalidate(currentExercisePlanProvider);
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('运动计划已创建 ✅'),
backgroundColor: Color(0xFF635BFF),
));
}
void _checkIn(WidgetRef ref, String itemId, BuildContext context) async {
final service = ref.read(exerciseServiceProvider);
await service.checkIn(itemId);
ref.invalidate(currentExercisePlanProvider);
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('打卡成功 ✅'),
backgroundColor: Color(0xFF43A047),
));
}
}
class _ExercisePlanItem extends StatelessWidget {
final String day;
final int dayIndex;
final bool isRest;
final bool isDone;
final String exerciseType;
final int duration;
final VoidCallback onCheckIn;
const _ExercisePlanItem({
required this.day,
required this.dayIndex,
required this.isRest,
required this.isDone,
required this.exerciseType,
required this.duration,
required this.onCheckIn,
});
@override
Widget build(BuildContext context) {
final today = DateTime.now().weekday - 1;
final isToday = dayIndex == today;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isToday ? const Color(0xFFFEFCE8) : Colors.white,
borderRadius: BorderRadius.circular(16),
border: isToday ? Border.all(color: const Color(0xFFFCD34D), width: 2) : null,
boxShadow: [BoxShadow(color: const Color(0xFF635BFF).withAlpha(8), blurRadius: 4, offset: const Offset(0, 2))],
),
child: Row(children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isDone ? const Color(0xFFDCFCE7) : isRest ? const Color(0xFFF3F4F6) : const Color(0xFFF5F3FF),
borderRadius: BorderRadius.circular(12),
),
child: isDone
? const Icon(Icons.check, size: 20, color: Color(0xFF43A047))
: isRest
? const Icon(Icons.coffee, size: 20, color: Color(0xFF999999))
: const Icon(Icons.directions_run, size: 20, color: Color(0xFF635BFF)),
),
const SizedBox(width: 12),
Expanded(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
Text(day, style: TextStyle(fontSize: 16, fontWeight: isToday ? FontWeight.w600 : FontWeight.w500)),
if (isToday) const SizedBox(width: 4),
if (isToday) const Text('(今天)', style: TextStyle(fontSize: 12, color: Color(0xFFF59E0B))),
]),
const SizedBox(height: 4),
Text(
isRest ? '休息日,好好休息' : '$exerciseType $duration分钟',
style: TextStyle(fontSize: 14, color: Colors.grey[500]),
),
]),
),
if (!isRest && !isDone)
ElevatedButton(
onPressed: onCheckIn,
child: const Text('打卡'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF635BFF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
),
),
if (isDone)
const Text('已完成', style: TextStyle(fontSize: 14, color: Color(0xFF43A047))),
]),
);
}
}
/// 复查列表
class FollowUpListPage extends ConsumerWidget {
const FollowUpListPage({super.key});
@override Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('复查随访'), centerTitle: true),
floatingActionButton: FloatingActionButton(
onPressed: () => _showAddDialog(context),
child: const Icon(Icons.add),
backgroundColor: const Color(0xFF635BFF),
),
body: ListView(children: _mockFollowUps.map((item) => _FollowUpItem(item: item)).toList()),
);
}
void _showAddDialog(BuildContext context) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('添加复查提醒'),
content: Column(mainAxisSize: MainAxisSize.min, children: [
TextField(decoration: const InputDecoration(labelText: '医院名称')),
const SizedBox(height: 12),
TextField(decoration: const InputDecoration(labelText: '科室')),
const SizedBox(height: 12),
TextField(decoration: const InputDecoration(labelText: '日期', hintText: 'YYYY-MM-DD')),
const SizedBox(height: 12),
TextField(decoration: const InputDecoration(labelText: '备注')),
]),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('取消')),
TextButton(
onPressed: () {
Navigator.pop(ctx);
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('复查提醒已添加 ✅'),
backgroundColor: Color(0xFF635BFF),
));
},
child: const Text('保存'),
),
],
),
);
}
}
final _mockFollowUps = [
{'id': '1', 'hospital': '协和医院', 'department': '心内科', 'date': '2025-01-20', 'type': '复诊', 'status': 'upcoming', 'notes': '常规复查,带齐病历'},
{'id': '2', 'hospital': '人民医院', 'department': '骨科', 'date': '2025-01-25', 'type': '复查', 'status': 'upcoming', 'notes': '术后3个月复查'},
{'id': '3', 'hospital': '协和医院', 'department': '心内科', 'date': '2024-12-15', 'type': '复诊', 'status': 'completed', 'notes': '已完成'},
];
class _FollowUpItem extends StatelessWidget {
final Map<String, dynamic> item;
const _FollowUpItem({required this.item});
@override
Widget build(BuildContext context) {
final isCompleted = item['status'] == 'completed';
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [BoxShadow(color: const Color(0xFF635BFF).withAlpha(10), blurRadius: 4, offset: const Offset(0, 2))],
),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: isCompleted ? const Color(0xFFDCFCE7) : const Color(0xFFFEFCE8),
borderRadius: BorderRadius.circular(8),
),
child: Text(
isCompleted ? '已完成' : '待就诊',
style: TextStyle(fontSize: 12, color: isCompleted ? const Color(0xFF43A047) : const Color(0xFFF59E0B)),
),
),
const SizedBox(width: 8),
Text(item['type']?.toString() ?? '', style: TextStyle(fontSize: 14, color: Colors.grey[500])),
]),
const SizedBox(height: 12),
Text(item['hospital']?.toString() ?? '', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
const SizedBox(height: 4),
Text('${item['department']} · ${item['date']}', style: TextStyle(fontSize: 14, color: Colors.grey[500])),
if ((item['notes']?.toString() ?? '').isNotEmpty) ...[
const SizedBox(height: 8),
Text(item['notes']?.toString() ?? '', style: TextStyle(fontSize: 14, color: Colors.grey[600])),
],
]),
);
}
}
/// 健康档案
class HealthArchivePage extends ConsumerWidget {
const HealthArchivePage({super.key});
@override Widget build(BuildContext context, WidgetRef ref) {
final service = ref.watch(userServiceProvider);
return Scaffold(
appBar: AppBar(title: const Text('健康档案')),
body: FutureBuilder<Map<String, dynamic>?>(
future: service.getHealthArchive(),
builder: (ctx, snap) {
if (snap.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator());
final data = snap.data;
if (data == null || data.isEmpty) return _empty(context, '暂无健康档案', '可通过 AI 对话或手动填写');
return ListView(
padding: const EdgeInsets.all(16),
children: [
_Section(title: '基本信息', children: [
_Field('诊断', data['diagnosis']), _Field('手术类型', data['surgeryType']),
_Field('手术日期', data['surgeryDate']),
]),
_Section(title: '病史与限制', children: [
_Field('过敏史', _listStr(data['allergies'])),
_Field('饮食限制', _listStr(data['dietRestrictions'])),
_Field('慢性病史', _listStr(data['chronicDiseases'])),
_Field('家族病史', data['familyHistory']),
]),
],
);
},
),
);
}
String _listStr(dynamic list) => list is List ? list.join('') : '--';
}
class _Section extends StatelessWidget {
final String title; final List<Widget> children;
const _Section({required this.title, required this.children});
@override Widget build(BuildContext context) => Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(padding: const EdgeInsets.only(bottom: 8, top: 16), child: Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)))),
...children,
]);
}
class _Field extends StatelessWidget {
final String label; final String? value;
const _Field(this.label, this.value);
@override Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(width: 80, child: Text('$label', style: const TextStyle(fontSize: 14, color: Color(0xFF666666)))),
Expanded(child: Text(value ?? '--', style: const TextStyle(fontSize: 14, color: Color(0xFF1A1A1A)))),
]),
);
}
/// 编辑资料
class EditProfilePage extends ConsumerStatefulWidget {
const EditProfilePage({super.key});
@override ConsumerState<EditProfilePage> createState() => _EditProfilePageState();
}
class _EditProfilePageState extends ConsumerState<EditProfilePage> {
final _nameCtrl = TextEditingController(); final _genderCtrl = TextEditingController(); final _birthCtrl = TextEditingController();
@override void dispose() { _nameCtrl.dispose(); _genderCtrl.dispose(); _birthCtrl.dispose(); super.dispose(); }
@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _load()); }
void _load() async {
final p = await ref.read(userServiceProvider).getProfile();
if (p != null && mounted) {
setState(() { _nameCtrl.text = p['name'] ?? ''; _genderCtrl.text = p['gender'] ?? ''; _birthCtrl.text = p['birthDate'] ?? ''; });
}
}
Future<void> _save() async {
await ref.read(userServiceProvider).updateProfile(name: _nameCtrl.text, gender: _genderCtrl.text, birthDate: _birthCtrl.text);
if (mounted) Navigator.pop(context);
}
@override Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('编辑资料')),
body: ListView(padding: const EdgeInsets.all(16), children: [
TextField(controller: _nameCtrl, decoration: const InputDecoration(labelText: '姓名')),
const SizedBox(height: 16), TextField(controller: _genderCtrl, decoration: const InputDecoration(labelText: '性别')),
const SizedBox(height: 16), TextField(controller: _birthCtrl, decoration: const InputDecoration(labelText: '出生日期', hintText: 'YYYY-MM-DD')),
const SizedBox(height: 32), SizedBox(width: double.infinity, child: ElevatedButton(onPressed: _save, child: const Text('保存'))),
]),
);
}
/// 健康日历
class HealthCalendarPage extends ConsumerStatefulWidget {
const HealthCalendarPage({super.key});
@override ConsumerState<HealthCalendarPage> createState() => _HealthCalendarPageState();
}
class _HealthCalendarPageState extends ConsumerState<HealthCalendarPage> {
DateTime _currentMonth = DateTime.now();
@override Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('健康日历'), centerTitle: true),
body: Column(children: [
_buildMonthHeader(),
_buildWeekdayHeader(),
_buildCalendarGrid(),
const SizedBox(height: 16),
_buildLegend(),
]),
);
}
Widget _buildMonthHeader() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.chevron_left, size: 32),
onPressed: () => setState(() => _currentMonth = DateTime(_currentMonth.year, _currentMonth.month - 1)),
),
Text(
'${_currentMonth.year}${_currentMonth.month}',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
IconButton(
icon: const Icon(Icons.chevron_right, size: 32),
onPressed: () => setState(() => _currentMonth = DateTime(_currentMonth.year, _currentMonth.month + 1)),
),
],
);
}
Widget _buildWeekdayHeader() {
const weekdays = ['', '', '', '', '', '', ''];
return Row(children: weekdays.map((day) => Expanded(
child: Center(child: Text(day, style: TextStyle(fontSize: 14, color: Colors.grey[500]))),
)).toList());
}
Widget _buildCalendarGrid() {
final firstDay = DateTime(_currentMonth.year, _currentMonth.month, 1);
final lastDay = DateTime(_currentMonth.year, _currentMonth.month + 1, 0);
final daysInMonth = lastDay.day;
final startWeekday = firstDay.weekday % 7;
final days = List.generate(42, (i) {
final dayIndex = i - startWeekday;
if (dayIndex < 0 || dayIndex >= daysInMonth) return null;
return dayIndex + 1;
});
return Expanded(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 7),
itemCount: 42,
itemBuilder: (ctx, i) {
final day = days[i];
if (day == null) return const SizedBox();
return _buildDayCell(day);
},
),
);
}
Widget _buildDayCell(int day) {
final date = DateTime(_currentMonth.year, _currentMonth.month, day);
final today = DateTime.now();
final isToday = date.year == today.year && date.month == today.month && date.day == today.day;
final events = _getEvents(date);
return Container(
decoration: isToday ? BoxDecoration(
color: const Color(0xFF635BFF),
borderRadius: BorderRadius.circular(20),
) : null,
child: Stack(
alignment: Alignment.center,
children: [
Text(
'$day',
style: TextStyle(
fontSize: 16,
color: isToday ? Colors.white : Colors.black,
fontWeight: isToday ? FontWeight.w600 : FontWeight.normal,
),
),
if (events.isNotEmpty)
Positioned(
bottom: 4,
child: Row(children: events.map((type) => Container(
width: 6,
height: 6,
margin: const EdgeInsets.symmetric(horizontal: 1),
decoration: BoxDecoration(
color: _getEventColor(type),
borderRadius: BorderRadius.circular(3),
),
)).toList()),
),
],
),
);
}
List<String> _getEvents(DateTime date) {
final events = <String>[];
if (date.day == 5 || date.day == 12 || date.day == 19 || date.day == 26) events.add('medication');
if (date.day == 8 || date.day == 15 || date.day == 22 || date.day == 29) events.add('exercise');
if (date.day == 20) events.add('followup');
return events;
}
Color _getEventColor(String type) {
switch (type) {
case 'medication': return const Color(0xFF635BFF);
case 'exercise': return const Color(0xFF43A047);
case 'followup': return const Color(0xFFF59E0B);
default: return Colors.grey;
}
}
Widget _buildLegend() {
final items = [
{'color': const Color(0xFF635BFF), 'label': '用药提醒'},
{'color': const Color(0xFF43A047), 'label': '运动计划'},
{'color': const Color(0xFFF59E0B), 'label': '复查随访'},
];
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: items.map((item) => Row(children: [
Container(width: 10, height: 10, decoration: BoxDecoration(color: item['color'] as Color, borderRadius: BorderRadius.circular(5))),
const SizedBox(width: 4),
Text(item['label'] as String, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
const SizedBox(width: 20),
])).toList()),
);
}
}
/// 静态文本页
class StaticTextPage extends ConsumerWidget {
final String type;
const StaticTextPage({super.key, required this.type});
@override Widget build(BuildContext context, WidgetRef ref) {
final titles = {'privacy': '隐私协议', 'terms': '服务协议', 'about': '关于健康管家'};
final contents = {
'privacy': '''## 隐私政策
**更新日期2026年1月1日**
### 一、信息收集
我们收集以下类型的信息:
- **账户信息**:手机号、昵称、头像(您主动提供)
- **健康数据**:血压、心率、血糖、血氧、体重等健康指标记录
- **用药信息**:药品名称、剂量、服药时间等用药计划数据
- **饮食记录**:通过拍照或手动录入的饮食数据
- **设备信息**:设备型号、操作系统版本(用于适配优化)
- **日志信息**App 使用情况、崩溃报告
### 二、信息使用
我们使用您的信息用于以下目的:
- 提供和改进健康管理服务
- AI 健康分析和个性化建议
- 用药提醒和复查通知推送
- App 功能优化和问题修复
### 三、信息保护
- 所有健康数据均采用 HTTPS 加密传输
- 数据存储于安全服务器,采用行业标准的加密措施
- 我们不会向任何第三方出售、出租或共享您的个人健康数据
- 医生仅可查看其签约患者的数据,且需经过您的授权
### 四、信息保留
- 对话记录保留 30 天后自动删除
- 您可以随时删除自己的健康数据和对话记录
- 账号注销后,所有数据将在 7 天内永久删除
### 五、您的权利
- 查看和导出您的个人数据
- 修改不准确的个人信息
- 删除不需要的数据
- 注销账号并清除所有数据
- 关闭推送通知
### 六、联系我们
如有任何关于隐私的问题,请联系:
邮箱privacy@healthbutler.com
电话400-xxx-xxxx''',
'about': '''## 关于健康管家
**版本**v1.0.0 (Build 20260101)
### 产品介绍
健康管家是一款面向心脏术后康复患者的私人 AI 健康管理应用。以对话为核心交互方式,患者可以通过自然语言记录健康数据、获取饮食运动建议、管理用药、解读检查报告。
### 核心功能
- **AI 智能问诊**:基于大语言模型的健康咨询服务
- **健康数据管理**:血压、心率、血糖、血氧、体重的记录与趋势分析
- **智能用药管理**AI 解析处方,自动生成用药计划和提醒
- **饮食识别分析**:拍照即可识别食物种类、估算热量营养素
- **报告智能解读**上传检查报告AI 自动提取指标并预解读
- **运动计划管理**:制定和追踪每日运动目标
- **在线医生问诊**:与签约医生进行远程咨询
### 开发团队
由专业医疗团队与 AI 技术团队联合打造。
### 技术支持
如遇到问题或有建议,请通过以下方式联系我们:
- 在线客服App 内「设置」→「意见反馈」
- 客服热线400-xxx-xxxx工作日 9:00-18:00
### 版权声明
© 2025-2026 健康管家团队。保留所有权利。
本软件受中华人民共和国著作权法保护。''',
};
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(icon: const Icon(Icons.chevron_left), onPressed: () => popRoute(ref)),
title: Text(titles[type] ?? '', style: const TextStyle(color: Color(0xFF1A1A1A), fontWeight: FontWeight.w600)),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Text(contents[type] ?? '内容加载中...', style: const TextStyle(fontSize: 14, height: 1.8, color: Color(0xFF333333))),
),
);
}
}
/// 设备管理(占位)
class DeviceManagementPage extends ConsumerWidget {
const DeviceManagementPage({super.key});
@override Widget build(BuildContext context, WidgetRef ref) => _empty(context, '设备管理', '暂无绑定设备');
}
Widget _empty(BuildContext context, String title, String subtitle) => Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Column(mainAxisSize: MainAxisSize.min, children: [
Icon(Icons.inbox_outlined, size: 64, color: Colors.grey[300]),
const SizedBox(height: 12), Text(subtitle, style: Theme.of(context).textTheme.bodyMedium),
])),
);