feat: 健康仪表盘配色调整 + 登录页品牌升级 + iOS 配置 + 隐私文案修订 + 文档清理
## UI 配色 - 健康仪表盘: 背景从蓝紫粉三色渐变改为 #4FACFE 纯色蓝, 白字清晰不抢眼 - app_colors / app_design_tokens / app_theme: 配色体系微调 - 多个 widget 和页面跟随配色更新(health_drawer/admin_drawer/doctor_drawer/enterprise_widgets 等) ## 登录页品牌升级 - 新增品牌素材: drawer_background_v2 / login_background_v2 / health_login_character_transparent - login_page 重构, 接入新品牌视觉 - app.dart 启动流程调整 ## iOS / Android 配置 - Info.plist: 权限描述调整 - AppIcon / LaunchImage: 资源更新 - AndroidManifest: 权限微调 ## 隐私文案修订 - privacy.html: 蓝牙设备描述调整为"标准蓝牙血压服务", 移除未上线设备类型表述 - terms.html: 移除"在线医生咨询"相关条款, 服务范围收窄 ## 通知中心 - notification_center_page: 重构通知项布局 ## 其他 - 删除已完成的计划/spec 文档(ui-system-first-pass / apple-sign-in / secondary-page-color-refresh / notification-preferences-design / ui-design-system) - 新增 native_navigation_test - 新增 HANDOFF 交接文档 - home_message_order_test 更新 - .gitignore 调整
This commit is contained in:
@@ -139,7 +139,26 @@ class _RootNavigator extends ConsumerWidget {
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (!didPop) popRoute(ref);
|
||||
},
|
||||
child: buildPage(current, ref),
|
||||
child: Navigator(
|
||||
pages: [
|
||||
for (var index = 0; index < stack.length; index++)
|
||||
MaterialPage<void>(
|
||||
key: ValueKey(_pageKey(stack[index], index)),
|
||||
name: stack[index].name,
|
||||
child: buildPage(stack[index], ref),
|
||||
),
|
||||
],
|
||||
onDidRemovePage: (_) {
|
||||
if (ref.read(routeStackProvider).length > 1) popRoute(ref);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _pageKey(RouteInfo route, int index) {
|
||||
final params = route.params.entries
|
||||
.map((entry) => '${entry.key}=${entry.value}')
|
||||
.join('&');
|
||||
return '$index:${route.name}:$params';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ class AppColors {
|
||||
static const Color meadowAccent = Color(0xFFBAE6FD);
|
||||
static const Color sageAccent = Color(0xFFE9D5FF);
|
||||
|
||||
static const Color health = Color(0xFF34D399);
|
||||
static const Color healthLight = Color(0xFFF0FDF7);
|
||||
static const Color healthBorder = Color(0xFFC8F7E1);
|
||||
static const Color health = Color(0xFF2D9D8F);
|
||||
static const Color healthLight = Color(0xFFECF8F5);
|
||||
static const Color healthBorder = Color(0xFFCBE9E2);
|
||||
static const Color medication = Color(0xFF00B8D9);
|
||||
static const Color medicationLight = Color(0xFFEAFBFF);
|
||||
static const Color medicationBorder = Color(0xFFB8F0FA);
|
||||
static const Color exercise = Color(0xFF4F7DD9);
|
||||
static const Color exerciseLight = Color(0xFFEFF4FF);
|
||||
static const Color exerciseBorder = Color(0xFFD5E2FF);
|
||||
static const Color exercise = Color(0xFF566FD4);
|
||||
static const Color exerciseLight = Color(0xFFF1F3FD);
|
||||
static const Color exerciseBorder = Color(0xFFD7DCF6);
|
||||
static const Color report = Color(0xFF6366F1);
|
||||
static const Color reportLight = Color(0xFFEFFBFF);
|
||||
static const Color reportBorder = Color(0xFFBAE6FD);
|
||||
@@ -51,7 +51,7 @@ class AppColors {
|
||||
static const Color followupLight = Color(0xFFFDF2F8);
|
||||
static const Color followupBorder = Color(0xFFFBCFE8);
|
||||
|
||||
static const Color background = Color(0xFFF7F8FA);
|
||||
static const Color background = Color(0xFFF3F5F8);
|
||||
static const Color backgroundSoft = Color(0xFFFFFFFF);
|
||||
static const Color cardBackground = Color(0xFFFFFFFF);
|
||||
static const Color cardInner = Color(0xFFF1F5F9);
|
||||
@@ -168,7 +168,7 @@ class AppColors {
|
||||
static const LinearGradient healthGradient = LinearGradient(
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [Color(0xFF6EE7B7), Color(0xFF67E8F9)],
|
||||
colors: [Color(0xFF2FAE9B), Color(0xFF63BFAE)],
|
||||
);
|
||||
|
||||
static const LinearGradient medicationGradient = LinearGradient(
|
||||
@@ -180,7 +180,7 @@ class AppColors {
|
||||
static const LinearGradient exerciseGradient = LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFFE0C3FC), Color(0xFF8EC5FC)],
|
||||
colors: [Color(0xFF5B7CDE), Color(0xFF7569D9)],
|
||||
);
|
||||
|
||||
static const LinearGradient reportGradient = LinearGradient(
|
||||
|
||||
@@ -75,13 +75,13 @@ class AppTextStyles {
|
||||
|
||||
static const TextStyle appBarTitle = TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
);
|
||||
|
||||
static const TextStyle summaryTitle = TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.2,
|
||||
);
|
||||
@@ -95,13 +95,13 @@ class AppTextStyles {
|
||||
|
||||
static const TextStyle sectionTitle = TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
);
|
||||
|
||||
static const TextStyle listTitle = TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.22,
|
||||
);
|
||||
@@ -122,7 +122,7 @@ class AppTextStyles {
|
||||
|
||||
static const TextStyle formLabel = TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
);
|
||||
|
||||
@@ -134,19 +134,19 @@ class AppTextStyles {
|
||||
|
||||
static const TextStyle tag = TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
height: 1.15,
|
||||
);
|
||||
|
||||
static const TextStyle button = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
static const TextStyle miniButton = TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
static const TextStyle chatBody = TextStyle(
|
||||
@@ -158,7 +158,7 @@ class AppTextStyles {
|
||||
static const TextStyle chatTitle = TextStyle(
|
||||
fontSize: 20,
|
||||
height: 1.35,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
);
|
||||
|
||||
|
||||
@@ -678,7 +678,7 @@ class AppTheme {
|
||||
surfaceTintColor: Colors.transparent,
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: text,
|
||||
),
|
||||
toolbarHeight: 52,
|
||||
@@ -724,7 +724,7 @@ class AppTheme {
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rLg)),
|
||||
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
|
||||
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
elevation: 0,
|
||||
),
|
||||
),
|
||||
@@ -753,12 +753,12 @@ class AppTheme {
|
||||
textTheme: const TextTheme(
|
||||
headlineLarge: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: text,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: text,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
|
||||
@@ -19,7 +19,7 @@ class LoginPage extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
static const _loginBg = 'assets/branding/login_background_v1.png';
|
||||
static const _loginBg = 'assets/branding/login_background_v2.png';
|
||||
static const _loginActionGradient = AppColors.primaryGradient;
|
||||
|
||||
void _openStaticText(String type) {
|
||||
@@ -220,7 +220,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 21,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
@@ -303,7 +303,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -350,7 +350,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
'选择健康服务医生',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -438,10 +438,12 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
_loginBg,
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.topCenter,
|
||||
child: RepaintBoundary(
|
||||
child: Image.asset(
|
||||
_loginBg,
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
@@ -460,16 +462,13 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedPadding(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
curve: Curves.easeOutCubic,
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.viewInsetsOf(context).bottom,
|
||||
),
|
||||
_LoginKeyboardLift(
|
||||
child: SafeArea(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
keyboardDismissBehavior:
|
||||
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
padding: const EdgeInsets.fromLTRB(20, 22, 20, 24),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
@@ -589,7 +588,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
_isLogin ? '没有账号?去注册' : '已有账号?去登录',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -613,6 +612,23 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginKeyboardLift extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const _LoginKeyboardLift({required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final keyboardHeight = MediaQuery.viewInsetsOf(context).bottom;
|
||||
final lift = (keyboardHeight * 0.42).clamp(0.0, 150.0);
|
||||
return Transform.translate(
|
||||
offset: Offset(0, -lift),
|
||||
transformHitTests: true,
|
||||
child: RepaintBoundary(child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginHero extends StatelessWidget {
|
||||
const _LoginHero();
|
||||
|
||||
@@ -626,7 +642,7 @@ class _LoginHero extends StatelessWidget {
|
||||
'小脉健康',
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
@@ -676,26 +692,11 @@ class _BrandMark extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 108,
|
||||
height: 108,
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.72),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.white.withValues(alpha: 0.86)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.primary.withValues(alpha: 0.10),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/branding/health_icon_master.png',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
width: 116,
|
||||
height: 116,
|
||||
child: Image.asset(
|
||||
'assets/branding/health_login_character_transparent.png',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -981,7 +982,7 @@ class _PrimaryButton extends StatelessWidget {
|
||||
style: const TextStyle(
|
||||
fontSize: 17,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -555,7 +555,7 @@ class _TrendPageState extends ConsumerState<TrendPage> {
|
||||
_displayValue(record),
|
||||
style: const TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.05,
|
||||
),
|
||||
|
||||
@@ -618,7 +618,7 @@ class _DevicesHeader extends StatelessWidget {
|
||||
'已绑定设备',
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -627,7 +627,7 @@ class _DevicesHeader extends StatelessWidget {
|
||||
'$deviceCount 台设备',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
@@ -685,7 +685,7 @@ class _DeviceSection extends StatelessWidget {
|
||||
type.label,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -783,7 +783,7 @@ class _BoundDeviceTile extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -822,7 +822,7 @@ class _BoundDeviceTile extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
height: 1,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.successText,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -399,7 +399,7 @@ class _ScanningEmpty extends StatelessWidget {
|
||||
scanning ? '正在搜索设备' : '暂未发现设备',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -479,7 +479,7 @@ class _DeviceResultTile extends StatelessWidget {
|
||||
type?.label ?? '健康设备',
|
||||
style: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -516,7 +516,7 @@ class _DeviceResultTile extends StatelessWidget {
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
child: Text(connecting ? '连接中' : '连接'),
|
||||
|
||||
@@ -539,7 +539,7 @@ class _DietCapturePageState extends ConsumerState<DietCapturePage> {
|
||||
'识别结果',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -750,7 +750,7 @@ class _DietCapturePageState extends ConsumerState<DietCapturePage> {
|
||||
'AI饮食建议',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class DietCalorieRing extends StatelessWidget {
|
||||
'$calories',
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -341,7 +341,7 @@ class _ExercisePlanGroup extends StatelessWidget {
|
||||
'${_phaseLabel(phase)}(${plans.length})',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class ConversationHistoryPage extends ConsumerWidget {
|
||||
'清空',
|
||||
style: TextStyle(
|
||||
color: AppColors.errorText,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -201,7 +201,7 @@ class _HistoryTile extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:io';
|
||||
import 'dart:async';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
@@ -31,6 +30,26 @@ class _HomePageState extends ConsumerState<HomePage>
|
||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
String? _pickedImagePath;
|
||||
Timer? _notificationTimer;
|
||||
double _conversationDragDistance = 0;
|
||||
|
||||
void _startConversationDrawerDrag(DragStartDetails details) {
|
||||
_conversationDragDistance = 0;
|
||||
}
|
||||
|
||||
void _updateConversationDrawerDrag(DragUpdateDetails details) {
|
||||
_conversationDragDistance += details.delta.dx;
|
||||
if (_conversationDragDistance < 0) {
|
||||
_conversationDragDistance = 0;
|
||||
}
|
||||
if (_conversationDragDistance >= 42) {
|
||||
_conversationDragDistance = 0;
|
||||
_scaffoldKey.currentState?.openDrawer();
|
||||
}
|
||||
}
|
||||
|
||||
void _endConversationDrawerDrag(DragEndDetails details) {
|
||||
_conversationDragDistance = 0;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -134,9 +153,7 @@ class _HomePageState extends ConsumerState<HomePage>
|
||||
key: _scaffoldKey,
|
||||
backgroundColor: const Color(0xFFFFFCFF),
|
||||
drawer: const HealthDrawer(),
|
||||
drawerEnableOpenDragGesture: true,
|
||||
drawerDragStartBehavior: DragStartBehavior.down,
|
||||
drawerEdgeDragWidth: MediaQuery.sizeOf(context).width * 0.8,
|
||||
drawerEnableOpenDragGesture: false,
|
||||
body: DecoratedBox(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
@@ -150,8 +167,15 @@ class _HomePageState extends ConsumerState<HomePage>
|
||||
children: [
|
||||
_buildHeader(user),
|
||||
Expanded(
|
||||
child: RepaintBoundary(
|
||||
child: _HomeMessages(scrollCtrl: _scrollCtrl),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onHorizontalDragStart: _startConversationDrawerDrag,
|
||||
onHorizontalDragUpdate: _updateConversationDrawerDrag,
|
||||
onHorizontalDragEnd: _endConversationDrawerDrag,
|
||||
onHorizontalDragCancel: () => _conversationDragDistance = 0,
|
||||
child: RepaintBoundary(
|
||||
child: _HomeMessages(scrollCtrl: _scrollCtrl),
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildBottomBar(context),
|
||||
@@ -197,7 +221,7 @@ class _HomePageState extends ConsumerState<HomePage>
|
||||
'${_getGreeting()},$name',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -657,7 +681,7 @@ class _HeaderIconButton extends StatelessWidget {
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -231,7 +231,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
info.$2,
|
||||
style: AppTextStyles.chatTitle.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -692,7 +692,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
text: mainValue,
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: abnormal
|
||||
? AppColors.error
|
||||
: AppColors.textPrimary,
|
||||
@@ -886,7 +886,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
'确认录入',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -1416,7 +1416,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
'今日健康',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -1570,8 +1570,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
'健康指标',
|
||||
trailing: trailing,
|
||||
status: 'warning',
|
||||
iconColor: AppColors.warning,
|
||||
iconBg: AppColors.warningLight,
|
||||
iconGradient: AppModuleVisuals.health.gradient,
|
||||
onTap: () => pushRoute(ref, 'trend'),
|
||||
),
|
||||
);
|
||||
@@ -1586,6 +1585,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
status: 'done',
|
||||
iconColor: healthIconColor,
|
||||
iconBg: healthIconBg,
|
||||
iconGradient: AppModuleVisuals.health.gradient,
|
||||
onTap: () => pushRoute(ref, 'trend'),
|
||||
),
|
||||
);
|
||||
@@ -1625,6 +1625,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
: (now.hour >= 18 ? 'overdue' : 'pending'),
|
||||
iconColor: exIconColor,
|
||||
iconBg: exIconBg,
|
||||
iconGradient: AppModuleVisuals.exercise.gradient,
|
||||
onTap: () => pushRoute(ref, 'exercisePlan'),
|
||||
),
|
||||
);
|
||||
@@ -1642,6 +1643,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
status: 'pending',
|
||||
iconColor: exIconColor,
|
||||
iconBg: exIconBg,
|
||||
iconGradient: AppModuleVisuals.exercise.gradient,
|
||||
onTap: () => pushRoute(ref, 'exercisePlan'),
|
||||
),
|
||||
);
|
||||
@@ -1657,6 +1659,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
status: 'overdue',
|
||||
iconColor: exIconColor,
|
||||
iconBg: exIconBg,
|
||||
iconGradient: AppModuleVisuals.exercise.gradient,
|
||||
onTap: () => pushRoute(ref, 'exercisePlan'),
|
||||
),
|
||||
);
|
||||
@@ -1672,6 +1675,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
status: 'pending',
|
||||
iconColor: exIconColor,
|
||||
iconBg: exIconBg,
|
||||
iconGradient: AppModuleVisuals.exercise.gradient,
|
||||
onTap: () => pushRoute(ref, 'exercisePlan'),
|
||||
),
|
||||
);
|
||||
@@ -1690,12 +1694,13 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
tasks.add(
|
||||
_taskRow(
|
||||
context,
|
||||
LucideIcons.pill,
|
||||
AppModuleVisuals.medication.icon,
|
||||
'用药',
|
||||
trailing: meds.isEmpty ? '暂无用药安排' : '暂时不用吃药',
|
||||
status: 'done',
|
||||
iconColor: medIconColor,
|
||||
iconBg: medIconBg,
|
||||
iconGradient: AppModuleVisuals.medication.gradient,
|
||||
onTap: () => pushRoute(ref, 'medCheckIn'),
|
||||
),
|
||||
);
|
||||
@@ -1748,12 +1753,13 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
tasks.add(
|
||||
_taskRow(
|
||||
context,
|
||||
LucideIcons.pill,
|
||||
AppModuleVisuals.medication.icon,
|
||||
title,
|
||||
trailing: trailing,
|
||||
status: status,
|
||||
iconColor: medIconColor,
|
||||
iconBg: medIconBg,
|
||||
iconGradient: AppModuleVisuals.medication.gradient,
|
||||
onTap: () => pushRoute(ref, 'medCheckIn'),
|
||||
),
|
||||
);
|
||||
@@ -1788,7 +1794,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
'今日健康',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.15,
|
||||
),
|
||||
@@ -1835,10 +1841,11 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
VoidCallback? onTap,
|
||||
Color? iconColor,
|
||||
Color? iconBg,
|
||||
LinearGradient? iconGradient,
|
||||
}) {
|
||||
final colors = {
|
||||
'done': AppTheme.success,
|
||||
'warning': AppColors.warning,
|
||||
'warning': const Color(0xFFE8562A),
|
||||
'pending': AppColors.textHint,
|
||||
'overdue': AppTheme.error,
|
||||
};
|
||||
@@ -1852,7 +1859,7 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
final ibg = iconBg ?? AppTheme.primaryLight;
|
||||
final textColor = switch (status) {
|
||||
'overdue' => AppColors.error,
|
||||
'warning' => AppColors.warning,
|
||||
'warning' => const Color(0xFFE8562A),
|
||||
_ => AppColors.textPrimary,
|
||||
};
|
||||
return Padding(
|
||||
@@ -1871,10 +1878,15 @@ class ChatMessagesView extends ConsumerWidget {
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: ibg,
|
||||
color: iconGradient == null ? ibg : null,
|
||||
gradient: iconGradient,
|
||||
borderRadius: AppRadius.smBorder,
|
||||
),
|
||||
child: Icon(icon, size: 18, color: ic),
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 18,
|
||||
color: iconGradient == null ? ic : Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
|
||||
@@ -285,7 +285,7 @@ class _MedicationPhaseGroup extends StatelessWidget {
|
||||
'${_phaseLabel(phase)}(${medications.length})',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -196,7 +196,7 @@ class _NotificationCenterPageState
|
||||
'通知中心',
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -396,7 +396,7 @@ class _UnreadHint extends StatelessWidget {
|
||||
TextSpan(
|
||||
text: '$unreadCount',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.errorText,
|
||||
),
|
||||
),
|
||||
@@ -507,115 +507,118 @@ class _NotificationRow extends StatelessWidget {
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minHeight: 82),
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(14, 10, 10, 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: visual.lightColor,
|
||||
borderRadius: AppRadius.smBorder,
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: showDivider ? 81 : 82),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(14, 10, 10, 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: visual.lightColor,
|
||||
borderRadius: AppRadius.smBorder,
|
||||
),
|
||||
child: Icon(visual.icon, size: 21, color: visual.color),
|
||||
),
|
||||
child: Icon(visual.icon, size: 21, color: visual.color),
|
||||
),
|
||||
const SizedBox(width: 11),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: item.isRead
|
||||
? FontWeight.w700
|
||||
: FontWeight.w800,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.2,
|
||||
const SizedBox(width: 11),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: item.isRead
|
||||
? FontWeight.w600
|
||||
: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: visual.lightColor,
|
||||
borderRadius: AppRadius.pillBorder,
|
||||
),
|
||||
child: Text(
|
||||
visual.label,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: visual.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.message,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
height: 1.2,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: 68,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (item.isRead)
|
||||
const SizedBox(height: 8)
|
||||
else
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: visual.lightColor,
|
||||
borderRadius: AppRadius.pillBorder,
|
||||
),
|
||||
child: Text(
|
||||
visual.label,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: visual.color,
|
||||
),
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.error,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.message,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
height: 1.2,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: 68,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
if (item.isRead)
|
||||
const SizedBox(height: 8)
|
||||
else
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.error,
|
||||
shape: BoxShape.circle,
|
||||
const SizedBox(height: 7),
|
||||
Text(
|
||||
_formatTime(item.createdAt),
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.right,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textHint,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
Text(
|
||||
_formatTime(item.createdAt),
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.right,
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textHint,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showDivider)
|
||||
|
||||
@@ -234,7 +234,7 @@ class _FieldLabel extends StatelessWidget {
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -189,7 +189,7 @@ class _SectionTitle extends StatelessWidget {
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
@@ -266,7 +266,7 @@ class _ActionRow extends StatelessWidget {
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -147,7 +147,7 @@ class _DietRecordListPageState extends ConsumerState<DietRecordListPage> {
|
||||
: '${_selectedDate.month}月${_selectedDate.day}日饮食',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -242,7 +242,7 @@ class _DietRecordListPageState extends ConsumerState<DietRecordListPage> {
|
||||
'${d.day}',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: isSel ? Colors.white : AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -441,7 +441,7 @@ class _DietDaySummary extends StatelessWidget {
|
||||
'${date.month}月${date.day}日',
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -556,7 +556,7 @@ class _DietTrendPanel extends StatelessWidget {
|
||||
'热量趋势',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -803,7 +803,7 @@ class _DietRecordDetailPageState extends ConsumerState<DietRecordDetailPage> {
|
||||
mealNames[d['mealType']?.toString()] ?? '饮食记录',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -1222,7 +1222,7 @@ class _ExercisePlanOverviewCard extends StatelessWidget {
|
||||
'$done/$total 天',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _ExercisePlanPageState._exerciseVisual.color,
|
||||
),
|
||||
),
|
||||
@@ -1375,7 +1375,7 @@ class _ExerciseTodayInlineTask extends StatelessWidget {
|
||||
!hasItem || isRestDay ? '今日无' : (isCompleted ? '撤销' : '打卡'),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1439,7 +1439,7 @@ class _ExerciseCheckInSummary extends StatelessWidget {
|
||||
'${(progress * 100).round()}%',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: _ExercisePlanPageState._exerciseVisual.color,
|
||||
),
|
||||
),
|
||||
@@ -1531,7 +1531,7 @@ class _ExerciseSummaryStat extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -1767,7 +1767,7 @@ class _ExerciseHeroCard extends StatelessWidget {
|
||||
style: const TextStyle(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@@ -1896,7 +1896,7 @@ class _TodayExerciseCard extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -1943,7 +1943,7 @@ class _TodayExerciseCard extends StatelessWidget {
|
||||
isRestDay ? '休息' : (isDone ? '撤销' : '打卡'),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -2062,7 +2062,7 @@ class _ExerciseDayTile extends StatelessWidget {
|
||||
!isToday
|
||||
? '只读'
|
||||
: (isRestDay ? '休息' : (isCompleted ? '撤销' : '打卡')),
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w800),
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -3091,7 +3091,7 @@ class _ArchiveSummary extends StatelessWidget {
|
||||
'$completion%',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -3166,7 +3166,7 @@ class _Section extends StatelessWidget {
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -4375,7 +4375,7 @@ class StaticTextPage extends ConsumerWidget {
|
||||
|
||||
### 一、我们收集的信息
|
||||
|
||||
为向您提供健康管理、AI 健康咨询、报告解读、饮食识别、用药管理、运动计划、在线医生咨询和蓝牙设备同步等功能,我们会根据您使用的具体功能收集以下信息:
|
||||
为向您提供健康管理、AI 健康咨询、报告解读、饮食识别、用药管理、运动计划和蓝牙设备同步等功能,我们会根据您使用的具体功能收集以下信息:
|
||||
|
||||
1. 账号与身份信息
|
||||
- 手机号码:用于注册、登录、身份识别和账号安全验证。
|
||||
@@ -4397,7 +4397,7 @@ class StaticTextPage extends ConsumerWidget {
|
||||
- 未经您主动选择或确认,我们不会读取您的相册其他内容,也不会持续访问您的相机。
|
||||
|
||||
4. 蓝牙设备信息
|
||||
- 当您使用蓝牙设备功能时,我们会扫描和连接附近支持的健康设备。目前代码中已实现血压计数据同步,设备模型中预留了血糖仪、体重秤、血氧仪类型,但当前实际支持的数据解析以血压计为主。
|
||||
- 当您使用蓝牙设备功能时,我们会扫描和连接附近支持的健康设备。目前实际支持以采用标准蓝牙血压服务的血压计为主;其他设备类型将在功能正式开放后另行说明。
|
||||
- 我们会在本地保存已绑定设备的设备标识、设备名称、设备类型、服务 UUID、最近同步时间,以及用于避免重复同步的短期记录指纹。
|
||||
- 通过血压计同步时,我们会读取并记录收缩压、舒张压、脉搏、测量时间等数据。
|
||||
|
||||
@@ -4522,7 +4522,7 @@ class StaticTextPage extends ConsumerWidget {
|
||||
|
||||
### 一、服务内容
|
||||
|
||||
小脉健康为用户提供健康数据记录、健康档案管理、用药管理、饮食识别、运动计划、健康日历、检查报告管理、AI 健康解释、在线医生咨询、随访和蓝牙健康设备同步等功能。具体服务内容会根据产品版本、实际开通情况、监管要求和运营安排进行调整。
|
||||
小脉健康为用户提供健康数据记录、健康档案管理、用药管理、饮食识别、运动计划、健康日历、检查报告管理、AI 健康解释和蓝牙健康设备同步等功能。具体服务内容会根据产品版本、实际开通情况、监管要求和运营安排进行调整。
|
||||
|
||||
本应用当前可能包含以下服务:
|
||||
|
||||
@@ -4532,7 +4532,6 @@ class StaticTextPage extends ConsumerWidget {
|
||||
- 饮食识别:上传或拍摄饮食图片,识别食物种类、估算份量和热量。
|
||||
- AI 健康咨询:根据您输入的文字、图片、PDF 或健康档案,提供健康解释和管理建议。
|
||||
- 用药、运动和日历提醒:帮助您记录计划、查看进度和接收站内提醒。
|
||||
- 在线医生咨询:在开通范围内与医生或相关服务人员进行消息沟通。
|
||||
|
||||
### 二、账号注册与使用
|
||||
|
||||
@@ -4551,7 +4550,6 @@ class StaticTextPage extends ConsumerWidget {
|
||||
- 检查报告解读仅是对报告文字、指标或图片内容的辅助说明,不能替代医生结合病史、体格检查、影像资料和线下检查作出的诊断。
|
||||
- 饮食识别、热量估算、运动建议和健康评分可能存在误差,仅作为参考。
|
||||
- 用药相关内容不应替代医生、药师或说明书建议。请勿根据 App 内容自行开始、停止、更换或调整药物。
|
||||
- 医生咨询、随访和报告审核等服务,应结合线下诊疗意见综合判断。
|
||||
|
||||
如您出现胸痛、胸闷憋气、呼吸困难、意识异常、晕厥、言语不清、一侧肢体无力、严重头痛、严重过敏、血压或血糖明显异常等紧急情况,请立即拨打急救电话或前往医疗机构就诊,不应等待或依赖本应用回复。
|
||||
|
||||
@@ -4684,7 +4682,7 @@ class StaticTextPage extends ConsumerWidget {
|
||||
style: const TextStyle(
|
||||
fontSize: 19,
|
||||
color: AppColors.textPrimary,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
@@ -4709,7 +4707,7 @@ class StaticTextPage extends ConsumerWidget {
|
||||
h1Padding: const EdgeInsets.only(bottom: 18),
|
||||
h1: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.35,
|
||||
letterSpacing: 0,
|
||||
@@ -4717,7 +4715,7 @@ class StaticTextPage extends ConsumerWidget {
|
||||
h2Padding: const EdgeInsets.only(top: 18, bottom: 8),
|
||||
h2: const TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.45,
|
||||
letterSpacing: 0,
|
||||
@@ -4725,7 +4723,7 @@ class StaticTextPage extends ConsumerWidget {
|
||||
h3Padding: const EdgeInsets.only(top: 16, bottom: 6),
|
||||
h3: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.45,
|
||||
letterSpacing: 0,
|
||||
|
||||
@@ -866,7 +866,7 @@ class _ReportListPageState extends ConsumerState<ReportListPage> {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppTextStyles.listTitle.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
|
||||
@@ -465,7 +465,7 @@ class _HealthMetricSheetState extends State<_HealthMetricSheet> {
|
||||
'选择提醒指标',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -114,7 +114,7 @@ class SettingsPage extends ConsumerWidget {
|
||||
minimumSize: const Size.fromHeight(52),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: AppRadius.lgBorder,
|
||||
@@ -134,7 +134,7 @@ class SettingsPage extends ConsumerWidget {
|
||||
minimumSize: const Size.fromHeight(52),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: AppRadius.lgBorder,
|
||||
|
||||
@@ -54,7 +54,7 @@ class AdminDrawer extends ConsumerWidget {
|
||||
'系统管理员',
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -49,17 +49,17 @@ class AiMarkdownView extends StatelessWidget {
|
||||
p: AppTextStyles.chatBody,
|
||||
a: AppTextStyles.chatBody.copyWith(
|
||||
color: AppColors.primary,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: AppColors.primary,
|
||||
),
|
||||
strong: AppTextStyles.chatBody.copyWith(fontWeight: FontWeight.w800),
|
||||
strong: AppTextStyles.chatBody.copyWith(fontWeight: FontWeight.w600),
|
||||
h1: AppTextStyles.chatTitle.copyWith(fontSize: 20),
|
||||
h2: AppTextStyles.chatTitle.copyWith(fontSize: 20),
|
||||
h3: AppTextStyles.chatTitle.copyWith(fontSize: 19),
|
||||
listBullet: AppTextStyles.chatBody.copyWith(
|
||||
fontSize: 21,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
listBulletPadding: const EdgeInsets.only(right: 8),
|
||||
blockquote: AppTextStyles.chatBody.copyWith(
|
||||
|
||||
@@ -84,7 +84,7 @@ class AppGradientOutlineButton extends StatelessWidget {
|
||||
child: AppGradientText(
|
||||
label,
|
||||
gradient: gradient,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w800),
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -133,7 +133,7 @@ class _MetricCard extends StatelessWidget {
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1,
|
||||
),
|
||||
|
||||
@@ -155,7 +155,7 @@ class _DrawerHeader extends StatelessWidget {
|
||||
name,
|
||||
style: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
||||
@@ -135,7 +135,7 @@ class _HeaderStat extends StatelessWidget {
|
||||
style: const TextStyle(
|
||||
color: AppColors.textPrimary,
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
@@ -146,7 +146,7 @@ class _HeaderStat extends StatelessWidget {
|
||||
style: const TextStyle(
|
||||
color: AppColors.textSecondary,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -76,7 +76,7 @@ class _DrawerHero extends StatelessWidget {
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
'assets/branding/drawer_background_v1.png',
|
||||
'assets/branding/drawer_background_v2.png',
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.topLeft,
|
||||
),
|
||||
@@ -88,9 +88,9 @@ class _DrawerHero extends StatelessWidget {
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Colors.white.withValues(alpha: 0.12),
|
||||
Colors.white.withValues(alpha: 0.35),
|
||||
Colors.white.withValues(alpha: 0.82),
|
||||
Colors.white.withValues(alpha: 0.06),
|
||||
Colors.white.withValues(alpha: 0.18),
|
||||
Colors.white.withValues(alpha: 0.78),
|
||||
],
|
||||
stops: const [0.0, 0.58, 1.0],
|
||||
),
|
||||
@@ -169,7 +169,7 @@ class _AccountHeader extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -214,9 +214,9 @@ class _HealthDashboard extends StatelessWidget {
|
||||
),
|
||||
titleColor: Colors.white,
|
||||
backgroundGradient: const LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFF60A5FA), Color(0xFF8B5CF6), Color(0xFFF472B6)],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [Color(0xFF4FACFE), Color(0xFF00F2FE)],
|
||||
),
|
||||
child: latestHealth.when(
|
||||
data: (data) => _DashboardMetrics(data: data, ref: ref),
|
||||
@@ -335,7 +335,7 @@ class _MetricTile extends StatelessWidget {
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
@@ -355,7 +355,7 @@ class _MetricTile extends StatelessWidget {
|
||||
info.label,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
@@ -485,8 +485,8 @@ class _NavTile extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 52,
|
||||
height: 52,
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
@@ -496,13 +496,13 @@ class _NavTile extends StatelessWidget {
|
||||
borderRadius: AppRadius.mdBorder,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _colors.last.withValues(alpha: 0.18),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 5),
|
||||
color: _colors.last.withValues(alpha: 0.10),
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Icon(item.icon, color: Colors.white, size: 26),
|
||||
child: Icon(item.icon, color: Colors.white, size: 24),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
@@ -512,7 +512,7 @@ class _NavTile extends StatelessWidget {
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
height: 1.1,
|
||||
),
|
||||
@@ -548,7 +548,7 @@ class _LightSection extends StatelessWidget {
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
@@ -605,7 +605,7 @@ class _Panel extends StatelessWidget {
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: titleColor,
|
||||
),
|
||||
),
|
||||
@@ -651,7 +651,7 @@ class _TextAction extends StatelessWidget {
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: light ? Colors.white : Color(0xFF6D28D9),
|
||||
),
|
||||
),
|
||||
@@ -829,7 +829,7 @@ class _HistorySectionState extends ConsumerState<_HistorySection> {
|
||||
_expanded ? '收起' : '显示全部',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
@@ -867,7 +867,7 @@ class _HistorySectionState extends ConsumerState<_HistorySection> {
|
||||
),
|
||||
child: Text(
|
||||
_clearing ? '清空中' : '清空全部',
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w800),
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1026,7 +1026,7 @@ class _HistoryActionButton extends StatelessWidget {
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: onTap == null ? AppColors.textHint : color,
|
||||
),
|
||||
),
|
||||
@@ -1080,7 +1080,7 @@ class _DrawerHistoryTile extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: selected
|
||||
? AppColors.textSecondary
|
||||
: AppColors.textPrimary,
|
||||
|
||||
Reference in New Issue
Block a user