Flutter client: - Progress dialog: redesigned with pulsing animated icon, info hint about background mode, full-width Minimize button; dismiss signal via ValueNotifier so the dialog always closes regardless of widget lifecycle - Background recognition: when user taps Minimize, wasMinimizedByUser flag is set; on completion a snackbar is shown instead of opening DishResultSheet directly; snackbar action opens the sheet on demand - Fix dialog spinning forever: finally block guarantees dismissSignal=true on all exit paths including early returns from context.mounted checks - Fix DishResultSheet not appearing: add ValueKey to _DailyMealsSection and meal card Padding so Flutter reuses elements when _TodayJobsWidget is inserted/removed from the SliverChildListDelegate list - todayJobsProvider refresh: added refresh() method; called after job submit and on DishJobDone; all ref.read() calls guarded with context.mounted checks - food_search_sheet: scan buttons replaced with full-width stacked OutlinedButtons - app.dart: WidgetsBindingObserver refreshes scan providers on app resume - L10n: added dishRecognitionHint and minimize keys to all 12 locales Backend: - migrations/003: ALTER TYPE recipe_source ADD VALUE 'recommendation' to fix 22P02 error in GET /home/summary -> getRecommendations() - item_enricher: normalizeProductCategory() validates AI-returned category against known slugs, falls back to "other" — fixes products_category_fkey FK violation during receipt recognition - recognition prompt: enumerate valid categories so AI returns correct values Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
55 lines
1.5 KiB
Dart
55 lines
1.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:food_ai/l10n/app_localizations.dart';
|
|
|
|
import 'core/locale/language_provider.dart';
|
|
import 'core/router/app_router.dart';
|
|
import 'core/theme/app_theme.dart';
|
|
import 'features/home/home_provider.dart';
|
|
import 'features/products/product_job_provider.dart';
|
|
|
|
class App extends ConsumerStatefulWidget {
|
|
const App({super.key});
|
|
|
|
@override
|
|
ConsumerState<App> createState() => _AppState();
|
|
}
|
|
|
|
class _AppState extends ConsumerState<App> with WidgetsBindingObserver {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState lifecycleState) {
|
|
if (lifecycleState == AppLifecycleState.resumed) {
|
|
ref.read(recentProductJobsProvider.notifier).refresh();
|
|
ref.read(todayJobsProvider.notifier).refresh();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final router = ref.watch(routerProvider);
|
|
final languageCode = ref.watch(languageProvider);
|
|
|
|
return MaterialApp.router(
|
|
title: 'FoodAI',
|
|
theme: appTheme(),
|
|
routerConfig: router,
|
|
debugShowCheckedModeBanner: false,
|
|
locale: Locale(languageCode),
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
);
|
|
}
|
|
}
|