diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 0578b5b0593987ab6d887474406bfc661cb518ba..6aad7a37b6d900b4aa698e8f335915416c280702 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
tools:replace="android:label"
android:label="is_eat_safe"
android:name="${applicationName}"
- android:icon="@mipmap/ic_launcher">
+ android:icon="@mipmap/launcher_icon">
<activity
diff --git a/assets/fonts/MavenPro-Regular.ttf b/assets/fonts/MavenPro-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..6442c2b226743874a32c0560eab32a46b51d2593
Binary files /dev/null and b/assets/fonts/MavenPro-Regular.ttf differ
diff --git a/assets/fonts/Pixel.ttf b/assets/fonts/Pixel.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..153a450e3e3d07a00fdcf8038e0720609684df3d
Binary files /dev/null and b/assets/fonts/Pixel.ttf differ
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..b58be7152ea2900def52acc0225a20b578c18d1b
Binary files /dev/null and b/icon.png differ
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index a0fcd5423951ecda32e7210f5a2dc4ab565d60f7..82208e8c53f6c61424d6bee314d78deb6b32d01c 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 50;
+ objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
@@ -76,7 +76,6 @@
690E2380A11CE0BD0043536F /* Pods-Runner.release.xcconfig */,
6E2BBF6CE3DB802CFC25AAEF /* Pods-Runner.profile.xcconfig */,
);
- name = Pods;
path = Pods;
sourceTree = "<group>";
};
@@ -340,10 +339,10 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
- SUPPORTED_PLATFORMS = iphoneos;
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -359,7 +358,7 @@
DEVELOPMENT_TEAM = 9FQC3XMQ2Q;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -419,7 +418,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.2;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -468,10 +467,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.2;
MTL_ENABLE_DEBUG_INFO = NO;
+ ONLY_ACTIVE_ARCH = NO;
SDKROOT = iphoneos;
- SUPPORTED_PLATFORMS = iphoneos;
+ SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
@@ -489,7 +489,7 @@
DEVELOPMENT_TEAM = 9FQC3XMQ2Q;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -513,7 +513,7 @@
DEVELOPMENT_TEAM = 9FQC3XMQ2Q;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index c87d15a335208541da7c11961b0f6d5f6035512e..8ee81127a8271940d824af02681be540b153c20c 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1300"
+ LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -40,7 +40,7 @@
</Testables>
</TestAction>
<LaunchAction
- buildConfiguration = "Debug"
+ buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
index dc9ada4725e9b0ddb1deab583e5b5102493aa332..a86228facfd0a952361ffd9113c1f31fceacfa0b 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
index 28c6bf03016f6c994b70f38d1b7346e5831b531f..6a57f89e93b6d8567d82c2ca481a233ce536e7fc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
index 2ccbfd967d9697cd4b83225558af2911e9571c9b..61b7788bc6bbc088c596307a5f5629585bd620a5 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
index f091b6b0bca859a3f474b03065bef75ba58a9e4c..6373de964e212599a8818b2afb9a9d013509b641 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
index 4cde12118dda48d71e01fcb589a74d069c5d7cb5..69c062c7377fb69b28bffae5f38aaeb2fc32f407 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..ca53289010d3a78df0dd7c03f90fb00f744034ba 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
index dcdc2306c28505ebc0b6c3a359c4d252bf626b9f..3e1c620c7a65cdeb29324693e9523d89d9a548dc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
index 2ccbfd967d9697cd4b83225558af2911e9571c9b..61b7788bc6bbc088c596307a5f5629585bd620a5 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..ffafacada143973a8aa763d518d8fa48a9a68093 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
index a6d6b8609df07bf62e5100a53a01510388bd2b22..e36cc4bbb109478cd8428cae762c9343e59d08bc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
index a6d6b8609df07bf62e5100a53a01510388bd2b22..e36cc4bbb109478cd8428cae762c9343e59d08bc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0f22c5c51441e7f9bbc650fb3406bb51a3ca6948 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..e8bf25665f0bcc01c2dcc3ef0ef949636efa486d 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
index 6a84f41e14e27f4b11f16f9ee39279ac98f8d5ac..e179e7e5a3576c41104c3eb38d1b8c9041fc1e15 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index d0e1f58536026aebc4f1f70e481f6993c9ff088d..799c5b1434d8ae8681aa2bc433840defddda9045 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/lib/BackgroundTasks/background_tasks_utils.dart b/lib/BackgroundTasks/background_tasks_utils.dart
index 692674471ed4f9626d4a448bc90ba8fcbc422142..5e040d18e41ab9bde54df93dfcbfbaa9583bd9b6 100644
--- a/lib/BackgroundTasks/background_tasks_utils.dart
+++ b/lib/BackgroundTasks/background_tasks_utils.dart
@@ -1,5 +1,3 @@
-import 'dart:ffi';
-
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:background_fetch/background_fetch.dart';
import 'package:is_eat_safe/Notifications/custom_notifications.dart';
@@ -8,64 +6,54 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../api_utils.dart';
-class BackgroundUtils{
-
+class BackgroundUtils {
@pragma('vm:entry-point')
static void backgroundFetchHeadlessTask(HeadlessTask task) async {
- String taskId = task.taskId;
- bool isTimeout = task.timeout;
- if (isTimeout) {
- // This task has exceeded its allowed running-time.
- // You must stop what you're doing and immediately .finish(taskId)
- print("[BackgroundFetch] Headless task timed-out: $taskId");
+ String taskId = task.taskId;
+ bool isTimeout = task.timeout;
+ if (isTimeout) {
+ BackgroundFetch.finish(taskId);
+ return;
+ }
+ onHeadlessBackgroundFetch();
BackgroundFetch.finish(taskId);
- return;
- }
- print('[BackgroundFetch] Headless event received.');
- onHeadlessBackgroundFetch();
- BackgroundFetch.finish(taskId);
}
- static Future<void> configureBackgroundFetch(SharedPreferences prefs, FlutterLocalNotificationsPlugin fln) async {
- int status = await BackgroundFetch.configure(BackgroundFetchConfig(
- minimumFetchInterval: 15,
- requiredNetworkType: NetworkType.ANY
- ), (String taskId) async { // <-- Event callback.
- // This is the fetch-event callback.
- print("[BackgroundFetch] taskId: $taskId");
+ static Future<void> configureBackgroundFetch(
+ SharedPreferences prefs, FlutterLocalNotificationsPlugin fln) async {
+ await BackgroundFetch.configure(
+ BackgroundFetchConfig(minimumFetchInterval: 15), (String taskId) async {
+ // <-- Event callback.
+ print(" ========= ici =======");
+ onBackgroundFetch(prefs, fln);
+ print("Default fetch task");
- // Use a switch statement to route task-handling.
- switch (taskId) {
- case 'com.transistorsoft.customtask':
- print("Received custom task");
- break;
- default:
- print("Default fetch task");
- }
- onBackgroundFetch(prefs,fln);
// Finish, providing received taskId.
BackgroundFetch.finish(taskId);
- }, (String taskId) async { // <-- Event timeout callback
+ }, (String taskId) async {
+ // <-- Event timeout callback
// This task has exceeded its allowed running-time. You must stop what you're doing and immediately .finish(taskId)
print("[BackgroundFetch] TIMEOUT taskId: $taskId");
BackgroundFetch.finish(taskId);
});
-// Step 2: Schedule a custom "oneshot" task "com.transistorsoft.customtask" to execute 5000ms from now.
- BackgroundFetch.scheduleTask(TaskConfig(
- taskId: "com.transistorsoft.customtask",
+ /*BackgroundFetch.scheduleTask(TaskConfig(
+ taskId: "com.transistorsoft.fetch",
delay: 5000 // <-- milliseconds
- ));
+ ));*/
}
-
- static Future<void> onBackgroundFetch(SharedPreferences prefs, FlutterLocalNotificationsPlugin fln) async{
+ static Future<void> onBackgroundFetch(
+ SharedPreferences prefs, FlutterLocalNotificationsPlugin fln) async {
final List<String> items = prefs.getStringList('watchlist_items') ?? [];
- //CustomNotifications.showBigTextNotification(title: "Danger", body: "Un article de votre liste a été rapplé", fln: fln);
+
for (var e in items) {
WatchlistItem item = await ApiUtils.fetchWLItem(e);
- if(item.isRecalled){
- CustomNotifications.showBigTextNotification(title: "Danger", body: "Un article de votre liste a été rappelé", fln: fln);
+ if (item.isRecalled) {
+ CustomNotifications.showBigTextNotification(
+ title: "Danger",
+ body: "Un article de votre liste a été rappelé",
+ fln: fln);
return;
}
}
@@ -75,7 +63,6 @@ class BackgroundUtils{
final prefs = await SharedPreferences.getInstance();
FlutterLocalNotificationsPlugin fln = FlutterLocalNotificationsPlugin();
CustomNotifications.initialize(fln);
- onBackgroundFetch(prefs,fln);
+ onBackgroundFetch(prefs, fln);
}
-
-}
\ No newline at end of file
+}
diff --git a/lib/Notifications/custom_notifications.dart b/lib/Notifications/custom_notifications.dart
index 3e4525e27a9e93ccc32412d652229d06b9861a1e..c8c24124c21930f19cf519a4412c46b2df741315 100644
--- a/lib/Notifications/custom_notifications.dart
+++ b/lib/Notifications/custom_notifications.dart
@@ -3,7 +3,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class CustomNotifications{
static Future initialize(FlutterLocalNotificationsPlugin fln) async {
- var androidInit = const AndroidInitializationSettings('mipmap/ic_launcher');
+ var androidInit = const AndroidInitializationSettings('mipmap/ic_launcher'); //App icon is automatically replaced due to flutter_launcher_icon plugin
var iosInit = const DarwinInitializationSettings();
var initSettings = InitializationSettings(
android: androidInit,
diff --git a/lib/bloc/watchlist_bloc.dart b/lib/bloc/watchlist_bloc.dart
index af705e9da5f6551ea5e560b8d75736bf0b4f4061..b5c681e4bfa7cb39d3b17421a9fac146d508fad8 100644
--- a/lib/bloc/watchlist_bloc.dart
+++ b/lib/bloc/watchlist_bloc.dart
@@ -33,6 +33,10 @@ class WatchlistBloc extends Bloc<WatchlistEvent, WatchlistState> {
_saveWatchlistToSharedPreferences(tmp);
emit(tmp);
});
+
+ on<CheckForRecalledItems>((event, emit) async{
+ emit(await _updateItemsRecalledState(state));
+ });
}
Future<WatchlistState> _mapToStateOnAdded(
@@ -47,12 +51,12 @@ class WatchlistBloc extends Bloc<WatchlistEvent, WatchlistState> {
}
state as WatchlistFilled;
+ //Cant add same element twice
if (state.wlItems.any((element) => element.id == toBeAddedItemId)) {
return state;
}
return state.copyWith(wlItems: List.of(state.wlItems)..add(it));
- } catch (e) {
- print(e);
+ } catch (_) {
return WatchlistError();
}
}
@@ -67,10 +71,12 @@ class WatchlistBloc extends Bloc<WatchlistEvent, WatchlistState> {
return state.copyWith(wlItems: res);
}
+ //On all items deleted from watchlist
Future<WatchlistState> _getInitialAfterListDeleted() async {
return WatchlistInitial();
}
+ //Saves items to cache, gets them back on app relaunched
Future<void> _saveWatchlistToSharedPreferences(WatchlistState state) async {
if (state is WatchlistInitial) {
await prefs.remove('watchlist_items');
@@ -80,4 +86,19 @@ class WatchlistBloc extends Bloc<WatchlistEvent, WatchlistState> {
await prefs.setStringList(
'watchlist_items', List.of(state.wlItems.map((e) => e.id)));
}
+
+ Future<WatchlistState> _updateItemsRecalledState(WatchlistState state) async {
+ if(state is WatchlistInitial){
+ return state;
+ }
+
+ state as WatchlistFilled;
+ List<WatchlistItem> res = List.from(state.wlItems);
+ for (var element in res) {
+ element.isRecalled = await ApiUtils.isItemRecalled(element.id);
+ }
+
+ return state.copyWith(wlItems: res);
+ }
+
}
diff --git a/lib/bloc/watchlist_event.dart b/lib/bloc/watchlist_event.dart
index 449d3a35ebb4b1948180f342dd8e8fe3bbca5edc..d3d66f932d43d3646325cff272097b0caf0b8f53 100644
--- a/lib/bloc/watchlist_event.dart
+++ b/lib/bloc/watchlist_event.dart
@@ -25,3 +25,6 @@ class AllElementsToBeRemoved extends WatchlistEvent{
class WatchlistDeleted extends WatchlistEvent{
}
+class CheckForRecalledItems extends WatchlistEvent{
+}
+
diff --git a/lib/main.dart b/lib/main.dart
index 41e92db059b41b51eb5b48d84d4968ad89b49c92..3cc01cf779569e2925100f07ae6e5f7cacf385bd 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,4 @@
-import 'dart:async';
import 'dart:io' show Platform;
-import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -13,6 +11,8 @@ import 'package:is_eat_safe/views/detailled_product_view.dart';
import 'package:is_eat_safe/views/rappel_listview.dart';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:is_eat_safe/views/watchlist_view.dart';
+import 'package:is_eat_safe/widgets/popup_product_is_safe.dart';
+import 'package:is_eat_safe/widgets/search_textfield.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:background_fetch/background_fetch.dart';
@@ -26,8 +26,6 @@ late ProduitBloc _produitBloc;
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
-
-
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -45,7 +43,7 @@ void main() async {
runApp( MaterialApp(
title: 'IsEatSafe',
- theme: ThemeData(primarySwatch: Colors.orange),
+ theme: ThemeData(primarySwatch: Colors.orange, fontFamily: 'mavenpro'),
debugShowCheckedModeBanner: false,
home: const MyApp(),));
@@ -78,10 +76,11 @@ class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
+ extendBody: true,
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
appBar: AppBar(
- title: _searchBoolean && _index == 0 ? _searchTextField() : const Text(
- 'IsEatSafe'),
+ title: _searchBoolean && _index == 0 ? SearchTextfield(produitBloc: _produitBloc,) : const Text(
+ 'IsEatSafe', style: TextStyle(fontWeight: FontWeight.bold),),
actions: [
//add
if (_index == 0)
@@ -130,7 +129,11 @@ class _MyAppState extends State<MyApp> {
? FloatingActionButton(
backgroundColor: Colors.black,
onPressed: () async {
+ //Scan to get detailled view of recalled product or dialog saying its not recalled
var result = await BarcodeScanner.scan();
+ if(result.type != ResultType.Barcode || result.format != BarcodeFormat.ean13){
+ return;
+ }
if (await ApiUtils.isItemRecalled(result.rawContent)) {
ApiUtils.getOneRecalledProduct(result.rawContent).then((value) =>
Navigator.push(context, MaterialPageRoute(
@@ -138,7 +141,7 @@ class _MyAppState extends State<MyApp> {
} else {
showDialog(
context: context,
- builder: (_) => _productIsOkDialog(),
+ builder: (_) => const PopUpOk(),
barrierDismissible: true,
);
}
@@ -151,7 +154,11 @@ class _MyAppState extends State<MyApp> {
: FloatingActionButton(
backgroundColor: Colors.black,
onPressed: () async {
+ //Scan to add an item to the watchlist
var result = await BarcodeScanner.scan();
+ if(result.type != ResultType.Barcode || result.format != BarcodeFormat.ean13){
+ return;
+ }
_watchlistBloc.add(ElementToBeAdded(result.rawContent));
},
child: const Icon(
@@ -200,56 +207,5 @@ class _MyAppState extends State<MyApp> {
);
}
-
- //Search bar widget to be hidden or showed if search icon is clicked
- Widget _searchTextField() {
- return TextField(
- onChanged: (String s) {
- //Emits a bloc event to trigger the search/rebuild the listview with desired query
- _produitBloc.add(ProduitSearched(s));
- },
- autofocus: true,
- //Display the keyboard when TextField is displayed
- cursorColor: Colors.white,
- style: const TextStyle(
- color: Colors.white,
- fontSize: 20,
- ),
- textInputAction: TextInputAction.search,
- //Specify the action button on the keyboard
- decoration: const InputDecoration(
- //Style of TextField
- enabledBorder: UnderlineInputBorder(
- //Default TextField border
- borderSide: BorderSide(color: Colors.white)),
- focusedBorder: UnderlineInputBorder(
- //Borders when a TextField is in focus
- borderSide: BorderSide(color: Colors.white)),
- hintText: 'Search', //Text that is displayed when nothing is entered.
- hintStyle: TextStyle(
- //Style of hintText
- color: Colors.white60,
- fontSize: 20,
- ),
- ),
- );
- }
-
-
- Widget _productIsOkDialog() {
- return AlertDialog(
- title: const Text("Produit OK"),
- content: const Text(
- "Selon nos informations ce produit ne fait pas l'objet d'un rappel"),
- actions: <Widget>[
- TextButton(
- child: const Text('Bien reçu!'),
- onPressed: () {
- Navigator.of(context).pop();
- },
- ),
- ],
- );
- }
}
diff --git a/lib/views/detailled_product_view.dart b/lib/views/detailled_product_view.dart
index 3751acbb4417f625c7b5b27dbc5a69a74107dfa8..b39ea703b9dc0cee82a349541e37940b3483eabc 100644
--- a/lib/views/detailled_product_view.dart
+++ b/lib/views/detailled_product_view.dart
@@ -1,11 +1,9 @@
import 'package:flutter/material.dart';
-import 'package:is_eat_safe/api_utils.dart';
+import 'package:is_eat_safe/views/fullscreen_image.dart';
import '../models/produit.dart';
-
class DetailledProductView extends StatelessWidget {
-
final Produit p;
const DetailledProductView(this.p, {super.key});
@@ -13,23 +11,103 @@ class DetailledProductView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
- body: Center(
- child: CustomScrollView(
- slivers: <Widget>[
- SliverAppBar(
- expandedHeight: 350,
- flexibleSpace: FlexibleSpaceBar(
- background: Image.network(p.images),
- ),
- ),
- SliverFixedExtentList(
- itemExtent: 30,
- delegate: SliverChildListDelegate([
- Text(p.nomProduit),
- Text(p.motifRappel),
- Text(p.risques),
- ]),
- )
+ appBar: AppBar(
+ title: const Text("Détails"),
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Column(
+ children: <Widget>[
+ Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: <Widget>[
+ GestureDetector(
+ onTap: () {
+ Navigator.push(context, MaterialPageRoute(builder: (_) {
+ return FullscreenImage(s: p.images.split(' ')[0]);
+ }));
+ },
+ child: Hero(
+ tag: "product image",
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(20),
+ child: Image.network(
+ p.images.split(' ')[0],
+ width: 200,
+ fit: BoxFit.fitWidth,
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ Flexible(
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ p.nomProduit,
+ style: const TextStyle(
+ color: Colors.black,
+ overflow: TextOverflow.clip,
+ fontWeight: FontWeight.bold),
+ ),
+ ),
+ )
+ ],
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(24.0),
+ color: Colors.orangeAccent,
+ ),
+ child: const Padding(
+ padding: EdgeInsets.fromLTRB(10, 2, 10, 4),
+ child: Text(
+ "Raison du rappel :",
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ )),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(25, 0, 25, 0),
+ child: Align(
+ alignment: Alignment.centerLeft, child: Text(p.motifRappel)),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(24.0),
+ color: Colors.orangeAccent,
+ ),
+ child: const Padding(
+ padding: EdgeInsets.fromLTRB(10, 2, 10, 4),
+ child: Text(
+ "Risques encourus :",
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ )),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(25, 0, 25, 0),
+ child: Align(
+ alignment: Alignment.centerLeft, child: Text(p.risques)),
+ ),
],
),
),
diff --git a/lib/views/fullscreen_image.dart b/lib/views/fullscreen_image.dart
new file mode 100644
index 0000000000000000000000000000000000000000..bfc13ed018034ac4d8137ab15efb9cefe8c53e55
--- /dev/null
+++ b/lib/views/fullscreen_image.dart
@@ -0,0 +1,28 @@
+import 'package:flutter/material.dart';
+
+class FullscreenImage extends StatelessWidget {
+ final String s;
+ const FullscreenImage({Key? key, required this.s}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.black,
+ body: GestureDetector(
+ child: Container(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height,
+ child: Hero(
+ tag: 'product image',
+ child: Image.network(
+ s,
+ ),
+ ),
+ ),
+ onTap: () {
+ Navigator.pop(context);
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/views/rappel_listview.dart b/lib/views/rappel_listview.dart
index e095a3d5be5f5e72c11526d359c8cb851f43fb93..0c3a7261852c57ff042417bfa5201604f0c23e0f 100644
--- a/lib/views/rappel_listview.dart
+++ b/lib/views/rappel_listview.dart
@@ -1,8 +1,7 @@
-import 'dart:convert';
-
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:is_eat_safe/widgets/listview_tile.dart';
import '../bloc/produit_bloc.dart';
@@ -32,6 +31,10 @@ class _RappelListViewState extends State<RappelListView> {
});
}
+ Future<void> refresh() async{
+ _produitBloc.add(ProduitRefreshed());
+ }
+
@override
void dispose() {
controller.dispose();
@@ -40,46 +43,50 @@ class _RappelListViewState extends State<RappelListView> {
@override
Widget build(BuildContext context) {
- return BlocBuilder<ProduitBloc, ProduitState>(
- builder: (context, state) {
- if (state is ProduitInitial) {
- return const Center(child: CircularProgressIndicator(),);
- }
-
- if (state is ProduitLoaded) {
- if (state.produits.isEmpty) {
- return const Center(child: Text("Aucun produit correspondant touvé"),);
+ return RefreshIndicator(
+ onRefresh: refresh,
+ child: BlocBuilder<ProduitBloc, ProduitState>(
+ builder: (context, state) {
+ if (state is ProduitInitial) {
+ return const Center(child: CircularProgressIndicator(),);
}
- return ListView.builder(
- controller: controller,
- itemCount: (state.hasReachedMax) ? state.produits.length : state
- .produits.length + 1,
- itemBuilder: (BuildContext context, int index) {
- if (index < state.produits.length) {
- final item = state.produits[index];
+ if (state is ProduitLoaded) {
+ if (state.produits.isEmpty) {
+ return const Center(child: Text("Aucun produit correspondant touvé"),);
+ }
+ return ListView.builder(
+ controller: controller,
+ itemCount: (state.hasReachedMax) ? state.produits.length : state
+ .produits.length + 1,
+ itemBuilder: (BuildContext context, int index) {
- return ListTile(
- leading: Image.network(item.images.split(' ')[0],
- height: 150, width: 100, fit: BoxFit.cover),
- title: Text(item.nomProduit),
- );
- }
- else {
- return const Padding(
- padding: EdgeInsets.symmetric(vertical: 32),
- child: Center(
- child: CircularProgressIndicator(),
- ),
- );
- }
- },
- );
- }
- else {
- return const Center(child: Text("Erreur"),);
- }
- },
+ if (index < state.produits.length) {
+ final item = state.produits[index];
+
+ return ListviewTile(p: item);
+ /*ListTile(
+ leading: Image.network(item.images.split(' ')[0],
+ height: 150, width: 100, fit: BoxFit.cover),
+ title: Text(item.nomProduit),
+ );*/
+ }
+ else {
+ return const Padding(
+ padding: EdgeInsets.symmetric(vertical: 32),
+ child: Center(
+ child: CircularProgressIndicator(),
+ ),
+ );
+ }
+ },
+ );
+ }
+ else {
+ return const Center(child: Text("Erreur"),);
+ }
+ },
+ ),
);
}
}
diff --git a/lib/views/watchlist_view.dart b/lib/views/watchlist_view.dart
index 71411cd9c3863b832bd7454ac5ae3863512bc40d..108bece29eed9ebc0cfa36913f7901facdea4c1c 100644
--- a/lib/views/watchlist_view.dart
+++ b/lib/views/watchlist_view.dart
@@ -3,9 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:is_eat_safe/bloc/watchlist_bloc.dart';
import 'package:collection/collection.dart';
-import 'package:is_eat_safe/models/produit.dart';
-import 'package:is_eat_safe/api_utils.dart';
-import 'package:is_eat_safe/views/detailled_product_view.dart';
+import 'package:is_eat_safe/widgets/watchlist_tile.dart';
class WatchListView extends StatefulWidget {
const WatchListView({Key? key}) : super(key: key);
@@ -19,69 +17,76 @@ class _WatchListViewState extends State<WatchListView> {
@override
void initState() {
+ //WidgetsBinding.instance.addObserver(this);
super.initState();
-
_watchlistBloc = BlocProvider.of(context);
}
- @override
- void dispose() {
- super.dispose();
- }
+
+ Future<void> refresh() async{
+ _watchlistBloc.add(CheckForRecalledItems());
+ }
@override
Widget build(BuildContext context) {
- return BlocBuilder<WatchlistBloc, WatchlistState>(
- builder: (context, state) {
- print("rebuild");
- if (state is WatchlistInitial) {
- return const Center(
- child: Text(
- "To add an item to your watchlist press the \"+\" sign below"),
- );
- }
+ return RefreshIndicator(
+ onRefresh: refresh,
+ child: BlocBuilder<WatchlistBloc, WatchlistState>(
+ builder: (context, state) {
+ if (state is WatchlistInitial) {
+ return const Center(
+ child: Text(
+ "Pour ajouter un produit à cotre liste, cliquez sur le bouton \"+\" si dessous"),
+ );
+ }
- if (state is WatchlistFilled) {
- return Container(
- child: Column(
+ if (state is WatchlistFilled) {
+ return Column(
children: [
- if(state.wlItems.firstWhereOrNull((it) => it.isRecalled == true) != null) const Text("Votre list contient un/des produits rappelés"),
- TextButton(onPressed: (){
- _watchlistBloc.add(WatchlistDeleted());
- },
- child: const Text("Delete all items")),
- ListView.builder(
- physics: const RangeMaintainingScrollPhysics(),
- scrollDirection: Axis.vertical,
- shrinkWrap: true,
- itemCount: state.wlItems.length,
- itemBuilder: (BuildContext context, int index) {
- final item = state.wlItems[index];
+ if(state.wlItems.firstWhereOrNull((it) => it.isRecalled == true) != null)
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Container(
+ decoration: const BoxDecoration(
+ borderRadius: BorderRadius.all(Radius.circular(10)),
+ color: Colors.redAccent
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Row(
+ children: const [
+ Icon(Icons.warning_amber),
+ Text("Votre liste contient un/des produits rappelés",style: TextStyle(fontSize: 18),),
+ ],
+ ),
+ ),
+ ),
+ ),
+ SizedBox(
+ height: 100,
+ width: double.infinity,
+ child: TextButton(onPressed: (){
+ _watchlistBloc.add(WatchlistDeleted());
+ },
+ child: const Text("Delete all items")),
+ ),
+ Flexible(
+ child: ListView.builder(
+ itemCount: state.wlItems.length,
+ itemBuilder: (BuildContext context, int index) {
+ final item = state.wlItems[index];
- return ListTile(
- onTap: () => {
- if(item.isRecalled){
- ApiUtils.getOneRecalledProduct(item.id).then((value) => Navigator.push(context, MaterialPageRoute(builder: (context) => DetailledProductView(value))))
- }
- },
- tileColor: item.isRecalled ? Colors.redAccent : Colors.white,
- leading: Image.network(item.image),
- title: Text(item.nomProduit),
- trailing: IconButton(
- icon: const Icon(Icons.delete_forever),
- onPressed: () {
- _watchlistBloc.add(ElementToBeDeleted(item));
- }),
- );
- }),
+ return WatchlistTile(item: item, watchlistBloc: _watchlistBloc);
+ }),
+ ),
],
- ),
- );
- } else {
- return const Center(
- child: Text("erreur"),
- );
- }
- });
+ );
+ } else {
+ return const Center(
+ child: Text("erreur"),
+ );
+ }
+ }),
+ );
}
}
diff --git a/lib/widgets/listview_tile.dart b/lib/widgets/listview_tile.dart
new file mode 100644
index 0000000000000000000000000000000000000000..70b0a7c6515296bfe47a9873a4e2ca889daaf32d
--- /dev/null
+++ b/lib/widgets/listview_tile.dart
@@ -0,0 +1,62 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:is_eat_safe/models/produit.dart';
+
+import '../views/detailled_product_view.dart';
+
+class ListviewTile extends StatelessWidget {
+ final Produit p;
+
+ final double imageWidth = 100;
+ final double imageHeight = 100;
+ final BoxFit imageFitting = BoxFit.cover;
+
+ const ListviewTile({Key? key, required this.p}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: () => {
+ Navigator.push(context, CupertinoPageRoute(builder: (context) => DetailledProductView(p)))
+ },
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(8, 10, 8, 0),
+ child: Container(
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [Color.fromRGBO(248, 244, 10, 0.30980392156862746), Color.fromRGBO(
+ 255, 0, 0, 0.30196078431372547)]
+ ),
+ borderRadius: BorderRadius.all(Radius.circular(10))
+ ),
+ child: Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(8),
+ child: Image.network(p.images.split(' ')[0], width: imageWidth, height: imageHeight, fit: imageFitting,
+ //errorBuilder: (context, exception, stackTrace) {return Image.network("https://developers.google.com/static/maps/documentation/streetview/images/error-image-generic.png", width: imageWidth, height: imageHeight, fit: imageFitting,);},
+ ),
+ ),
+ ),
+ Expanded(
+ child: Text(
+ p.nomProduit,
+ overflow: TextOverflow.ellipsis,
+ style: const TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.w600,
+ color: Colors.black,
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/popup_product_is_safe.dart b/lib/widgets/popup_product_is_safe.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4eb074d5444630f93c1dddbd5a76bec2473d2999
--- /dev/null
+++ b/lib/widgets/popup_product_is_safe.dart
@@ -0,0 +1,29 @@
+import 'package:flutter/material.dart';
+
+class PopUpOk extends StatelessWidget {
+ const PopUpOk({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return AlertDialog(
+ title: const Text("Produit OK"),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: const [
+ Text(
+ "Selon nos informations ce produit ne fait pas l'objet d'un rappel"),
+ SizedBox(height: 20,),
+ Icon(Icons.check_circle, color: Colors.green,size: 40,),
+ ],
+ ),
+ actions: <Widget>[
+ TextButton(
+ child: const Text('Bien reçu!'),
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/widgets/search_textfield.dart b/lib/widgets/search_textfield.dart
new file mode 100644
index 0000000000000000000000000000000000000000..0144fb13d47ebd61220043bde5087c77a1db721e
--- /dev/null
+++ b/lib/widgets/search_textfield.dart
@@ -0,0 +1,41 @@
+import 'package:flutter/material.dart';
+import 'package:is_eat_safe/bloc/produit_bloc.dart';
+
+class SearchTextfield extends StatelessWidget {
+ final ProduitBloc produitBloc;
+ const SearchTextfield({Key? key, required this.produitBloc}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return TextField(
+ onChanged: (String s) {
+ //Emits a bloc event to trigger the search/rebuild the listview with desired query
+ produitBloc.add(ProduitSearched(s));
+ },
+ autofocus: true,
+ //Display the keyboard when TextField is displayed
+ cursorColor: Colors.white,
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 20,
+ ),
+ textInputAction: TextInputAction.search,
+ //Specify the action button on the keyboard
+ decoration: const InputDecoration(
+ //Style of TextField
+ enabledBorder: UnderlineInputBorder(
+ //Default TextField border
+ borderSide: BorderSide(color: Colors.white)),
+ focusedBorder: UnderlineInputBorder(
+ //Borders when a TextField is in focus
+ borderSide: BorderSide(color: Colors.white)),
+ hintText: 'Search', //Text that is displayed when nothing is entered.
+ hintStyle: TextStyle(
+ //Style of hintText
+ color: Colors.white60,
+ fontSize: 20,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/watchlist_tile.dart b/lib/widgets/watchlist_tile.dart
new file mode 100644
index 0000000000000000000000000000000000000000..52ba3c63e005cbbcaa22794ee8494b5acc3e0187
--- /dev/null
+++ b/lib/widgets/watchlist_tile.dart
@@ -0,0 +1,71 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:is_eat_safe/bloc/watchlist_bloc.dart';
+import 'package:is_eat_safe/models/watchlist_item.dart';
+
+import '../api_utils.dart';
+import '../views/detailled_product_view.dart';
+
+class WatchlistTile extends StatelessWidget {
+
+ final WatchlistItem item;
+ final WatchlistBloc watchlistBloc;
+
+ final double imageWidth = 75;
+ final double imageHeight = 75;
+ final BoxFit imageFitting = BoxFit.fitHeight;
+
+ const WatchlistTile({Key? key, required this.item, required this.watchlistBloc}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: () => {
+ if(item.isRecalled){
+ ApiUtils.getOneRecalledProduct(item.id).then((value) => Navigator.push(context, MaterialPageRoute(builder: (context) => DetailledProductView(value))))
+ }
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: item.isRecalled ? const [Color.fromRGBO(248, 10, 10, 0.30980392156862746), Color.fromRGBO(
+ 255, 255, 255, 0.4745098039215686)]
+ : const [Color.fromRGBO(38, 246, 6, 0.30980392156862746), Color.fromRGBO(
+ 255, 255, 255, 0.4745098039215686)]
+ ),
+ ),
+ child: Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(8),
+ child: Image.network(item.image, width: imageWidth, height: imageHeight, fit: imageFitting,
+ //errorBuilder: (context, exception, stackTrace) {return Image.network("https://developers.google.com/static/maps/documentation/streetview/images/error-image-generic.png", width: imageWidth, height: imageHeight, fit: imageFitting,);},
+ ),
+ ),
+ ),
+ Expanded(
+ child: Text(
+ item.nomProduit,
+ overflow: TextOverflow.ellipsis,
+ style: const TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.w600,
+ color: Colors.black,
+ ),
+ ),
+ ),
+ IconButton(
+ icon: const Icon(Icons.delete_forever),
+ onPressed: () {
+ watchlistBloc.add(ElementToBeDeleted(item));
+ }),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
index 36768f21d23f9b1c9d27e1fff66f4dbbf8c55670..7101b0f6e06346dcff50216a431d180e75f17080 100644
--- a/macos/Podfile.lock
+++ b/macos/Podfile.lock
@@ -1,19 +1,25 @@
PODS:
+ - flutter_local_notifications (0.0.1):
+ - FlutterMacOS
- FlutterMacOS (1.0.0)
- shared_preferences_macos (0.0.1):
- FlutterMacOS
DEPENDENCIES:
+ - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
EXTERNAL SOURCES:
+ flutter_local_notifications:
+ :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
FlutterMacOS:
:path: Flutter/ephemeral
shared_preferences_macos:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
SPEC CHECKSUMS:
+ flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
diff --git a/pubspec.lock b/pubspec.lock
index 1ec893ac42f2397e28fa559148a2232c66399dfe..07686b14e1a60b8778f5edecf577fc8dc1cb3117 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,13 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.3.5"
args:
dependency: transitive
description:
@@ -50,6 +57,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ cli_util:
+ dependency: transitive
+ description:
+ name: cli_util
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.5"
clock:
dependency: transitive
description:
@@ -64,6 +85,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.1"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.2"
cupertino_icons:
dependency: "direct main"
description:
@@ -160,6 +195,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.1"
+ flutter_launcher_icons:
+ dependency: "direct dev"
+ description:
+ name: flutter_launcher_icons
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.11.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -219,6 +261,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
+ image:
+ dependency: transitive
+ description:
+ name: image
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.2.2"
js:
dependency: transitive
description:
@@ -226,6 +275,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.7.0"
lints:
dependency: transitive
description:
@@ -310,6 +366,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
+ pointycastle:
+ dependency: transitive
+ description:
+ name: pointycastle
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.6.2"
process:
dependency: transitive
description:
@@ -476,6 +539,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.0"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.1"
sdks:
dart: ">=2.18.1 <3.0.0"
flutter: ">=3.0.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 778f8f84c35f5cca9489895371344a586ff06e96..d3345c19db20543b3eb1fc7844e5537b627185c9 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -55,6 +55,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
+ flutter_launcher_icons: "^0.11.0"
+
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
@@ -63,10 +65,21 @@ dev_dependencies:
# rules and activating additional ones.
flutter_lints: ^2.0.0
+flutter_icons:
+ android: "launcher_icon"
+ ios: true
+ image_path: "icon.png"
+ min_sdk_android: 21 # android min sdk min:16, default 21
+ remove_alpha_ios: true
+
+
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
+
+
+
flutter:
# The following line ensures that the Material Icons font is
@@ -90,10 +103,10 @@ flutter:
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
- # fonts:
- # - family: Schyler
- # fonts:
- # - asset: fonts/Schyler-Regular.ttf
+ fonts:
+ - family: mavenpro
+ fonts:
+ - asset: assets/fonts/MavenPro-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro