From f0d575e2f36f4becc7d7e73f3319f169308dbd8b Mon Sep 17 00:00:00 2001 From: Frederik Benoist Date: Mon, 15 May 2023 05:37:01 +0200 Subject: [PATCH] refactor: Global evolution 15/05/2023 --- assets/images/no_connection.png | Bin 0 -> 42559 bytes ios/Podfile.lock | 10 ++ lib/db/box_visit_photo.dart | 11 +- lib/main.dart | 3 - lib/network/api_provider.dart | 1 + lib/objectbox-model.json | 10 +- lib/objectbox.dart | 95 +++++------- lib/objectbox.g.dart | 21 ++- lib/service/shared_prefs.dart | 15 +- lib/ui/home.dart | 3 +- lib/ui/home/tab_home.dart | 129 +++++++++++++--- lib/ui/reusable/reusable_widget.dart | 5 - lib/ui/splash_screen.dart | 7 +- lib/ui/sync/check_connection.dart | 125 +++++++++++++++ lib/ui/sync/sync_calendar.dart | 2 +- lib/ui/sync/upload_photos.dart | 1 - lib/ui/visit/tab_visit.dart | 142 ++++++++++++------ lib/ui/visit/visit_photo_tag.dart | 2 - lib/ui/visit/visit_photo_typology_detail.dart | 67 +++++---- lib/ui/visit/visit_photo_typology_list.dart | 70 +++++++-- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 36 ++++- pubspec.yaml | 7 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 25 files changed, 560 insertions(+), 208 deletions(-) create mode 100644 assets/images/no_connection.png create mode 100644 lib/ui/sync/check_connection.dart diff --git a/assets/images/no_connection.png b/assets/images/no_connection.png new file mode 100644 index 0000000000000000000000000000000000000000..8777448056026979b4e4add5835980dad950bee4 GIT binary patch literal 42559 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 bBJlqKL2)R}tWcjN00000NkvXXu0mjfv5+ln literal 0 HcmV?d00001 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5d23c25..d2a0085 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,6 +1,9 @@ PODS: - camera_avfoundation (0.0.1): - Flutter + - connectivity_plus (0.0.1): + - Flutter + - ReachabilitySwift - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) @@ -23,6 +26,7 @@ PODS: - FlutterMacOS - permission_handler_apple (9.0.4): - Flutter + - ReachabilitySwift (5.0.0) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -37,6 +41,7 @@ PODS: DEPENDENCIES: - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) @@ -54,11 +59,14 @@ SPEC REPOS: trunk: - FMDB - ObjectBox + - ReachabilitySwift - Toast EXTERNAL SOURCES: camera_avfoundation: :path: ".symlinks/plugins/camera_avfoundation/ios" + connectivity_plus: + :path: ".symlinks/plugins/connectivity_plus/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: @@ -86,6 +94,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: camera_avfoundation: 3125e8cd1a4387f6f31c6c63abb8a55892a9eeeb + connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 @@ -96,6 +105,7 @@ SPEC CHECKSUMS: package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 diff --git a/lib/db/box_visit_photo.dart b/lib/db/box_visit_photo.dart index 1db3e66..b633f22 100644 --- a/lib/db/box_visit_photo.dart +++ b/lib/db/box_visit_photo.dart @@ -20,6 +20,7 @@ class VisitPhoto { int photo_principale; String tags; int id_concurrence_lien; + bool depuis_galerie; VisitPhoto( {this.id = 0, @@ -31,7 +32,8 @@ class VisitPhoto { this.photo_principale = 0, this.tags = '', DateTime? date_photo, - this.id_concurrence_lien = 0}) + this.id_concurrence_lien = 0, + this.depuis_galerie = false}) : date_photo = date_photo ?? DateTime.now(); static String? _photosDir = SharedPrefs().photosDir; @@ -40,7 +42,10 @@ class VisitPhoto { if (_photosDir == null) { throw Exception('Photos directory not initialized'); } - return '$_photosDir/$image_name'; + if (depuis_galerie == false) { + return '$_photosDir/$image_name'; + } else + return image_name; } String get dateFormat => DateFormat('dd.MM.yyyy hh:mm:ss').format(date_photo); @@ -57,6 +62,7 @@ class VisitPhoto { int? photo_principale, String? tags, int? id_concurrence_lien, + bool? depuis_galerie, }) { return VisitPhoto( id: id ?? this.id, @@ -69,6 +75,7 @@ class VisitPhoto { photo_principale: photo_principale ?? this.photo_principale, tags: tags ?? this.tags, id_concurrence_lien: id_concurrence_lien ?? this.id_concurrence_lien, + depuis_galerie: depuis_galerie ?? this.depuis_galerie, ); } /* diff --git a/lib/main.dart b/lib/main.dart index 5988045..340183c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -43,9 +43,6 @@ Future main() async { objectbox = await ObjectBox.create(); - int objectboxAddress = identityHashCode(objectbox); - print(objectboxAddress); - /// Log objectbox.addLog('LOG', 'MOBDR', 'Ouverture application ', 0); diff --git a/lib/network/api_provider.dart b/lib/network/api_provider.dart index 604cfc6..2dcf0ea 100644 --- a/lib/network/api_provider.dart +++ b/lib/network/api_provider.dart @@ -183,6 +183,7 @@ class ApiProvider { String base64Photo = base64.encode(ReponseImg.data); /// create box user + objectbox.userBox.removeAll(); objectbox.addUSer( response.data['id_utilisateur'], response.data['login'], diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json index 4086c7b..b7700fe 100644 --- a/lib/objectbox-model.json +++ b/lib/objectbox-model.json @@ -224,7 +224,7 @@ }, { "id": "13:6298506278273268036", - "lastPropertyId": "11:7594245284938827569", + "lastPropertyId": "13:3286310216758176958", "name": "VisitPhoto", "properties": [ { @@ -277,6 +277,11 @@ "id": "11:7594245284938827569", "name": "id_concurrence_lien", "type": 6 + }, + { + "id": "13:3286310216758176958", + "name": "depuis_galerie", + "type": 1 } ], "relations": [] @@ -463,7 +468,8 @@ 102253757473665009, 1526411175344533047, 1603887098520719919, - 427077651567855068 + 427077651567855068, + 7039119413270734559 ], "retiredRelationUids": [], "version": 1 diff --git a/lib/objectbox.dart b/lib/objectbox.dart index dc79fcb..34aef1b 100644 --- a/lib/objectbox.dart +++ b/lib/objectbox.dart @@ -54,27 +54,6 @@ class ObjectBox { PhotoTypologyBox = Box(store); visitPhotoBox = Box(store); logBox = Box(store); - - // Add some demo data if the box is empty. - if (noteBox.isEmpty()) { - _putDemoData(); - } - - userBox.removeAll(); - - /* - etabBox.removeAll(); - etabCompetitorBox.removeAll(); - visitBox.removeAll(); - visitTagBox.removeAll(); - visitPhotoBox.removeAll(); - PhotoTypologyBox.removeAll(); - */ - - // Add some demo data if the box is empty. - if (userBox.isEmpty()) { - _putUserAdminData(); - } } /// Create an instance of ObjectBox to use throughout the app. @@ -84,19 +63,9 @@ class ObjectBox { return ObjectBox._create(store); } - void _putDemoData() { - final demoNotes = [ - Note('Quickly add a note by writing text and pressing Enter'), - Note('Delete notes by tapping on one'), - Note('Write a demo app for ObjectBox') - ]; - store.runInTransactionAsync(TxMode.write, _putNotesInTx, demoNotes); - } - - void _putUserAdminData() { - //addUSer(0, 'root', 'admim', 'admin', ''); - } - + /** + * TODO: A SUPPRIMER + Box note + */ Stream> getNotes() { // Query for all notes, sorted by their date. // https://docs.objectbox.io/queries @@ -134,20 +103,14 @@ class ObjectBox { /// USER --------------------------------------------------------------------- /// - Future addUSer(int _id_utilisateur, String _login, String _nom, - String _prenom, String _photo) => - store.runInTransactionAsync( - TxMode.write, - _addUserInTx, - User( - id_utilisateur: _id_utilisateur, - login: _login, - nom: _nom, - prenom: _prenom, - photo: _photo)); - - static void _addUserInTx(Store store, _User) { - store.box().put(_User); + void addUSer(int _id_utilisateur, String _login, String _nom, String _prenom, + String _photo) { + userBox.put(User( + id_utilisateur: _id_utilisateur, + login: _login, + nom: _nom, + prenom: _prenom, + photo: _photo)); } String getUserAvatar(int id_utilisateur) { @@ -241,9 +204,23 @@ class ObjectBox { .query(EtabCompetitor_.id_etab.equals(_id_etab)) .order(EtabCompetitor_.nom) .build(); - final photoCompetitors = query.find(); + final etabCompetitors = query.find(); - return photoCompetitors.toList(); + return etabCompetitors.toList(); + } + + /// Retrieves a etab competitor object from the ObjectBox database with the specified ID. + /// + /// Parameters: + /// _id_concurrence_lien : The _id_concurrence_lien of the competitor to retrieve. + /// + /// Returns: + /// A Etab Competitor object, or null if no object is found. + EtabCompetitor? getEtabCompetitorByLink(int _id_concurrence_lien) { + final query = etabCompetitorBox + .query(EtabCompetitor_.id_concurrence_lien.equals(_id_concurrence_lien)) + .build(); + return query.findFirst(); } int getEtabCompetitorsCount() { @@ -598,42 +575,42 @@ class ObjectBox { } } - Future putPhotoTypologie(int photoId, int typologieId) async { + void putPhotoTypologie(int photoId, int typologieId) { final photo = visitPhotoBox.get(photoId); if (photo != null) { final updatedPhoto = photo.copyWith(id_photo_typologie: typologieId); - await visitPhotoBox.putAsync(updatedPhoto); + visitPhotoBox.put(updatedPhoto); } } - Future putPhotoTags(int photoId, List tags) async { + void putPhotoTags(int photoId, List tags) { final photo = visitPhotoBox.get(photoId); if (photo != null) { final updatedPhoto = photo.copyWith(tags: tags.join(",")); - await visitPhotoBox.putAsync(updatedPhoto); + visitPhotoBox.put(updatedPhoto); } } - Future putPhotoVisibilities( - int photoId, List visibilities) async { + void putPhotoVisibilities(int photoId, List visibilities) { final photo = visitPhotoBox.get(photoId); if (photo != null) { final updatedPhoto = photo.copyWith( photo_principale: visibilities.contains('principal') ? 1 : 0, photo_privee: visibilities.contains('private') ? 1 : 0); - await visitPhotoBox.putAsync(updatedPhoto); + + visitPhotoBox.put(updatedPhoto); } } - Future putPhotoCompetitor(int photoId, int competitorId) async { + void putPhotoCompetitor(int photoId, int competitorId) { final photo = visitPhotoBox.get(photoId); if (photo != null) { final updatedPhoto = photo.copyWith(id_concurrence_lien: competitorId); - await visitPhotoBox.putAsync(updatedPhoto); + visitPhotoBox.put(updatedPhoto); } } diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart index 19c1604..2234bd5 100644 --- a/lib/objectbox.g.dart +++ b/lib/objectbox.g.dart @@ -249,7 +249,7 @@ final _entities = [ ModelEntity( id: const IdUid(13, 6298506278273268036), name: 'VisitPhoto', - lastPropertyId: const IdUid(11, 7594245284938827569), + lastPropertyId: const IdUid(13, 3286310216758176958), flags: 0, properties: [ ModelProperty( @@ -301,6 +301,11 @@ final _entities = [ id: const IdUid(11, 7594245284938827569), name: 'id_concurrence_lien', type: 6, + flags: 0), + ModelProperty( + id: const IdUid(13, 3286310216758176958), + name: 'depuis_galerie', + type: 1, flags: 0) ], relations: [], @@ -504,7 +509,8 @@ ModelDefinition getObjectBoxModel() { 102253757473665009, 1526411175344533047, 1603887098520719919, - 427077651567855068 + 427077651567855068, + 7039119413270734559 ], retiredRelationUids: const [], modelVersion: 5, @@ -761,7 +767,7 @@ ModelDefinition getObjectBoxModel() { objectToFB: (VisitPhoto object, fb.Builder fbb) { final image_nameOffset = fbb.writeString(object.image_name); final tagsOffset = fbb.writeString(object.tags); - fbb.startTable(12); + fbb.startTable(14); fbb.addInt64(0, object.id); fbb.addInt64(1, object.id_visite); fbb.addInt64(2, object.id_photo_typologie); @@ -772,6 +778,7 @@ ModelDefinition getObjectBoxModel() { fbb.addInt64(7, object.photo_principale); fbb.addOffset(8, tagsOffset); fbb.addInt64(10, object.id_concurrence_lien); + fbb.addBool(12, object.depuis_galerie); fbb.finish(fbb.endTable()); return object.id; }, @@ -798,7 +805,9 @@ ModelDefinition getObjectBoxModel() { date_photo: DateTime.fromMillisecondsSinceEpoch( const fb.Int64Reader().vTableGet(buffer, rootOffset, 12, 0)), id_concurrence_lien: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0)); + const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0), + depuis_galerie: const fb.BoolReader() + .vTableGet(buffer, rootOffset, 28, false)); return object; }), @@ -1081,6 +1090,10 @@ class VisitPhoto_ { /// see [VisitPhoto.id_concurrence_lien] static final id_concurrence_lien = QueryIntegerProperty(_entities[6].properties[9]); + + /// see [VisitPhoto.depuis_galerie] + static final depuis_galerie = + QueryBooleanProperty(_entities[6].properties[10]); } /// [Visit] entity fields to define ObjectBox queries. diff --git a/lib/service/shared_prefs.dart b/lib/service/shared_prefs.dart index ba5e86a..6befde2 100644 --- a/lib/service/shared_prefs.dart +++ b/lib/service/shared_prefs.dart @@ -89,18 +89,11 @@ class SharedPrefs { _sharedPrefs.setString('key_photo', value); } - /// get/set id_distrib - int get id_distrib => _sharedPrefs.getInt('key_id_distrib') ?? 0; + /// get/set last id_visite + int get last_id_visite => _sharedPrefs.getInt('key_last_id_visite') ?? 0; - set id_distrib(int value) { - _sharedPrefs.setInt('key_id_distrib', value); - } - - /// get/set id_visite - int get id_visite => _sharedPrefs.getInt('key_id_visite') ?? 0; - - set id_visite(int value) { - _sharedPrefs.setInt('key_id_visite', value); + set last_id_visite(int value) { + _sharedPrefs.setInt('key_last_id_visite', value); } /// get/set isSimulator diff --git a/lib/ui/home.dart b/lib/ui/home.dart index 7d1704f..b7a46cd 100644 --- a/lib/ui/home.dart +++ b/lib/ui/home.dart @@ -50,7 +50,6 @@ class _HomePageState extends State } void _handleTabSelection() { - // TODO a voir si on laisse setState ?? /* setState(() { }); @@ -99,7 +98,7 @@ class _HomePageState extends State icon: Icon(Icons.home, color: _currentIndex == 0 ? PRIMARY_COLOR : CHARCOAL)), BottomNavigationBarItem( - label: 'Visits', + label: 'Synchro', icon: Icon(Icons.sync, color: _currentIndex == 1 ? ASSENT_COLOR : CHARCOAL)), BottomNavigationBarItem( diff --git a/lib/ui/home/tab_home.dart b/lib/ui/home/tab_home.dart index ac3356a..183a35e 100644 --- a/lib/ui/home/tab_home.dart +++ b/lib/ui/home/tab_home.dart @@ -6,6 +6,7 @@ we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navba import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:badges/badges.dart' as badges; @@ -43,13 +44,13 @@ class _TabHomePageState extends State String defaultLang = 'en'; - late LanguageCubit _languageCubit; + DateTime _todayDate = DateTime.now(); - bool _isLoading = true; - String _errorMessage = ''; + late LanguageCubit _languageCubit; late List todayVisitData = []; late List previousVisitData = []; + VisitModel? lastVisitData = null; @override void initState() { @@ -65,21 +66,22 @@ class _TabHomePageState extends State loadData().then((_) { setState(() { - _isLoading = false; - // Listen particular event subVisitPhotoCountEvent = eventBus.on().listen((e) { + SharedPrefs().last_id_visite = e.id_visite; setState(() { for (int i = 0; i < todayVisitData.length; i++) { if (todayVisitData[i].id_visite == e.id_visite) { todayVisitData[i].photoCount = e.photoCount; + lastVisitData = todayVisitData[i]; break; } } for (int i = 0; i < previousVisitData.length; i++) { if (previousVisitData[i].id_visite == e.id_visite) { previousVisitData[i].photoCount = e.photoCount; + lastVisitData = previousVisitData[i]; break; } } @@ -114,9 +116,9 @@ class _TabHomePageState extends State // if we used AutomaticKeepAliveClientMixin, we must call super.build(context); super.build(context); final double boxImageSize = (MediaQuery.of(context).size.width / 3); - if (_isLoading) { - return Center(child: CircularProgressIndicator()); - } + final formattedDate = + DateFormat('EEEE d MMMM y', 'fr_FR').format(_todayDate); + return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, @@ -149,22 +151,93 @@ class _TabHomePageState extends State body: ListView( physics: AlwaysScrollableScrollPhysics(), children: [ + Padding( + padding: EdgeInsets.all(16.0), + child: Row( + children: [ + Icon(Icons.calendar_today), + SizedBox(width: 8.0), + Text( + formattedDate, + style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold), + ), + ], + ), + ), + _buildLastVisit(boxImageSize), _buildTodayVisits(boxImageSize), _builPreviousVisits(boxImageSize), + ElevatedButton( + onPressed: () async { + final result = await Navigator.push( + context, + MaterialPageRoute(builder: (context) => SyncCalendarPage()), + ); + + // Refresh the widget if the synchronization was successful. + if (result == true) { + setState(() { + loadData(); + }); + } + }, + child: Text('Synchronisation'), + ) ], ), ); } + Widget _buildLastVisit(boxImageSize) { + if (lastVisitData == null) { + return SizedBox.shrink(); // Rien ne sera affiché + } else { + return Column( + children: [ + Container( + padding: EdgeInsets.fromLTRB(16, 16, 16, 0), + child: Row( + children: [ + Text('Last visit access', style: GlobalStyle.horizontalTitle), + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 8), + height: boxImageSize * GlobalStyle.cardHeightMultiplication, + alignment: Alignment.centerLeft, + padding: EdgeInsets.symmetric(horizontal: 12), + child: buildHorizontalVisitListCard(context, lastVisitData)), + ], + ); + } + } + Widget _buildTodayVisits(boxImageSize) { return Column( children: [ Container( padding: EdgeInsets.fromLTRB(16, 16, 16, 0), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Today Visits', style: GlobalStyle.horizontalTitle), + Container( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 2.0, horizontal: 8.0), + child: badges.Badge( + badgeStyle: badges.BadgeStyle( + badgeColor: Colors.black, + padding: + EdgeInsets.all(todayVisitData.length >= 10 ? 2 : 6), + ), + badgeContent: Text( + todayVisitData.length.toString(), + style: TextStyle(color: Colors.white), + ), + ), + ), + ), ], ), ), @@ -175,11 +248,18 @@ class _TabHomePageState extends State SizedBox(height: 16), // Ajout de l'espace ici Text('Aucune visite ce jour'), ElevatedButton( - onPressed: () { - Navigator.push( + onPressed: () async { + final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => SyncCalendarPage()), ); + + // Refresh the widget if the synchronization was successful. + if (result == true) { + setState(() { + loadData(); + }); + } }, child: Text('Synchroniser'), ), @@ -329,13 +409,26 @@ class _TabHomePageState extends State /// Initializes data when the page loads. Future loadData() async { - try { - // visite model initialisation - todayVisitData = await VisitModel.getTodayVisit(); - previousVisitData = await VisitModel.getPreviousVisit(); - } catch (e) { - // set errorMessage for debug - _errorMessage = 'Error loading visites : $e'; + // visite model initialisation + todayVisitData = await VisitModel.getTodayVisit(); + previousVisitData = await VisitModel.getPreviousVisit(); + + // Search for the visit matching last_id_visit + int lastIdVisite = SharedPrefs().last_id_visite; + + lastVisitData = null; + + if (lastIdVisite > 0) { + for (var visit in todayVisitData) { + if (visit.id_visite == lastIdVisite) { + lastVisitData = visit; + break; + } + } + + if (lastVisitData == null) { + SharedPrefs().last_id_visite = 0; + } } } } diff --git a/lib/ui/reusable/reusable_widget.dart b/lib/ui/reusable/reusable_widget.dart index 37695ec..48a3c8c 100644 --- a/lib/ui/reusable/reusable_widget.dart +++ b/lib/ui/reusable/reusable_widget.dart @@ -1,12 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:mobdr/main.dart'; -import 'package:mobdr/events.dart'; -import 'package:mobdr/config/global_style.dart'; import 'package:mobdr/config/constant.dart'; -import 'package:mobdr/service/shared_prefs.dart'; -import 'package:mobdr/ui/reusable/cache_image_network.dart'; //TODO Rechercher toutes les utilisations diff --git a/lib/ui/splash_screen.dart b/lib/ui/splash_screen.dart index 5044274..32932a2 100644 --- a/lib/ui/splash_screen.dart +++ b/lib/ui/splash_screen.dart @@ -6,7 +6,7 @@ import 'package:flutter/services.dart'; import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/config/constant.dart'; import 'package:mobdr/ui/onboarding.dart'; -import 'package:mobdr/ui/authentication/signin.dart'; +import 'package:mobdr/ui/home.dart'; class SplashScreenPage extends StatefulWidget { @override @@ -28,14 +28,11 @@ class _SplashScreenPageState extends State { if (SharedPrefs().onboarding == 0) { SharedPrefs().onboarding = 1; - - // for this example we will use pushReplacement because we want to go back to the list Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => OnBoardingPage())); } else { - // for this example we will use pushReplacement because we want to go back to the list Navigator.pushReplacement( - context, MaterialPageRoute(builder: (context) => SigninPage())); + context, MaterialPageRoute(builder: (context) => HomePage())); } // if you use this splash screen on the very first time when you open the page, use below code //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => OnBoardingPage()), (Route route) => false); diff --git a/lib/ui/sync/check_connection.dart b/lib/ui/sync/check_connection.dart new file mode 100644 index 0000000..a5b059d --- /dev/null +++ b/lib/ui/sync/check_connection.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; + +class CheckConnectionPage extends StatefulWidget { + final Widget redirectPage; + + const CheckConnectionPage({Key? key, required this.redirectPage}) + : super(key: key); + + @override + _CheckConnectionPageState createState() => _CheckConnectionPageState(); +} + +class _CheckConnectionPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Mayday ..."), + backgroundColor: + Colors.red, // Définir la couleur de fond de l'appbar en rouge + ), + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/no_connection.png', + width: 250, + ), + SizedBox(height: 32), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "No internet connection", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + SizedBox(height: 10), + Center( + child: Text( + "You are not connected. Check your connection.", + style: TextStyle(fontSize: 14), + textAlign: TextAlign.center, + ), + ), + ], + ), + SizedBox(height: 32), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + ), + child: const Text("Check Connection"), + onPressed: () async { + final connectivityResult = + await Connectivity().checkConnectivity(); + if (connectivityResult == ConnectivityResult.none) { + await showDialog( + barrierDismissible: false, + context: context, + builder: (_) => NetworkErrorDialog( + onRetryPressed: () async { + final connectivityResult = + await Connectivity().checkConnectivity(); + if (connectivityResult == ConnectivityResult.none) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Please turn on your wifi or mobile data'))); + } else { + Navigator.pop(context); + // redirect to UploadPhotosPage here + } + }, + ), + ); + } else { + // redirect to UploadPhotosPage here + } + }, + ), + ], + ), + ), + ); + } +} + +class NetworkErrorDialog extends StatelessWidget { + const NetworkErrorDialog({Key? key, this.onRetryPressed}) : super(key: key); + + final VoidCallback? onRetryPressed; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('No Internet Connection'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.error, color: Colors.red, size: 48), + const SizedBox(height: 16), + const Text( + 'Please check your internet connection and try again.', + textAlign: TextAlign.center, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Go Back'), + ), + TextButton( + onPressed: onRetryPressed, + child: const Text('Retry'), + ), + ], + ); + } +} diff --git a/lib/ui/sync/sync_calendar.dart b/lib/ui/sync/sync_calendar.dart index a354f6c..3caf655 100644 --- a/lib/ui/sync/sync_calendar.dart +++ b/lib/ui/sync/sync_calendar.dart @@ -41,7 +41,7 @@ class _SyncCalendarPageState extends State { void _popScreen() { if (mounted) { - Navigator.of(context).pop(); + Navigator.of(context).pop(_syncSuccessful); } } diff --git a/lib/ui/sync/upload_photos.dart b/lib/ui/sync/upload_photos.dart index b84ef8d..c6a5002 100644 --- a/lib/ui/sync/upload_photos.dart +++ b/lib/ui/sync/upload_photos.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; diff --git a/lib/ui/visit/tab_visit.dart b/lib/ui/visit/tab_visit.dart index b96c029..aeb7031 100644 --- a/lib/ui/visit/tab_visit.dart +++ b/lib/ui/visit/tab_visit.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:mobdr/main.dart'; import 'package:mobdr/config/constant.dart'; @@ -9,6 +10,7 @@ import 'package:mobdr/model/visit_model.dart'; import 'package:mobdr/ui/visit/visit_photo_typology.dart'; import 'package:mobdr/ui/reusable/cache_image_network.dart'; import 'package:mobdr/ui/sync/upload_photos.dart'; +import 'package:mobdr/ui/sync/check_connection.dart'; class TabVisitListPage extends StatefulWidget { @override @@ -17,9 +19,6 @@ class TabVisitListPage extends StatefulWidget { class _TabVisitListPageState extends State with AutomaticKeepAliveClientMixin { - // _listKey is used for AnimatedList - final GlobalKey _listKey = GlobalKey(); - // keep the state to do not refresh when switch navbar @override bool get wantKeepAlive => true; @@ -31,6 +30,9 @@ class _TabVisitListPageState extends State late StreamSubscription subVisitPhotoCountEvent; + // _listKey is used for AnimatedList + var _listKey = GlobalKey(); + @override void initState() { super.initState(); @@ -73,28 +75,57 @@ class _TabVisitListPageState extends State ); } return Scaffold( - appBar: AppBar( - iconTheme: IconThemeData( - color: GlobalStyle.appBarIconThemeColor, - ), - elevation: GlobalStyle.appBarElevation, - title: Text( - 'Visit List', - style: GlobalStyle.appBarTitle, - ), - backgroundColor: GlobalStyle.appBarBackgroundColor, - systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle), - body: Column(children: [ + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Synchronization', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle), + body: Column( + children: [ Flexible( - child: AnimatedList( - key: _listKey, - initialItemCount: modelData.length, - physics: AlwaysScrollableScrollPhysics(), - itemBuilder: (context, index, animation) { - return _buildVisitelistCard( - modelData[index], boxImageSize, animation, index); - }, - )), + child: AnimatedList( + key: _listKey, + initialItemCount: modelData.length, + physics: AlwaysScrollableScrollPhysics(), + itemBuilder: (context, index, animation) { + return Dismissible( + key: UniqueKey(), + direction: DismissDirection.endToStart, + onDismissed: (direction) { + // the photo must be removed + setState(() { + modelData.removeAt(index); + _listKey = GlobalKey(); + }); + }, + background: Container( + color: Colors.red, + child: Stack( + children: [ + Positioned.fill( + child: Align( + alignment: Alignment.center, + child: Icon( + Icons.delete, + color: Colors.white, + ), + ), + ), + ], + ), + ), + child: _buildVisitelistCard( + modelData[index], boxImageSize, animation, index), + ); + }, + ), + ), Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( @@ -114,11 +145,11 @@ class _TabVisitListPageState extends State onTap: () { // TODO functionality to be implemented /*` - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ChatUsPage())); - */ + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatUsPage())); + */ }, child: ClipOval( child: Container( @@ -135,10 +166,7 @@ class _TabVisitListPageState extends State Expanded( child: GestureDetector( onTap: () { - Route route = MaterialPageRoute( - builder: (context) => UploadPhotosPage(pp_id_visite: 0), - ); - Navigator.push(context, route); + navigateToPage(context, 0); }, child: Container( alignment: Alignment.center, @@ -146,20 +174,24 @@ class _TabVisitListPageState extends State margin: EdgeInsets.only(right: 8), decoration: BoxDecoration( color: Colors.white, - border: Border.all(width: 1, color: SOFT_BLUE), - borderRadius: BorderRadius.all(Radius.circular( - 10) // <--- border radius here - )), - child: Text('Synchronize ALL', - style: TextStyle( - color: SOFT_BLUE, fontWeight: FontWeight.bold)), + border: Border.all(width: 1, color: Colors.red), + borderRadius: BorderRadius.all( + Radius.circular(10), + )), + child: Text( + 'Synchronize ALL visits', + style: TextStyle( + color: Colors.red, fontWeight: FontWeight.bold), + ), ), ), ), ], ), - ) - ])); + ), + ], + ), + ); } Widget _buildVisitelistCard(VisitModel data, boxImageSize, animation, index) { @@ -283,11 +315,7 @@ class _TabVisitListPageState extends State )) : OutlinedButton( onPressed: () { - Route route = MaterialPageRoute( - builder: (context) => UploadPhotosPage( - pp_id_visite: data.id_visite), - ); - Navigator.push(context, route); + navigateToPage(context, data.id_visite); }, style: ButtonStyle( minimumSize: MaterialStateProperty.all( @@ -324,6 +352,26 @@ class _TabVisitListPageState extends State ); } + Future navigateToPage(BuildContext context, int id_visite) async { + var connectivityResult = await (Connectivity().checkConnectivity()); + + if (connectivityResult == ConnectivityResult.none) { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => CheckConnectionPage( + redirectPage: UploadPhotosPage(pp_id_visite: id_visite), + ), + ), + ); + } else { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => UploadPhotosPage(pp_id_visite: id_visite))); + } + } + /// Initializes data when the page loads. Future loadData() async { try { diff --git a/lib/ui/visit/visit_photo_tag.dart b/lib/ui/visit/visit_photo_tag.dart index f29c20a..b4c4810 100644 --- a/lib/ui/visit/visit_photo_tag.dart +++ b/lib/ui/visit/visit_photo_tag.dart @@ -28,8 +28,6 @@ class PhotoTagPage extends StatefulWidget { } class _PhotoTagPageState extends State { - final GlobalKey _formKey = GlobalKey(); - late List allTagsList = []; late List _selectedTags = []; bool isLoading = true; diff --git a/lib/ui/visit/visit_photo_typology_detail.dart b/lib/ui/visit/visit_photo_typology_detail.dart index 0cb563e..5bd0382 100644 --- a/lib/ui/visit/visit_photo_typology_detail.dart +++ b/lib/ui/visit/visit_photo_typology_detail.dart @@ -40,17 +40,17 @@ class _VisitPhotoTypologyDetailPageState String _errorMessage = ''; // Typology list - late List _typologiesList = []; - int _typologyIndex = 0; + late List _photoTypologiesList = []; + int _photoTypologyIndex = 0; List _visibilities = []; late List tagList = []; - late VisitPhoto _photo; + late VisitPhoto _visitPhoto; // competitors - List _competitorsList = []; - String _competitor = ''; + List _etabCompetitorsList = []; + String _etabCompetitor = ''; @override void initState() { @@ -104,8 +104,8 @@ class _VisitPhotoTypologyDetailPageState onWillPop: () { // fred Map result = { - 'change_typologie': _typologyIndex != - _typologiesList.indexWhere((typology) => + 'change_typologie': _photoTypologyIndex != + _photoTypologiesList.indexWhere((typology) => typology.id_photo_typologie == widget.pp_id_typologie), 'tags': tagList.join(","), 'photo_principale': _visibilities.contains('principal') ? 1 : 0, @@ -213,8 +213,8 @@ class _VisitPhotoTypologyDetailPageState height: 16, ), Wrap( - children: List.generate(_typologiesList.length, (index) { - return radioSize(_typologiesList[index].libelle, index); + children: List.generate(_photoTypologiesList.length, (index) { + return radioSize(_photoTypologiesList[index].libelle, index); }), ), ], @@ -225,27 +225,29 @@ class _VisitPhotoTypologyDetailPageState return GestureDetector( onTap: () async { setState(() { - _typologyIndex = index; + _photoTypologyIndex = index; }); // save photo typology in the database objectbox.putPhotoTypologie( - widget.pp_imageId, _typologiesList[index].id_photo_typologie); + widget.pp_imageId, _photoTypologiesList[index].id_photo_typologie); }, child: Container( padding: EdgeInsets.fromLTRB(12, 8, 12, 8), margin: EdgeInsets.only(right: 8, top: 8), decoration: BoxDecoration( - color: _typologyIndex == index ? SOFT_BLUE : Colors.white, + color: _photoTypologyIndex == index ? SOFT_BLUE : Colors.white, border: Border.all( width: 1, - color: _typologyIndex == index ? SOFT_BLUE : Colors.grey[300]!), + color: _photoTypologyIndex == index + ? SOFT_BLUE + : Colors.grey[300]!), borderRadius: BorderRadius.all( Radius.circular(10) // <--- border radius here )), child: Text(txt, style: TextStyle( - color: _typologyIndex == index ? Colors.white : CHARCOAL)), + color: _photoTypologyIndex == index ? Colors.white : CHARCOAL)), ), ); } @@ -338,7 +340,7 @@ class _VisitPhotoTypologyDetailPageState builder: (context) => PhotoTagPage( pp_langage: widget.pp_visitModel.langage, pp_id_distrib: widget.pp_visitModel.id_distrib, - pp_photoId: this._photo.id, + pp_photoId: this._visitPhoto.id, pp_currentTags: tagList), ), ); @@ -412,7 +414,7 @@ class _VisitPhotoTypologyDetailPageState borderRadius: BorderRadius.all( Radius.circular(10) // <--- border radius here )), - child: _competitor == '' + child: _etabCompetitor == '' ? Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -435,7 +437,7 @@ class _VisitPhotoTypologyDetailPageState Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(_competitor, + Text(_etabCompetitor, style: TextStyle( color: CHARCOAL, fontWeight: FontWeight.bold)), @@ -473,19 +475,19 @@ class _VisitPhotoTypologyDetailPageState Flexible( child: ListView.builder( padding: EdgeInsets.all(16), - itemCount: _competitorsList.length, + itemCount: _etabCompetitorsList.length, itemBuilder: (BuildContext context, int index) { - EtabCompetitor competitor = _competitorsList[index]; + EtabCompetitor competitor = _etabCompetitorsList[index]; return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { setState(() { - _competitor = competitor.nom; + _etabCompetitor = competitor.nom; }); // save photo competitor in the database - objectbox.putPhotoCompetitor(widget.pp_visitModel.id, - competitor.id_concurrence_lien); + objectbox.putPhotoCompetitor( + widget.pp_imageId, competitor.id_concurrence_lien); Navigator.pop(context); }, @@ -513,26 +515,29 @@ class _VisitPhotoTypologyDetailPageState try { // photo typologies initialization - _typologiesList = objectbox.getPhotoTypologiesList(); + _photoTypologiesList = objectbox.getPhotoTypologiesList(); - _typologyIndex = _typologiesList.indexWhere( + _photoTypologyIndex = _photoTypologiesList.indexWhere( (typology) => typology.id_photo_typologie == widget.pp_id_typologie); // get photo object - _photo = objectbox.getPhotoById(photoId)!; + _visitPhoto = objectbox.getPhotoById(photoId)!; // visibilities initialization - if (_photo.photo_privee == 1) _visibilities.add('private'); - if (_photo.photo_principale == 1) _visibilities.add('principal'); + if (_visitPhoto.photo_privee == 1) _visibilities.add('private'); + if (_visitPhoto.photo_principale == 1) _visibilities.add('principal'); // photo tag initialization - tags = _photo.tags; - tagList = tags.isEmpty ? [] : _photo.tags.split(","); + tags = _visitPhoto.tags; + tagList = tags.isEmpty ? [] : _visitPhoto.tags.split(","); // competitor initialization - _competitorsList = + _etabCompetitorsList = objectbox.getEtabCompetitorList(widget.pp_visitModel.id_etab); - _competitor = ""; + + final etabCompetitor = + objectbox.getEtabCompetitorByLink(_visitPhoto.id_concurrence_lien); + _etabCompetitor = etabCompetitor != null ? etabCompetitor.nom : ""; } catch (e) { // set errorMessage for debug _errorMessage = 'Error loading photo: $e'; diff --git a/lib/ui/visit/visit_photo_typology_list.dart b/lib/ui/visit/visit_photo_typology_list.dart index 00ad398..583010d 100644 --- a/lib/ui/visit/visit_photo_typology_list.dart +++ b/lib/ui/visit/visit_photo_typology_list.dart @@ -8,6 +8,7 @@ import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:image/image.dart' as img; +import 'package:image_picker/image_picker.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; @@ -138,20 +139,19 @@ class _VisitPhotoTypologyListPageState Container( child: GestureDetector( onTap: () { - // TODO functionality to be implemented - /*` - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ChatUsPage())); - */ + ImportImageFromGallery(); + /*Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PhotoPickPage())); + */ }, child: ClipOval( child: Container( color: SOFT_BLUE, padding: EdgeInsets.all(9), - child: - Icon(Icons.chat, color: Colors.white, size: 16)), + child: Icon(Icons.photo_library, + color: Colors.white, size: 16)), ), ), ), @@ -443,7 +443,6 @@ class _VisitPhotoTypologyListPageState ); } - // TODO ƒuture void ? void loadData() { _visitPhotoData = objectbox.getAllVisitTypologyPhotos( widget.pp_visitModel.id_visite, widget.pp_id_typologie); @@ -474,6 +473,7 @@ class _VisitPhotoTypologyListPageState _listPhotos.add(VisitPhoto( id_visite: widget.pp_visitModel.id_visite, id_photo_typologie: widget.pp_id_typologie, + depuis_galerie: false, image_name: myPhoto.path.split('/').last)); } @@ -542,8 +542,11 @@ class _VisitPhotoTypologyListPageState // delete file on database objectbox.delPhotoByName(removedItem.image_name); - // delete file on local storage - deleteFile(new File(removedItem.getImage())); + // if the photo is not from the gallery + if (removedItem.depuis_galerie == false) { + // delete file on local storage + deleteFile(new File(removedItem.getImage())); + } // This builder is just so that the animation has something // to work with before it disappears from view since the original @@ -650,4 +653,47 @@ class _VisitPhotoTypologyListPageState return tempFile; } + + Future ImportImageFromGallery() async { + try { + // get images from gallery + List? images = await ImagePicker().pickMultiImage(); + + // if images have been selected + if (images.length > 0) { + final List _listPhotos = []; + + for (var image in images) { + // to insert into database + _listPhotos.add(VisitPhoto( + id_visite: widget.pp_visitModel.id_visite, + id_photo_typologie: widget.pp_id_typologie, + depuis_galerie: true, + image_name: image.path)); + } + + // insert photo(s) in database (async) + final addedPhotos = await objectbox.addPhotos(_listPhotos); + + // insert photo(s) in widget at the beginning (0) + _visitPhotoData.insertAll(0, addedPhotos); + + // if new photos are taken + if (addedPhotos.length > 0) { + visitPhotoCount += addedPhotos.length; + // a global refresh event is sent + eventBus.fire(VisitPhotoCountEvent( + widget.pp_visitModel.id_visite, visitPhotoCount)); + } + + // refresh widget + setState(() { + _listKey = GlobalKey(); + }); + } + } catch (e) { + // Gestion de l'erreur + print('Erreur lors de la sélection d\'images : $e'); + } + } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 83c4826..4464393 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import connectivity_plus import device_info_plus import objectbox_flutter_libs import package_info_plus @@ -14,6 +15,7 @@ import sqflite import wakelock_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 6f91b2a..a4c58d5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -241,6 +241,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: b74247fad72c171381dbe700ca17da24deac637ab6d43c343b42867acb95c991 + url: "https://pub.dev" + source: hosted + version: "3.0.6" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" + source: hosted + version: "1.2.4" convert: dependency: transitive description: @@ -297,6 +313,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.4" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" debounce_throttle: dependency: transitive description: @@ -529,10 +553,10 @@ packages: dependency: "direct main" description: name: image_picker - sha256: "64b21d9f0e065f9ab0e4dde458076226c97382cc0c6949144cb874c62bf8e9f8" + sha256: "3da954c3b8906d82ecb50fd5e2b5401758f06d5678904eed6cbc06172283a263" url: "https://pub.dev" source: hosted - version: "0.8.7" + version: "0.8.7+4" image_picker_android: dependency: transitive description: @@ -653,6 +677,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" numerus: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7d0c82f..a019ecd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,7 +51,8 @@ dependencies: wakelock: ^0.6.2 shimmer: 2.0.0 - image_picker: ^0.8.7 + # https://pub.dev/packages/image_picker + image_picker: ^0.8.7+4 # https://pub.dev/packages/camera camera: ^0.10.4 @@ -89,6 +90,9 @@ dependencies: # https://pub.dev/packages/badges badges: ^3.1.1 + + # https://pub.dev/packages/connectivity_plus + connectivity_plus: ^3.0.6 dev_dependencies: flutter_test: @@ -165,6 +169,7 @@ flutter: - assets/images/process_timeline/status4.png - assets/images/process_timeline/status5.png - assets/images/simulator.jpeg + - assets/images/no_connection.png - assets/lang/fr.json - assets/lang/en.json diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 2ef7154..be59d5c 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,10 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); ObjectboxFlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ObjectboxFlutterLibsPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index c46e00f..7121101 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus objectbox_flutter_libs permission_handler_windows )