fix: diet capture flow - 去掉多余页面,点击直接拍照
- 点击"拍照识别/上传照片"直接打开相机/相册,不再跳转多余选择页 - DietCapturePage 去掉重复的拍照按钮,只保留分析结果展示 - 新增 dietActionProvider 处理拍饮食触发 - 修复 initState reset 导致结果页被清空的 bug - 修复 IP 地址恢复问题
This commit is contained in:
@@ -3,7 +3,7 @@ import 'package:dio/dio.dart';
|
|||||||
import 'local_database.dart';
|
import 'local_database.dart';
|
||||||
|
|
||||||
/// API 基础地址
|
/// API 基础地址
|
||||||
const String baseUrl = 'http://10.4.231.53:5000';
|
const String baseUrl = 'http://192.168.1.45:5000';
|
||||||
|
|
||||||
/// Dio HTTP 客户端封装——带 token 注入、401 自动刷新
|
/// Dio HTTP 客户端封装——带 token 注入、401 自动刷新
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ class DietCapturePage extends ConsumerStatefulWidget {
|
|||||||
class _DietCapturePageState extends ConsumerState<DietCapturePage> {
|
class _DietCapturePageState extends ConsumerState<DietCapturePage> {
|
||||||
@override void initState() {
|
@override void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
Future.microtask(() => ref.read(dietProvider.notifier).reset());
|
// 不 reset — 图片由 home_page 传入,这里只做展示
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -242,74 +242,12 @@ class _DietCapturePageState extends ConsumerState<DietCapturePage> {
|
|||||||
title: const Text('拍饮食'),
|
title: const Text('拍饮食'),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: state.imagePath == null ? _buildCaptureView(context, ref) : _buildResultView(context, ref),
|
body: state.imagePath == null
|
||||||
|
? const Center(child: Text('请从首页拍摄或选择食物照片', style: TextStyle(color: Color(0xFF999999))))
|
||||||
|
: _buildResultView(context, ref),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCaptureView(BuildContext context, WidgetRef ref) {
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 180,
|
|
||||||
height: 180,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF0F2FF),
|
|
||||||
borderRadius: BorderRadius.circular(90),
|
|
||||||
border: Border.all(color: const Color(0xFF8B9CF7), width: 2),
|
|
||||||
),
|
|
||||||
child: const Icon(Icons.camera_alt, size: 48, color: Color(0xFF8B9CF7)),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
const Text('拍摄或上传您的餐食照片', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text('AI将识别食物并分析营养成分', style: TextStyle(fontSize: 14, color: Color(0xFF999999))),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
_captureBtn(context, ref, Icons.camera_alt, '拍照', ImageSource.camera),
|
|
||||||
const SizedBox(width: 24),
|
|
||||||
_captureBtn(context, ref, Icons.photo_library, '相册', ImageSource.gallery),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _captureBtn(BuildContext context, WidgetRef ref, IconData icon, String label, ImageSource source) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 80,
|
|
||||||
height: 80,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFFEFEFF),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(20), blurRadius: 8, offset: const Offset(0, 2))],
|
|
||||||
),
|
|
||||||
child: IconButton(
|
|
||||||
icon: Icon(icon, size: 32, color: const Color(0xFF8B9CF7)),
|
|
||||||
onPressed: () => _pickImage(context, ref, source),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(label, style: const TextStyle(fontSize: 14, color: Color(0xFF666666))),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _pickImage(BuildContext context, WidgetRef ref, ImageSource source) async {
|
|
||||||
final picker = ImagePicker();
|
|
||||||
final picked = await picker.pickImage(source: source, imageQuality: 80, maxWidth: 1024, maxHeight: 1024);
|
|
||||||
if (picked != null) {
|
|
||||||
ref.read(dietProvider.notifier).setImage(picked.path);
|
|
||||||
ref.read(dietProvider.notifier).analyzeImage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─────────── 设计常量(与项目整体色调一致)───────────
|
// ─────────── 设计常量(与项目整体色调一致)───────────
|
||||||
static const _kPrimary = AppTheme.primary; // #6C5CE7
|
static const _kPrimary = AppTheme.primary; // #6C5CE7
|
||||||
static const _kPrimaryLight = AppTheme.primaryLight; // #EDEAFF
|
static const _kPrimaryLight = AppTheme.primaryLight; // #EDEAFF
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'dart:io';
|
|||||||
import '../../providers/auth_provider.dart';
|
import '../../providers/auth_provider.dart';
|
||||||
import '../../providers/chat_provider.dart';
|
import '../../providers/chat_provider.dart';
|
||||||
import '../../providers/data_providers.dart';
|
import '../../providers/data_providers.dart';
|
||||||
|
import '../diet/diet_capture_page.dart';
|
||||||
import '../../core/navigation_provider.dart';
|
import '../../core/navigation_provider.dart';
|
||||||
import '../../widgets/health_drawer.dart';
|
import '../../widgets/health_drawer.dart';
|
||||||
import 'widgets/chat_messages_view.dart';
|
import 'widgets/chat_messages_view.dart';
|
||||||
@@ -53,6 +54,16 @@ class _HomePageState extends ConsumerState<HomePage> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ref.listen(dietActionProvider, (prev, next) {
|
||||||
|
if (next == 'pickFoodCamera') {
|
||||||
|
_pickFoodImage(ImageSource.camera);
|
||||||
|
ref.read(dietActionProvider.notifier).clear();
|
||||||
|
} else if (next == 'pickFoodGallery') {
|
||||||
|
_pickFoodImage(ImageSource.gallery);
|
||||||
|
ref.read(dietActionProvider.notifier).clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
drawer: const HealthDrawer(),
|
drawer: const HealthDrawer(),
|
||||||
backgroundColor: const Color(0xFFF8F9FC),
|
backgroundColor: const Color(0xFFF8F9FC),
|
||||||
@@ -217,6 +228,17 @@ class _HomePageState extends ConsumerState<HomePage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _pickFoodImage(ImageSource source) async {
|
||||||
|
final picker = ImagePicker();
|
||||||
|
final picked = await picker.pickImage(source: source, imageQuality: 80, maxWidth: 1024, maxHeight: 1024);
|
||||||
|
if (picked != null && mounted) {
|
||||||
|
ref.read(dietProvider.notifier).reset();
|
||||||
|
ref.read(dietProvider.notifier).setImage(picked.path);
|
||||||
|
ref.read(dietProvider.notifier).analyzeImage();
|
||||||
|
pushRoute(ref, 'dietCapture');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _showAttachmentPicker(BuildContext context) {
|
void _showAttachmentPicker(BuildContext context) {
|
||||||
showModalBottomSheet(context: context, builder: (ctx) => SafeArea(child: Wrap(children: [
|
showModalBottomSheet(context: context, builder: (ctx) => SafeArea(child: Wrap(children: [
|
||||||
ListTile(leading: const Icon(Icons.camera_alt), title: const Text('拍照'), onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.camera); }),
|
ListTile(leading: const Icon(Icons.camera_alt), title: const Text('拍照'), onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.camera); }),
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ class ChatMessagesView extends ConsumerWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
if (a.label == '服药打卡') {
|
if (a.label == '服药打卡') {
|
||||||
_medicationCheckIn(ref, context);
|
_medicationCheckIn(ref, context);
|
||||||
|
} else if (a.action == 'pickFoodCamera' || a.action == 'pickFoodGallery') {
|
||||||
|
ref.read(dietActionProvider.notifier).trigger(a.action!);
|
||||||
} else if (a.route != null) {
|
} else if (a.route != null) {
|
||||||
if (a.route == 'camera' || a.route == 'gallery') {
|
if (a.route == 'camera' || a.route == 'gallery') {
|
||||||
ref.read(cameraActionProvider.notifier).trigger(a.route!);
|
ref.read(cameraActionProvider.notifier).trigger(a.route!);
|
||||||
@@ -1372,8 +1374,8 @@ final _agentActions = <ActiveAgent, List<_AgentAction>>{
|
|||||||
_AgentAction(label: '查看趋势', icon: Icons.trending_up, isWide: true, route: 'trend'),
|
_AgentAction(label: '查看趋势', icon: Icons.trending_up, isWide: true, route: 'trend'),
|
||||||
],
|
],
|
||||||
ActiveAgent.diet: [
|
ActiveAgent.diet: [
|
||||||
_AgentAction(label: '拍照识别', icon: Icons.camera_alt_outlined, isWide: true, route: 'dietCapture'),
|
_AgentAction(label: '拍照识别', icon: Icons.camera_alt_outlined, isWide: true, action: 'pickFoodCamera'),
|
||||||
_AgentAction(label: '上传照片', icon: Icons.photo_library_outlined, isWide: true, route: 'dietCapture'),
|
_AgentAction(label: '上传照片', icon: Icons.photo_library_outlined, isWide: true, action: 'pickFoodGallery'),
|
||||||
],
|
],
|
||||||
ActiveAgent.medication: [
|
ActiveAgent.medication: [
|
||||||
_AgentAction(label: '用药管理', icon: Icons.medication_liquid_outlined, isWide: true, route: 'medications'),
|
_AgentAction(label: '用药管理', icon: Icons.medication_liquid_outlined, isWide: true, route: 'medications'),
|
||||||
|
|||||||
@@ -124,3 +124,12 @@ class CameraActionNotifier extends Notifier<String?> {
|
|||||||
void trigger(String action) => state = action;
|
void trigger(String action) => state = action;
|
||||||
void clear() => state = null;
|
void clear() => state = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 拍饮食动作触发(不用跳多余页面)
|
||||||
|
final dietActionProvider = NotifierProvider<DietActionNotifier, String?>(DietActionNotifier.new);
|
||||||
|
|
||||||
|
class DietActionNotifier extends Notifier<String?> {
|
||||||
|
@override String? build() => null;
|
||||||
|
void trigger(String action) => state = action;
|
||||||
|
void clear() => state = null;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user