feat: Livraison version 3.0.6

- Amélioration de la gestion des entités et des utilisateurs
- Mise à jour des modèles Amicale et Client avec champs supplémentaires
- Ajout du service de logging et amélioration du chargement UI
- Refactoring des formulaires utilisateur et amicale
- Intégration de file_picker et image_picker pour la gestion des fichiers
- Amélioration de la gestion des membres et de leur suppression
- Optimisation des performances de l'API
- Mise à jour de la documentation technique

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-08 20:33:54 +02:00
parent 1018b86537
commit 0e98a94374
63 changed files with 104136 additions and 87983 deletions

File diff suppressed because one or more lines are too long

View File

@@ -35,13 +35,16 @@ class AmicaleModelAdapter extends TypeAdapter<AmicaleModel> {
chkStripe: fields[19] as bool,
createdAt: fields[20] as DateTime?,
updatedAt: fields[21] as DateTime?,
chkMdpManuel: fields[22] as bool,
chkUsernameManuel: fields[23] as bool,
logoBase64: fields[24] as String?,
);
}
@override
void write(BinaryWriter writer, AmicaleModel obj) {
writer
..writeByte(22)
..writeByte(25)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -85,7 +88,13 @@ class AmicaleModelAdapter extends TypeAdapter<AmicaleModel> {
..writeByte(20)
..write(obj.createdAt)
..writeByte(21)
..write(obj.updatedAt);
..write(obj.updatedAt)
..writeByte(22)
..write(obj.chkMdpManuel)
..writeByte(23)
..write(obj.chkUsernameManuel)
..writeByte(24)
..write(obj.logoBase64);
}
@override

View File

@@ -35,13 +35,15 @@ class ClientModelAdapter extends TypeAdapter<ClientModel> {
chkStripe: fields[19] as bool?,
createdAt: fields[20] as DateTime?,
updatedAt: fields[21] as DateTime?,
chkMdpManuel: fields[22] as bool?,
chkUsernameManuel: fields[23] as bool?,
);
}
@override
void write(BinaryWriter writer, ClientModel obj) {
writer
..writeByte(22)
..writeByte(24)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -85,7 +87,11 @@ class ClientModelAdapter extends TypeAdapter<ClientModel> {
..writeByte(20)
..write(obj.createdAt)
..writeByte(21)
..write(obj.updatedAt);
..write(obj.updatedAt)
..writeByte(22)
..write(obj.chkMdpManuel)
..writeByte(23)
..write(obj.chkUsernameManuel);
}
@override

View File

@@ -8,6 +8,7 @@
import 'package:connectivity_plus/src/connectivity_plus_web.dart';
import 'package:geolocator_web/geolocator_web.dart';
import 'package:image_picker_for_web/image_picker_for_web.dart';
import 'package:package_info_plus/src/package_info_plus_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
@@ -17,6 +18,7 @@ void registerPlugins([final Registrar? pluginRegistrar]) {
final Registrar registrar = pluginRegistrar ?? webPluginRegistrar;
ConnectivityPlusWebPlugin.registerWith(registrar);
GeolocatorPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar);
PackageInfoPlusWebPlugin.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);

File diff suppressed because one or more lines are too long

View File

@@ -86,6 +86,11 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interfa
file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/lib/method_channel_connectivity.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/lib/src/enums.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/cross_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/types/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/types/html.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/web_helpers/web_helpers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/x_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/lib/crypto.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/lib/src/digest.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/lib/src/digest_sink.dart
@@ -103,36 +108,36 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/dart_pol
file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/src/impl.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/src/point.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/adapters/browser_adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/cancel_token.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/compute/compute.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/compute/compute_web.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio/dio_for_browser.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio_exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio_mixin.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/form_data.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/headers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/interceptors/imply_content_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/interceptors/log.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/multipart_file/browser_multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/parameter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/progress_stream/browser_progress_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/redirect_record.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/response/response_stream_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/background_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/fused_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/sync_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/util/consolidate_bytes.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/util/transform_empty_to_null.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/adapters/browser_adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/cancel_token.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/compute/compute.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/compute/compute_web.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio/dio_for_browser.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio_exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio_mixin.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/form_data.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/headers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/interceptors/imply_content_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/interceptors/log.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/multipart_file/browser_multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/parameter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/progress_stream/browser_progress_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/redirect_record.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/response/response_stream_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/background_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/fused_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/sync_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/util/consolidate_bytes.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/util/transform_empty_to_null.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio_cache_interceptor-4.0.3/lib/dio_cache_interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio_cache_interceptor-4.0.3/lib/src/dio_cache_interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio_cache_interceptor-4.0.3/lib/src/dio_cache_interceptor_cache_utils.dart
@@ -442,25 +447,25 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/hive_ex
file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/stub/path.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/stub/path_provider.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/watch_box_builder.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/http.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/retry.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/abortable.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/base_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/base_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/base_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/boundary_characters.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/browser_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/byte_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/multipart_file_stub.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/multipart_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/streamed_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/streamed_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/http.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/retry.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/abortable.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/base_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/base_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/base_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/boundary_characters.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/browser_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/byte_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/multipart_file_stub.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/multipart_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/streamed_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/streamed_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_cache_core-1.1.1/lib/http_cache_core.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_cache_core-1.1.1/lib/src/model/cache/cache.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_cache_core-1.1.1/lib/src/model/cache/cache_cipher.dart
@@ -497,6 +502,29 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/http_dat
file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/media_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/scan.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker-1.1.2/lib/image_picker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/image_picker_for_web.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/src/image_resizer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/src/image_resizer_utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/src/pkg_web_tweaks.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/image_picker_platform_interface.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/method_channel/method_channel_image_picker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/platform_interface/image_picker_platform.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/camera_delegate.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/camera_device.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/image_options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/image_source.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/lost_data_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/media_options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/media_selection_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/multi_image_picker_options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/html.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/lost_data.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/picked_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/retrieve_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/types.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/date_symbol_data_custom.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/date_symbols.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/intl.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/number_symbols.dart
@@ -615,6 +643,15 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/classes/bb
file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/classes/lonlat.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/classes/utm.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/mgrs.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/mime.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/bound_multipart_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/char_code.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/default_extension_map.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/extension.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/magic_number.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/mime_multipart_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/mime_shared.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/mime_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/lib/package_info_plus.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/lib/src/package_info_plus_linux.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/lib/src/package_info_plus_web.dart
@@ -736,129 +773,128 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/src/span_
file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/src/string_scanner.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/string_scanner.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/charts.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/datetime_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/datetime_category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/logarithmic_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/multi_level_labels.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/numeric_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/plot_band.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/behaviors/crosshair.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/behaviors/trackball.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/behaviors/zooming.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/cartesian_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/circular_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/annotation.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/callbacks.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/chart_point.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/circular_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/circular_data_label_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/connector_line.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/core_legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/core_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/element_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/empty_points.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/funnel_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/interactive_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/layout_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/pyramid_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/title.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/funnel_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/accumulation_distribution_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/atr_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/bollinger_bands_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/ema_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/macd_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/momentum_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/roc_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/rsi_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/sma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/stochastic_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/technical_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/tma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/wma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/interactions/behavior.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/interactions/selection.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/interactions/tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/pyramid_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/box_and_whisker_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/bubble_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/candle_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/chart_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/doughnut_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/error_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/fast_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/funnel_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/hilo_open_close_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/hilo_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/histogram_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/pie_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/pyramid_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/radial_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/range_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/range_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/scatter_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/spline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_area100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_bar100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_column100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_line100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/step_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stepline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/waterfall_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/trendline/trendline.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/constants.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/renderer_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/typedef.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/zooming_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/sparkline/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/sparkline/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/sparkline/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/core.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/calendar/calendar_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/calendar/hijri_date_time.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/license.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/localizations/global_localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/slider_controller.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/assistview_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/barcodes_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/calendar_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/chat_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/color_scheme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/datagrid_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/datapager_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/daterangepicker_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/gauges_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/maps_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/pdfviewer_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/range_selector_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/range_slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/spark_charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/theme_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/treemap_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/utils/shape_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/charts.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/datetime_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/datetime_category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/logarithmic_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/multi_level_labels.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/numeric_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/plot_band.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/behaviors/crosshair.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/behaviors/trackball.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/behaviors/zooming.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/cartesian_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/circular_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/annotation.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/callbacks.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/chart_point.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/circular_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/circular_data_label_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/connector_line.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/core_legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/core_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/element_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/empty_points.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/funnel_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/interactive_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/layout_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/pyramid_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/title.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/funnel_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/accumulation_distribution_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/atr_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/bollinger_bands_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/ema_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/macd_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/momentum_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/roc_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/rsi_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/sma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/stochastic_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/technical_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/tma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/wma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/interactions/behavior.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/interactions/selection.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/interactions/tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/pyramid_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/box_and_whisker_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/bubble_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/candle_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/chart_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/doughnut_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/error_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/fast_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/funnel_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/hilo_open_close_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/hilo_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/histogram_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/pie_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/pyramid_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/radial_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/range_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/range_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/scatter_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/spline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_area100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_bar100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_column100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_line100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/step_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stepline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/waterfall_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/trendline/trendline.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/constants.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/renderer_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/typedef.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/zooming_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/sparkline/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/sparkline/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/sparkline/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/core.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/calendar/calendar_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/calendar/hijri_date_time.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/localizations/global_localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/slider_controller.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/assistview_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/barcodes_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/calendar_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/chat_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/color_scheme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/datagrid_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/datapager_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/daterangepicker_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/gauges_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/maps_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/pdfviewer_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/range_selector_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/range_slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/spark_charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/theme_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/treemap_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/utils/shape_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/lib/src/generated/ascii_glyph_set.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/lib/src/generated/glyph_set.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/lib/src/generated/top_level.dart
@@ -951,6 +987,7 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/src/vector_m
file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/src/vector_math_64/vector4.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/vector_math.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/vector_math_64.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/helpers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/src/dom.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/src/dom/accelerometer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/src/dom/angle_instanced_arrays.dart
@@ -1795,6 +1832,15 @@ file:///home/pierre/dev/flutter/packages/flutter/lib/src/widgets/widget_span.dar
file:///home/pierre/dev/flutter/packages/flutter/lib/src/widgets/widget_state.dart
file:///home/pierre/dev/flutter/packages/flutter/lib/src/widgets/will_pop_scope.dart
file:///home/pierre/dev/flutter/packages/flutter/lib/widgets.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/flutter_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/cupertino_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_cupertino_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_date_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_widgets_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/material_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/utils/date_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/widgets_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_web_plugins/lib/flutter_web_plugins.dart
file:///home/pierre/dev/flutter/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart
file:///home/pierre/dev/flutter/packages/flutter_web_plugins/lib/src/navigation/utils.dart
@@ -1858,6 +1904,7 @@ file:///home/pierre/dev/geosector/app/lib/core/services/hive_reset_state_service
file:///home/pierre/dev/geosector/app/lib/core/services/hive_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/hive_web_fix.dart
file:///home/pierre/dev/geosector/app/lib/core/services/location_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/logger_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/sync_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/theme_service.dart
file:///home/pierre/dev/geosector/app/lib/core/theme/app_theme.dart
@@ -1904,7 +1951,7 @@ file:///home/pierre/dev/geosector/app/lib/presentation/widgets/dashboard_app_bar
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/dashboard_layout.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/form_section.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/help_dialog.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/loading_progress_overlay.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/loading_spin_overlay.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/mapbox_map.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/membre_row_widget.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/membre_table_widget.dart

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -86,6 +86,11 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interfa
file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/lib/method_channel_connectivity.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/lib/src/enums.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/connectivity_plus_platform_interface-2.0.1/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/cross_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/types/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/types/html.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/web_helpers/web_helpers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/src/x_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/lib/crypto.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/lib/src/digest.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/lib/src/digest_sink.dart
@@ -103,36 +108,36 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/dart_pol
file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/src/impl.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/src/point.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dart_polylabel2-1.0.0/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/adapters/browser_adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/cancel_token.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/compute/compute.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/compute/compute_web.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio/dio_for_browser.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio_exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/dio_mixin.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/form_data.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/headers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/interceptors/imply_content_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/interceptors/log.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/multipart_file/browser_multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/parameter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/progress_stream/browser_progress_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/redirect_record.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/response/response_stream_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/background_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/fused_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/sync_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/util/consolidate_bytes.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/transformers/util/transform_empty_to_null.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/adapters/browser_adapter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/cancel_token.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/compute/compute.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/compute/compute_web.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio/dio_for_browser.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio_exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/dio_mixin.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/form_data.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/headers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/interceptors/imply_content_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/interceptors/log.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/multipart_file/browser_multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/parameter.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/progress_stream/browser_progress_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/redirect_record.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/response/response_stream_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/background_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/fused_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/sync_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/util/consolidate_bytes.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/transformers/util/transform_empty_to_null.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio_cache_interceptor-4.0.3/lib/dio_cache_interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio_cache_interceptor-4.0.3/lib/src/dio_cache_interceptor.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/dio_cache_interceptor-4.0.3/lib/src/dio_cache_interceptor_cache_utils.dart
@@ -442,25 +447,25 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/hive_ex
file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/stub/path.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/stub/path_provider.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/hive_flutter-1.1.0/lib/src/watch_box_builder.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/http.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/retry.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/abortable.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/base_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/base_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/base_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/boundary_characters.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/browser_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/byte_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/multipart_file_stub.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/multipart_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/streamed_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/streamed_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/http.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/retry.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/abortable.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/base_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/base_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/base_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/boundary_characters.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/browser_client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/byte_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/client.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/exception.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/multipart_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/multipart_file_stub.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/multipart_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/streamed_request.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/streamed_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_cache_core-1.1.1/lib/http_cache_core.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_cache_core-1.1.1/lib/src/model/cache/cache.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_cache_core-1.1.1/lib/src/model/cache/cache_cipher.dart
@@ -497,6 +502,29 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/http_dat
file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/media_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/scan.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/http_parser-4.1.2/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker-1.1.2/lib/image_picker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/image_picker_for_web.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/src/image_resizer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/src/image_resizer_utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/src/pkg_web_tweaks.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/image_picker_platform_interface.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/method_channel/method_channel_image_picker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/platform_interface/image_picker_platform.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/camera_delegate.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/camera_device.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/image_options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/image_source.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/lost_data_response.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/media_options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/media_selection_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/multi_image_picker_options.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/html.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/lost_data.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/picked_file/picked_file.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/retrieve_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/src/types/types.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/date_symbol_data_custom.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/date_symbols.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/intl.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/lib/number_symbols.dart
@@ -615,6 +643,15 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/classes/bb
file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/classes/lonlat.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/classes/utm.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mgrs_dart-2.0.0/lib/src/mgrs.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/mime.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/bound_multipart_stream.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/char_code.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/default_extension_map.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/extension.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/magic_number.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/mime_multipart_transformer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/mime_shared.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/mime-2.0.0/lib/src/mime_type.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/lib/package_info_plus.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/lib/src/package_info_plus_linux.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/lib/src/package_info_plus_web.dart
@@ -736,129 +773,128 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/src/span_
file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/src/string_scanner.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/src/utils.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/string_scanner.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/charts.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/datetime_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/datetime_category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/logarithmic_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/multi_level_labels.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/numeric_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/axis/plot_band.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/behaviors/crosshair.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/behaviors/trackball.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/behaviors/zooming.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/cartesian_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/circular_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/annotation.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/callbacks.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/chart_point.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/circular_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/circular_data_label_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/connector_line.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/core_legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/core_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/element_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/empty_points.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/funnel_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/interactive_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/layout_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/pyramid_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/common/title.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/funnel_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/accumulation_distribution_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/atr_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/bollinger_bands_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/ema_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/macd_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/momentum_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/roc_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/rsi_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/sma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/stochastic_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/technical_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/tma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/indicators/wma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/interactions/behavior.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/interactions/selection.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/interactions/tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/pyramid_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/box_and_whisker_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/bubble_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/candle_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/chart_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/doughnut_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/error_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/fast_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/funnel_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/hilo_open_close_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/hilo_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/histogram_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/pie_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/pyramid_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/radial_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/range_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/range_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/scatter_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/spline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_area100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_bar100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_column100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_line100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stacked_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/step_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/stepline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/series/waterfall_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/trendline/trendline.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/constants.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/renderer_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/typedef.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/charts/utils/zooming_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/sparkline/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/sparkline/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/src/sparkline/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/core.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/calendar/calendar_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/calendar/hijri_date_time.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/license.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/localizations/global_localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/slider_controller.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/assistview_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/barcodes_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/calendar_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/chat_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/color_scheme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/datagrid_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/datapager_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/daterangepicker_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/gauges_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/maps_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/pdfviewer_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/range_selector_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/range_slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/spark_charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/theme_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/theme/treemap_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/src/utils/shape_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/charts.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/datetime_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/datetime_category_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/logarithmic_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/multi_level_labels.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/numeric_axis.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/axis/plot_band.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/base.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/behaviors/crosshair.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/behaviors/trackball.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/behaviors/zooming.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/cartesian_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/circular_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/annotation.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/callbacks.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/chart_point.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/circular_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/circular_data_label_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/connector_line.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/core_legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/core_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/element_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/empty_points.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/funnel_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/interactive_tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/layout_handler.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/legend.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/pyramid_data_label.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/common/title.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/funnel_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/accumulation_distribution_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/atr_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/bollinger_bands_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/ema_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/macd_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/momentum_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/roc_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/rsi_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/sma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/stochastic_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/technical_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/tma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/indicators/wma_indicator.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/interactions/behavior.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/interactions/selection.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/interactions/tooltip.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/pyramid_chart.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/box_and_whisker_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/bubble_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/candle_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/chart_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/doughnut_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/error_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/fast_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/funnel_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/hilo_open_close_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/hilo_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/histogram_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/pie_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/pyramid_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/radial_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/range_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/range_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/scatter_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/spline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_area100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_bar100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_bar_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_column100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_column_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_line100_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stacked_line_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/step_area_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/stepline_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/series/waterfall_series.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/trendline/trendline.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/constants.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/renderer_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/typedef.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/charts/utils/zooming_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/sparkline/marker.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/sparkline/utils/enum.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/src/sparkline/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/core.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/calendar/calendar_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/calendar/hijri_date_time.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/localizations/global_localizations.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/slider_controller.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/assistview_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/barcodes_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/calendar_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/chat_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/color_scheme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/datagrid_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/datapager_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/daterangepicker_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/gauges_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/maps_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/pdfviewer_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/range_selector_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/range_slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/slider_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/spark_charts_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/theme_widget.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/theme/treemap_theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/utils/helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/src/utils/shape_helper.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/theme.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/lib/src/generated/ascii_glyph_set.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/lib/src/generated/glyph_set.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/term_glyph-1.2.2/lib/src/generated/top_level.dart
@@ -951,6 +987,7 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/src/vector_m
file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/src/vector_math_64/vector4.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/vector_math.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/vector_math_64.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/helpers.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/src/dom.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/src/dom/accelerometer.dart
file:///home/pierre/.pub-cache/hosted/pub.dev/web-1.1.1/lib/src/dom/angle_instanced_arrays.dart
@@ -1794,6 +1831,15 @@ file:///home/pierre/dev/flutter/packages/flutter/lib/src/widgets/widget_span.dar
file:///home/pierre/dev/flutter/packages/flutter/lib/src/widgets/widget_state.dart
file:///home/pierre/dev/flutter/packages/flutter/lib/src/widgets/will_pop_scope.dart
file:///home/pierre/dev/flutter/packages/flutter/lib/widgets.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/flutter_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/cupertino_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_cupertino_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_date_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_material_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/l10n/generated_widgets_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/material_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/utils/date_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/src/widgets_localizations.dart
file:///home/pierre/dev/flutter/packages/flutter_web_plugins/lib/flutter_web_plugins.dart
file:///home/pierre/dev/flutter/packages/flutter_web_plugins/lib/src/navigation/url_strategy.dart
file:///home/pierre/dev/flutter/packages/flutter_web_plugins/lib/src/navigation/utils.dart
@@ -1857,6 +1903,7 @@ file:///home/pierre/dev/geosector/app/lib/core/services/hive_reset_state_service
file:///home/pierre/dev/geosector/app/lib/core/services/hive_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/hive_web_fix.dart
file:///home/pierre/dev/geosector/app/lib/core/services/location_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/logger_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/sync_service.dart
file:///home/pierre/dev/geosector/app/lib/core/services/theme_service.dart
file:///home/pierre/dev/geosector/app/lib/core/theme/app_theme.dart
@@ -1903,7 +1950,7 @@ file:///home/pierre/dev/geosector/app/lib/presentation/widgets/dashboard_app_bar
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/dashboard_layout.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/form_section.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/help_dialog.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/loading_progress_overlay.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/loading_spin_overlay.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/mapbox_map.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/membre_row_widget.dart
file:///home/pierre/dev/geosector/app/lib/presentation/widgets/membre_table_widget.dart

View File

@@ -8,6 +8,7 @@
import 'package:connectivity_plus/src/connectivity_plus_web.dart';
import 'package:geolocator_web/geolocator_web.dart';
import 'package:image_picker_for_web/image_picker_for_web.dart';
import 'package:package_info_plus/src/package_info_plus_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
@@ -17,6 +18,7 @@ void registerPlugins([final Registrar? pluginRegistrar]) {
final Registrar registrar = pluginRegistrar ?? webPluginRegistrar;
ConnectivityPlusWebPlugin.registerWith(registrar);
GeolocatorPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar);
PackageInfoPlusWebPlugin.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);

File diff suppressed because one or more lines are too long

View File

@@ -87,7 +87,7 @@
},
{
"name": "built_value",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/built_value-8.11.0",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/built_value-8.11.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
@@ -151,6 +151,12 @@
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "cross_file",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "crypto",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6",
@@ -195,7 +201,7 @@
},
{
"name": "dio",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0",
"packageUri": "lib/",
"languageVersion": "2.18"
},
@@ -241,6 +247,30 @@
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "file_selector_linux",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_linux-0.9.3+2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "file_selector_macos",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_macos-0.9.4+3",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "file_selector_platform_interface",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_platform_interface-2.6.2",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "file_selector_windows",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_windows-0.9.3+4",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "fixnum",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/fixnum-1.1.1",
@@ -295,6 +325,12 @@
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "flutter_localizations",
"rootUri": "file:///home/pierre/dev/flutter/packages/flutter_localizations",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "flutter_map",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_map-8.2.1",
@@ -307,6 +343,12 @@
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "flutter_plugin_android_lifecycle",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.29",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "flutter_svg",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_svg-2.2.0",
@@ -435,7 +477,7 @@
},
{
"name": "http",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
@@ -469,6 +511,54 @@
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "image_picker",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker-1.1.2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "image_picker_android",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_android-0.8.12+25",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "image_picker_for_web",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "image_picker_ios",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_ios-0.8.12+2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "image_picker_linux",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_linux-0.2.1+2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "image_picker_macos",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_macos-0.2.1+2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "image_picker_platform_interface",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "image_picker_windows",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_windows-0.2.1+1",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "intl",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2",
@@ -717,7 +807,7 @@
},
{
"name": "shared_preferences_android",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.10",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.11",
"packageUri": "lib/",
"languageVersion": "3.6"
},
@@ -819,13 +909,13 @@
},
{
"name": "syncfusion_flutter_charts",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "syncfusion_flutter_core",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4",
"packageUri": "lib/",
"languageVersion": "3.7"
},
@@ -891,7 +981,7 @@
},
{
"name": "url_launcher_android",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.16",
"rootUri": "file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.17",
"packageUri": "lib/",
"languageVersion": "3.6"
},

View File

@@ -52,8 +52,8 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/built_collection-5.1.1/
file:///home/pierre/.pub-cache/hosted/pub.dev/built_collection-5.1.1/lib/
built_value
3.0
file:///home/pierre/.pub-cache/hosted/pub.dev/built_value-8.11.0/
file:///home/pierre/.pub-cache/hosted/pub.dev/built_value-8.11.0/lib/
file:///home/pierre/.pub-cache/hosted/pub.dev/built_value-8.11.1/
file:///home/pierre/.pub-cache/hosted/pub.dev/built_value-8.11.1/lib/
characters
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/characters-1.4.0/
@@ -94,6 +94,10 @@ convert
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/convert-3.1.2/
file:///home/pierre/.pub-cache/hosted/pub.dev/convert-3.1.2/lib/
cross_file
3.3
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/
file:///home/pierre/.pub-cache/hosted/pub.dev/cross_file-0.3.4+2/lib/
crypto
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/crypto-3.0.6/
@@ -124,8 +128,8 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/dbus-0.7.11/
file:///home/pierre/.pub-cache/hosted/pub.dev/dbus-0.7.11/lib/
dio
2.18
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.8.0+1/lib/
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/
file:///home/pierre/.pub-cache/hosted/pub.dev/dio-5.9.0/lib/
dio_cache_interceptor
3.0
file:///home/pierre/.pub-cache/hosted/pub.dev/dio_cache_interceptor-4.0.3/
@@ -154,6 +158,22 @@ file
3.0
file:///home/pierre/.pub-cache/hosted/pub.dev/file-7.0.1/
file:///home/pierre/.pub-cache/hosted/pub.dev/file-7.0.1/lib/
file_selector_linux
3.3
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_linux-0.9.3+2/
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_linux-0.9.3+2/lib/
file_selector_macos
3.6
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_macos-0.9.4+3/
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_macos-0.9.4+3/lib/
file_selector_platform_interface
3.0
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_platform_interface-2.6.2/
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_platform_interface-2.6.2/lib/
file_selector_windows
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_windows-0.9.3+4/
file:///home/pierre/.pub-cache/hosted/pub.dev/file_selector_windows-0.9.3+4/lib/
fixnum
3.1
file:///home/pierre/.pub-cache/hosted/pub.dev/fixnum-1.1.1/
@@ -194,6 +214,10 @@ flutter_map_cache
3.6
file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_map_cache-2.0.0+1/
file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_map_cache-2.0.0+1/lib/
flutter_plugin_android_lifecycle
3.6
file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.29/
file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.29/lib/
flutter_svg
3.6
file:///home/pierre/.pub-cache/hosted/pub.dev/flutter_svg-2.2.0/
@@ -272,8 +296,8 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/html-0.15.6/
file:///home/pierre/.pub-cache/hosted/pub.dev/html-0.15.6/lib/
http
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0-beta.2/lib/
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/
file:///home/pierre/.pub-cache/hosted/pub.dev/http-1.5.0/lib/
http_cache_core
3.0
file:///home/pierre/.pub-cache/hosted/pub.dev/http_cache_core-1.1.1/
@@ -294,6 +318,38 @@ image
3.0
file:///home/pierre/.pub-cache/hosted/pub.dev/image-4.5.4/
file:///home/pierre/.pub-cache/hosted/pub.dev/image-4.5.4/lib/
image_picker
3.3
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker-1.1.2/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker-1.1.2/lib/
image_picker_android
3.6
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_android-0.8.12+25/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_android-0.8.12+25/lib/
image_picker_for_web
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_for_web-3.0.6/lib/
image_picker_ios
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_ios-0.8.12+2/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_ios-0.8.12+2/lib/
image_picker_linux
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_linux-0.2.1+2/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_linux-0.2.1+2/lib/
image_picker_macos
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_macos-0.2.1+2/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_macos-0.2.1+2/lib/
image_picker_platform_interface
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_platform_interface-2.10.1/lib/
image_picker_windows
2.19
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_windows-0.2.1+1/
file:///home/pierre/.pub-cache/hosted/pub.dev/image_picker_windows-0.2.1+1/lib/
intl
3.3
file:///home/pierre/.pub-cache/hosted/pub.dev/intl-0.20.2/
@@ -460,8 +516,8 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences-2.5.3/
file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences-2.5.3/lib/
shared_preferences_android
3.6
file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.10/
file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.10/lib/
file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.11/
file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.11/lib/
shared_preferences_foundation
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/
@@ -524,12 +580,12 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/
file:///home/pierre/.pub-cache/hosted/pub.dev/string_scanner-1.4.1/lib/
syncfusion_flutter_charts
3.7
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.1.42/lib/
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_charts-30.2.4/lib/
syncfusion_flutter_core
3.7
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.1.42/lib/
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/
file:///home/pierre/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-30.2.4/lib/
synchronized
3.8
file:///home/pierre/.pub-cache/hosted/pub.dev/synchronized-3.4.0/
@@ -572,8 +628,8 @@ file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher-6.3.2/
file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher-6.3.2/lib/
url_launcher_android
3.6
file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.16/
file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.16/lib/
file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.17/
file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.17/lib/
url_launcher_ios
3.4
file:///home/pierre/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.3/
@@ -670,6 +726,10 @@ flutter
3.7
file:///home/pierre/dev/flutter/packages/flutter/
file:///home/pierre/dev/flutter/packages/flutter/lib/
flutter_localizations
3.7
file:///home/pierre/dev/flutter/packages/flutter_localizations/
file:///home/pierre/dev/flutter/packages/flutter_localizations/lib/
flutter_test
3.7
file:///home/pierre/dev/flutter/packages/flutter_test/

View File

@@ -5,7 +5,7 @@
"packages": [
{
"name": "geosector_app",
"version": "3.0.4+304",
"version": "3.0.6+306",
"dependencies": [
"connectivity_plus",
"cupertino_icons",
@@ -13,6 +13,7 @@
"fl_chart",
"flutter",
"flutter_local_notifications",
"flutter_localizations",
"flutter_map",
"flutter_map_cache",
"flutter_svg",
@@ -22,6 +23,7 @@
"hive",
"hive_flutter",
"http_cache_file_store",
"image_picker",
"intl",
"latlong2",
"mqtt5_client",
@@ -101,6 +103,20 @@
"vm_service"
]
},
{
"name": "image_picker",
"version": "1.1.2",
"dependencies": [
"flutter",
"image_picker_android",
"image_picker_for_web",
"image_picker_ios",
"image_picker_linux",
"image_picker_macos",
"image_picker_platform_interface",
"image_picker_windows"
]
},
{
"name": "universal_html",
"version": "2.2.4",
@@ -136,16 +152,6 @@
"intl"
]
},
{
"name": "syncfusion_flutter_charts",
"version": "30.1.42",
"dependencies": [
"flutter",
"intl",
"syncfusion_flutter_core",
"vector_math"
]
},
{
"name": "fl_chart",
"version": "1.0.0",
@@ -203,6 +209,21 @@
"meta"
]
},
{
"name": "flutter_localizations",
"version": "0.0.0",
"dependencies": [
"characters",
"clock",
"collection",
"flutter",
"intl",
"material_color_utilities",
"meta",
"path",
"vector_math"
]
},
{
"name": "flutter",
"version": "0.0.0",
@@ -406,14 +427,6 @@
"package_info_plus"
]
},
{
"name": "syncfusion_flutter_core",
"version": "30.1.42",
"dependencies": [
"flutter",
"vector_math"
]
},
{
"name": "equatable",
"version": "2.0.7",
@@ -489,6 +502,24 @@
"url_launcher_windows"
]
},
{
"name": "syncfusion_flutter_charts",
"version": "30.2.4",
"dependencies": [
"flutter",
"intl",
"syncfusion_flutter_core",
"vector_math"
]
},
{
"name": "syncfusion_flutter_core",
"version": "30.2.4",
"dependencies": [
"flutter",
"vector_math"
]
},
{
"name": "connectivity_plus",
"version": "6.1.4",
@@ -560,6 +591,26 @@
"yaml"
]
},
{
"name": "image_picker_windows",
"version": "0.2.1+1",
"dependencies": [
"file_selector_platform_interface",
"file_selector_windows",
"flutter",
"image_picker_platform_interface"
]
},
{
"name": "image_picker_platform_interface",
"version": "2.10.1",
"dependencies": [
"cross_file",
"flutter",
"http",
"plugin_platform_interface"
]
},
{
"name": "typed_data",
"version": "1.4.0",
@@ -567,6 +618,13 @@
"collection"
]
},
{
"name": "plugin_platform_interface",
"version": "2.1.8",
"dependencies": [
"meta"
]
},
{
"name": "http_cache_core",
"version": "1.1.1",
@@ -656,16 +714,6 @@
"unicode"
]
},
{
"name": "http",
"version": "1.5.0-beta.2",
"dependencies": [
"async",
"http_parser",
"meta",
"web"
]
},
{
"name": "dart_earcut",
"version": "1.2.0",
@@ -701,6 +749,26 @@
"version": "2.7.0",
"dependencies": []
},
{
"name": "image_picker_macos",
"version": "0.2.1+2",
"dependencies": [
"file_selector_macos",
"file_selector_platform_interface",
"flutter",
"image_picker_platform_interface"
]
},
{
"name": "image_picker_linux",
"version": "0.2.1+2",
"dependencies": [
"file_selector_linux",
"file_selector_platform_interface",
"flutter",
"image_picker_platform_interface"
]
},
{
"name": "csslib",
"version": "1.0.2",
@@ -731,6 +799,16 @@
"version": "1.1.1",
"dependencies": []
},
{
"name": "http",
"version": "1.5.0",
"dependencies": [
"async",
"http_parser",
"meta",
"web"
]
},
{
"name": "url_launcher_platform_interface",
"version": "2.3.2",
@@ -739,13 +817,6 @@
"plugin_platform_interface"
]
},
{
"name": "plugin_platform_interface",
"version": "2.1.8",
"dependencies": [
"meta"
]
},
{
"name": "path_provider_windows",
"version": "2.3.0",
@@ -955,6 +1026,33 @@
"url_launcher_platform_interface"
]
},
{
"name": "xdg_directories",
"version": "1.1.0",
"dependencies": [
"meta",
"path"
]
},
{
"name": "file_selector_linux",
"version": "0.9.3+2",
"dependencies": [
"cross_file",
"file_selector_platform_interface",
"flutter"
]
},
{
"name": "file_selector_platform_interface",
"version": "2.6.2",
"dependencies": [
"cross_file",
"flutter",
"http",
"plugin_platform_interface"
]
},
{
"name": "http_parser",
"version": "4.1.2",
@@ -965,14 +1063,6 @@
"typed_data"
]
},
{
"name": "xdg_directories",
"version": "1.1.0",
"dependencies": [
"meta",
"path"
]
},
{
"name": "crypto",
"version": "3.0.6",
@@ -1094,6 +1184,22 @@
"path"
]
},
{
"name": "image_picker_for_web",
"version": "3.0.6",
"dependencies": [
"flutter",
"flutter_web_plugins",
"image_picker_platform_interface",
"mime",
"web"
]
},
{
"name": "mime",
"version": "2.0.0",
"dependencies": []
},
{
"name": "platform",
"version": "3.1.6",
@@ -1151,6 +1257,15 @@
"web"
]
},
{
"name": "file_selector_macos",
"version": "0.9.4+3",
"dependencies": [
"cross_file",
"file_selector_platform_interface",
"flutter"
]
},
{
"name": "logger",
"version": "2.6.1",
@@ -1203,15 +1318,24 @@
"vector_graphics_codec"
]
},
{
"name": "path_provider_android",
"version": "2.2.17",
"dependencies": [
"flutter",
"path_provider_platform_interface"
]
},
{
"name": "dio",
"version": "5.8.0+1",
"version": "5.9.0",
"dependencies": [
"async",
"collection",
"dio_web_adapter",
"http_parser",
"meta",
"mime",
"path"
]
},
@@ -1225,17 +1349,9 @@
"web"
]
},
{
"name": "path_provider_android",
"version": "2.2.17",
"dependencies": [
"flutter",
"path_provider_platform_interface"
]
},
{
"name": "shared_preferences_android",
"version": "2.4.10",
"version": "2.4.11",
"dependencies": [
"flutter",
"shared_preferences_platform_interface"
@@ -1434,16 +1550,6 @@
"path"
]
},
{
"name": "built_value",
"version": "8.11.0",
"dependencies": [
"built_collection",
"collection",
"fixnum",
"meta"
]
},
{
"name": "watcher",
"version": "1.1.2",
@@ -1484,6 +1590,16 @@
"collection"
]
},
{
"name": "built_value",
"version": "8.11.1",
"dependencies": [
"built_collection",
"collection",
"fixnum",
"meta"
]
},
{
"name": "stream_transform",
"version": "2.1.1",
@@ -1540,11 +1656,6 @@
"web_socket_channel"
]
},
{
"name": "mime",
"version": "2.0.0",
"dependencies": []
},
{
"name": "js",
"version": "0.7.2",
@@ -1602,6 +1713,14 @@
"stream_channel"
]
},
{
"name": "image_picker_ios",
"version": "0.8.12+2",
"dependencies": [
"flutter",
"image_picker_platform_interface"
]
},
{
"name": "win32",
"version": "5.14.0",
@@ -1609,13 +1728,46 @@
"ffi"
]
},
{
"name": "file_selector_windows",
"version": "0.9.3+4",
"dependencies": [
"cross_file",
"file_selector_platform_interface",
"flutter"
]
},
{
"name": "cross_file",
"version": "0.3.4+2",
"dependencies": [
"meta",
"web"
]
},
{
"name": "url_launcher_android",
"version": "6.3.16",
"version": "6.3.17",
"dependencies": [
"flutter",
"url_launcher_platform_interface"
]
},
{
"name": "image_picker_android",
"version": "0.8.12+25",
"dependencies": [
"flutter",
"flutter_plugin_android_lifecycle",
"image_picker_platform_interface"
]
},
{
"name": "flutter_plugin_android_lifecycle",
"version": "2.0.29",
"dependencies": [
"flutter"
]
}
],
"configVersion": 1

File diff suppressed because one or more lines are too long

View File

@@ -96,6 +96,7 @@ GEOSECTOR est une solution complète développée en Flutter qui révolutionne l
| **Géolocalisation** | Geolocator | 10.1.0 | Services de localisation |
| **Chat** | MQTT5 Client | 4.2.0 | Messagerie temps réel |
| **UI** | Material Design 3 | Native | Composants d'interface |
| **Logging** | LoggerService | Custom | Logs conditionnels par env |
### 🏛️ Architecture en couches
@@ -423,6 +424,355 @@ Interface : Affichage utilisateur via ApiException.showError()
Network Errors : "Problème de connexion réseau"
Timeout : "Délai d'attente dépassé"
## 🔧 Pattern de gestion des erreurs API dans les Repositories
### 🎯 Problème à résoudre
Les messages d'erreur spécifiques de l'API (comme "Cet email est déjà utilisé") n'étaient pas affichés à l'utilisateur. À la place, un message générique "Erreur inattendue" apparaissait.
### 📝 Modifications requises
#### **1. ApiService - Conversion automatique des DioException**
Toutes les méthodes HTTP génériques doivent convertir les `DioException` en `ApiException` :
```dart
// ✅ CORRECT - ApiService avec conversion automatique
Future<Response> put(String path, {dynamic data}) async {
try {
return await _dio.put(path, data: data);
} on DioException catch (e) {
throw ApiException.fromDioException(e); // ← Extraction automatique du message
} catch (e) {
if (e is ApiException) rethrow;
throw ApiException('Erreur inattendue lors de la requête PUT', originalError: e);
}
}
```
Appliquer le même pattern pour `post()`, `get()`, `delete()`.
#### **2. Repository - Simplification de la gestion des erreurs**
```dart
// ❌ INCORRECT - Code inutile qui ne sera jamais exécuté
Future<bool> updateMembre(MembreModel membre) async {
try {
final response = await ApiService.instance.put('/users/${membre.id}', data: data);
if (response.statusCode == 200) {
await saveMembreBox(membre);
return true;
}
// ⚠️ CE CODE NE SERA JAMAIS ATTEINT car Dio lance une exception pour les codes d'erreur
if (response.data != null && response.data is Map<String, dynamic>) {
final responseData = response.data as Map<String, dynamic>;
if (responseData['status'] == 'error') {
throw Exception(responseData['message']);
}
}
return false;
} catch (e) {
rethrow;
}
}
```
```dart
// ✅ CORRECT - Code simplifié et fonctionnel
Future<bool> updateMembre(MembreModel membre) async {
try {
final response = await ApiService.instance.put('/users/${membre.id}', data: data);
// Si on arrive ici, c'est que la requête a réussi (200)
await saveMembreBox(membre);
return true;
} catch (e) {
// L'ApiException contient déjà le message extrait de l'API
rethrow; // Propager l'exception pour affichage
}
}
```
#### **3. Amélioration des logs (avec LoggerService)**
```dart
// ✅ CORRECT - Logs propres sans détails techniques
catch (e) {
// Ne pas logger les détails techniques de DioException
if (e is ApiException) {
LoggerService.error('Erreur lors de la mise à jour: ${e.message}');
} else {
LoggerService.error('Erreur lors de la mise à jour');
}
rethrow;
}
```
N'oubliez pas d'importer `ApiException` :
```dart
import 'package:geosector_app/core/utils/api_exception.dart';
```
### 🔄 Flux d'erreur corrigé
```mermaid
sequenceDiagram
participant API as API Server
participant Dio as Dio Client
participant AS as ApiService
participant AE as ApiException
participant R as Repository
participant UI as Interface
UI->>R: updateData()
R->>AS: put('/endpoint')
AS->>Dio: HTTP PUT
Dio->>API: Request
alt Code d'erreur (409, 400, etc.)
API-->>Dio: Error + JSON body
Note over API: {"status": "error",<br/>"message": "Message spécifique"}
Dio-->>AS: DioException
AS->>AE: fromDioException()
Note over AE: Extrait message<br/>depuis response.data
AE-->>AS: ApiException("Message spécifique")
AS-->>R: throw ApiException
R-->>UI: throw ApiException
UI->>UI: showError("Message spécifique")
else Succès (200, 201)
API-->>Dio: Success
Dio-->>AS: Response
AS-->>R: Response
R->>R: Sauvegarde Hive
R-->>UI: return true
end
```
### ✅ Checklist de migration
Pour chaque repository :
- [ ] Vérifier que l'ApiService convertit les DioException en ApiException
- [ ] Simplifier le code : supprimer les vérifications de statut après l'appel API
- [ ] Propager les exceptions avec `rethrow`
- [ ] Améliorer les logs pour ne pas afficher les détails techniques
- [ ] Importer `ApiException` si nécessaire
- [ ] Tester avec une erreur 409 pour vérifier l'affichage du message
### 📊 Résultat attendu
| Avant | Après |
|-------|-------|
| "Erreur inattendue" | "Cet email est déjà utilisé par un autre utilisateur" |
| Logs avec stack trace Dio | Message d'erreur simple et clair |
| Code complexe avec vérifications inutiles | Code simplifié et maintenable |
Cette approche garantit que tous les messages d'erreur de l'API sont correctement affichés à l'utilisateur, améliorant ainsi l'expérience utilisateur et facilitant le débogage.
## 📝 Service de Logging Intelligent
### 🎯 Vue d'ensemble
GEOSECTOR v2.0 implémente un **LoggerService centralisé** qui désactive automatiquement les logs de debug en production, optimisant ainsi les performances et la sécurité tout en facilitant le développement.
### 🔍 Détection automatique de l'environnement
Le LoggerService détecte automatiquement l'environnement d'exécution :
```dart
// Détection basée sur l'URL pour le web
if (currentUrl.contains('dapp.geosector.fr')) DEV
if (currentUrl.contains('rapp.geosector.fr')) REC
Sinon PROD
// Pour mobile/desktop : utilise kReleaseMode de Flutter
```
**Comportement par environnement :**
| Environnement | Logs Debug | Logs Erreur | Stack Traces |
|---------------|------------|-------------|--------------|
| **DEV** | ✅ Activés | ✅ Activés | ✅ Complètes |
| **REC** | ✅ Activés | ✅ Activés | ✅ Complètes |
| **PROD** | ❌ Désactivés | ✅ Activés | ❌ Masquées |
### 🛠️ API du LoggerService
#### **Méthodes principales**
```dart
// Remplacement direct de debugPrint
LoggerService.log('Message simple');
// Logs catégorisés avec emojis automatiques
LoggerService.info('Information'); //
LoggerService.success('Opération réussie'); // ✅
LoggerService.warning('Attention'); // ⚠️
LoggerService.error('Erreur', exception); // ❌ (toujours affiché)
LoggerService.debug('Debug', emoji: '🔧'); // 🔧
// Logs spécialisés
LoggerService.api('Requête API envoyée'); // 🔗
LoggerService.database('Box Hive ouverte'); // 💾
LoggerService.navigation('Route: /admin'); // 🧭
LoggerService.performance('Temps: 45ms'); // ⏱️
```
#### **Fonctionnalités avancées**
```dart
// Logs groupés pour améliorer la lisibilité
LoggerService.group('Traitement utilisateur', [
'Validation des données',
'Appel API',
'Sauvegarde locale',
'Notification envoyée'
]);
// Affiche :
// ┌─ Traitement utilisateur
// ├─ Validation des données
// ├─ Appel API
// ├─ Sauvegarde locale
// └─ Notification envoyée
// Log JSON formaté
LoggerService.json('Payload API', {
'id': 123,
'name': 'Test',
'active': true
});
// Affiche :
// 📋 Payload API:
// id: 123
// name: Test
// active: true
// Log conditionnel
LoggerService.conditional(
'Debug spécifique',
condition: user.role > 2
);
```
### 🔄 Migration depuis debugPrint
#### **Avant (debugPrint partout)**
```dart
debugPrint('🔄 Début de la création d\'un nouveau membre');
debugPrint('📤 Données envoyées à l\'API: $data');
debugPrint('❌ Erreur: $e');
```
#### **Après (LoggerService)**
```dart
LoggerService.info('Début de la création d\'un nouveau membre');
LoggerService.api('Données envoyées à l\'API: $data');
LoggerService.error('Erreur lors de la création', e);
```
### 📋 Utilisation avec les extensions
Pour une syntaxe encore plus concise, utilisez les extensions String :
```dart
// Import nécessaire
import 'package:geosector_app/core/services/logger_service.dart';
// Utilisation directe sur les strings
'Connexion réussie'.logSuccess();
'Erreur de validation'.logError(exception);
'Requête GET /users'.logApi();
'Box membres ouverte'.logDatabase();
```
### 🎯 Bonnes pratiques
#### **1. Catégorisation des logs**
```dart
// ✅ BON - Log catégorisé et clair
LoggerService.api('POST /users - Création membre');
LoggerService.database('Sauvegarde dans Box membres');
// ❌ MAUVAIS - Log générique sans contexte
debugPrint('Traitement en cours...');
```
#### **2. Gestion des erreurs**
```dart
try {
await operation();
LoggerService.success('Opération terminée');
} catch (e, stackTrace) {
// Les erreurs sont TOUJOURS loggées, même en PROD
LoggerService.error('Échec de l\'opération', e, stackTrace);
}
```
#### **3. Logs de performance**
```dart
final stopwatch = Stopwatch()..start();
await heavyOperation();
stopwatch.stop();
LoggerService.performance('Opération lourde: ${stopwatch.elapsedMilliseconds}ms');
```
### 🔒 Sécurité et performances
#### **Avantages en production**
1. **Sécurité** : Aucune information sensible exposée dans la console
2. **Performance** : Pas d'appels inutiles à debugPrint
3. **Taille** : Bundle JavaScript plus léger (tree shaking)
4. **Professionnalisme** : Console propre pour les utilisateurs finaux
#### **Conservation des logs d'erreur**
Les erreurs restent visibles en production pour faciliter le support :
```dart
// Toujours affiché, même en PROD
LoggerService.error('Erreur critique détectée', error);
// Mais sans la stack trace complète qui pourrait révéler
// des détails d'implémentation
```
### 📊 Impact sur le codebase
| Métrique | Avant | Après |
|----------|-------|-------|
| **Logs en PROD** | Tous visibles | Erreurs uniquement |
| **Performance web** | debugPrint actifs | Désactivés automatiquement |
| **Maintenance** | debugPrint dispersés | Service centralisé |
| **Lisibilité** | Emojis manuels | Catégorisation automatique |
### 🚀 Configuration et initialisation
Le LoggerService fonctionne automatiquement sans configuration :
```dart
// main.dart - Aucune initialisation requise
void main() async {
// LoggerService détecte automatiquement l'environnement
// via ApiService.getCurrentEnvironment()
runApp(GeosectorApp());
}
// Utilisation immédiate dans n'importe quel fichier
LoggerService.info('Application démarrée');
```
Cette architecture garantit un système de logging professionnel, sécurisé et performant, adapté aux besoins de développement tout en protégeant la production. 📝✨
## 🎯 Gestion des rôles
### Hiérarchie des permissions

766
app/TODO-APP.md Normal file
View File

@@ -0,0 +1,766 @@
# TODO-APP.md
## 📋 Liste des tâches à effectuer sur l'application GEOSECTOR
### 🔧 Migration du pattern de gestion des erreurs API
#### 🎯 Objectif
Appliquer le nouveau pattern de gestion des erreurs pour que tous les messages spécifiques de l'API soient correctement affichés aux utilisateurs (au lieu du message générique "Erreur inattendue").
---
### ✅ Repositories déjà migrés
- [x] **MembreRepository** (`lib/core/repositories/membre_repository.dart`)
- ✅ Conversion DioException → ApiException
- ✅ Simplification du code
- ✅ Logs avec LoggerService
---
### 📝 Repositories à migrer
#### 1. **UserRepository** (`lib/core/repositories/user_repository.dart`)
- [ ] Vérifier/ajouter l'import `ApiException`
- [ ] Simplifier `updateUser()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `syncUser()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `syncAllUsers()` - supprimer les vérifications de statut après l'appel API
- [ ] Remplacer `debugPrint` par `LoggerService`
- [ ] Améliorer la gestion des erreurs dans le bloc catch (ne pas logger DioException)
#### 2. **OperationRepository** (`lib/core/repositories/operation_repository.dart`)
- [ ] Vérifier/ajouter l'import `ApiException`
- [ ] Simplifier `createOperation()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `updateOperation()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `deleteOperation()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `activateOperation()` - supprimer les vérifications de statut après l'appel API
- [ ] Remplacer `debugPrint` par `LoggerService`
- [ ] Améliorer la gestion des erreurs dans le bloc catch (ne pas logger DioException)
#### 3. **PassageRepository** (`lib/core/repositories/passage_repository.dart`)
- [ ] Vérifier/ajouter l'import `ApiException`
- [ ] Simplifier `createPassage()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `updatePassage()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `updatePassageStatus()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `syncPassages()` - supprimer les vérifications de statut après l'appel API
- [ ] Remplacer `debugPrint` par `LoggerService`
- [ ] Améliorer la gestion des erreurs dans le bloc catch (ne pas logger DioException)
#### 4. **SectorRepository** (`lib/core/repositories/sector_repository.dart`)
- [ ] Vérifier/ajouter l'import `ApiException`
- [ ] Simplifier `createSector()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `updateSector()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `deleteSector()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `assignUserToSector()` - supprimer les vérifications de statut après l'appel API
- [ ] Remplacer `debugPrint` par `LoggerService`
- [ ] Améliorer la gestion des erreurs dans le bloc catch (ne pas logger DioException)
#### 5. **AmicaleRepository** (`lib/core/repositories/amicale_repository.dart`)
- [ ] Vérifier/ajouter l'import `ApiException`
- [ ] Simplifier `updateAmicale()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `createAmicale()` - supprimer les vérifications de statut après l'appel API
- [ ] Simplifier `syncAmicales()` - supprimer les vérifications de statut après l'appel API
- [ ] Remplacer `debugPrint` par `LoggerService`
- [ ] Améliorer la gestion des erreurs dans le bloc catch (ne pas logger DioException)
---
### 🛠️ Pattern à appliquer
#### ❌ Code à supprimer (ancien pattern)
```dart
try {
final response = await ApiService.instance.put('/endpoint', data: data);
if (response.statusCode == 200) {
// Traitement succès
return true;
}
// ⚠️ Ce code ne sera JAMAIS exécuté
if (response.data != null && response.data is Map<String, dynamic>) {
final responseData = response.data as Map<String, dynamic>;
if (responseData['status'] == 'error') {
throw Exception(responseData['message']);
}
}
return false;
} catch (e) {
debugPrint('Erreur: $e');
rethrow;
}
```
#### ✅ Nouveau pattern à utiliser
```dart
try {
final response = await ApiService.instance.put('/endpoint', data: data);
// Si on arrive ici, la requête a réussi (200/201)
// Traitement succès
return true;
} catch (e) {
// Log propre sans détails techniques
if (e is ApiException) {
LoggerService.error('Erreur lors de l\'opération: ${e.message}');
} else {
LoggerService.error('Erreur lors de l\'opération');
}
rethrow; // Propager pour affichage UI
}
```
---
### 📦 Imports nécessaires
Ajouter ces imports dans chaque repository si manquants :
```dart
import 'package:geosector_app/core/services/logger_service.dart';
import 'package:geosector_app/core/utils/api_exception.dart';
```
---
### 🧪 Tests de validation
Pour chaque repository migré, tester :
1. **Test d'erreur 409 (Conflict)** :
- Créer/modifier avec un email déjà existant
- Vérifier que le message "Cet email est déjà utilisé" s'affiche
2. **Test d'erreur 400 (Bad Request)** :
- Envoyer des données invalides
- Vérifier que le message d'erreur spécifique s'affiche
3. **Test d'erreur réseau** :
- Couper la connexion
- Vérifier que "Problème de connexion réseau" s'affiche
4. **Test de succès** :
- Opération normale
- Vérifier que tout fonctionne comme avant
---
### 📊 Critères de succès
- [ ] Tous les repositories utilisent le nouveau pattern
- [ ] Plus aucun `debugPrint` dans les repositories (remplacé par `LoggerService`)
- [ ] Les messages d'erreur spécifiques de l'API s'affichent correctement
- [ ] Les logs en production sont désactivés (sauf erreurs)
- [ ] Le code est plus simple et maintenable
---
### 🔍 Commandes utiles pour vérifier
```bash
# Rechercher les anciens patterns à remplacer
grep -r "response.statusCode ==" lib/core/repositories/
grep -r "responseData\['status'\] == 'error'" lib/core/repositories/
grep -r "debugPrint" lib/core/repositories/
# Vérifier les imports manquants
grep -L "LoggerService" lib/core/repositories/*.dart
grep -L "ApiException" lib/core/repositories/*.dart
```
---
### 📅 Priorité
1. **Haute** : UserRepository (utilisé partout)
2. **Haute** : OperationRepository (fonctionnalité critique)
3. **Moyenne** : PassageRepository (utilisé fréquemment)
4. **Moyenne** : AmicaleRepository (gestion administrative)
5. **Basse** : SectorRepository (moins critique)
---
### 📝 Notes
- Cette migration améliore l'UX en affichant des messages d'erreur clairs
- Le LoggerService désactive automatiquement les logs en production
- Le code devient plus maintenable et cohérent
- Documenter dans README-APP.md une fois terminé
---
## 🧪 Tests unitaires Flutter à implémenter
### 📁 Structure des tests
Le dossier `/test` doit contenir des tests unitaires pour valider le comportement de l'application.
### 📝 Tests prioritaires à créer
#### 1. **Tests des Repositories** (`test/repositories/`)
##### `test/repositories/membre_repository_test.dart`
- [ ] Test de création d'un membre avec succès
- [ ] Test de création avec email déjà existant (409)
- [ ] Test de mise à jour d'un membre
- [ ] Test de suppression d'un membre
- [ ] Test de gestion des erreurs réseau
- [ ] Test du cache Hive local
##### `test/repositories/user_repository_test.dart`
- [ ] Test de connexion réussie
- [ ] Test de connexion avec mauvais identifiants
- [ ] Test de mise à jour du profil utilisateur
- [ ] Test de synchronisation des données
- [ ] Test de gestion de session
##### `test/repositories/operation_repository_test.dart`
- [ ] Test de création d'opération
- [ ] Test d'activation/désactivation
- [ ] Test de récupération des opérations par amicale
- [ ] Test de suppression avec transfert de passages
#### 2. **Tests des Services** (`test/services/`)
##### `test/services/api_service_test.dart`
- [ ] Test de détection d'environnement (DEV/REC/PROD)
- [ ] Test de gestion des sessions
- [ ] Test de conversion DioException vers ApiException
- [ ] Test des différents codes d'erreur HTTP
- [ ] Test du retry automatique
##### `test/services/logger_service_test.dart`
- [ ] Test de désactivation des logs en PROD
- [ ] Test des différents niveaux de log
- [ ] Test du formatage des messages
##### `test/services/hive_service_test.dart`
- [ ] Test d'initialisation des boîtes Hive
- [ ] Test de sauvegarde et récupération
- [ ] Test de suppression de données
- [ ] Test de migration de schéma
#### 3. **Tests des Models** (`test/models/`)
##### `test/models/membre_model_test.dart`
- [ ] Test de sérialisation JSON
- [ ] Test de désérialisation JSON
- [ ] Test de conversion vers UserModel
- [ ] Test des valeurs par défaut
##### `test/models/amicale_model_test.dart`
- [ ] Test de sérialisation/désérialisation
- [ ] Test des nouveaux champs booléens (chk_mdp_manuel, chk_username_manuel)
- [ ] Test de validation des données
#### 4. **Tests des Widgets** (`test/widgets/`)
##### `test/widgets/user_form_test.dart`
- [ ] Test d'affichage conditionnel des champs (username/password)
- [ ] Test de validation du mot de passe (regex)
- [ ] Test de validation de l'username (pas d'espaces)
- [ ] Test de génération automatique d'username
- [ ] Test du mode création vs modification
##### `test/widgets/membre_table_widget_test.dart`
- [ ] Test d'affichage responsive (mobile vs web)
- [ ] Test de masquage des colonnes sur mobile
- [ ] Test des icônes de statut
- [ ] Test du tri et filtrage
#### 5. **Tests d'intégration** (`test/integration/`)
##### `test/integration/auth_flow_test.dart`
- [ ] Test du flow complet de connexion
- [ ] Test de persistance de session
- [ ] Test de déconnexion
- [ ] Test de redirection après expiration
##### `test/integration/membre_management_test.dart`
- [ ] Test de création complète d'un membre
- [ ] Test de modification avec validation
- [ ] Test de suppression avec transfert
- [ ] Test de gestion des erreurs
### 🛠️ Configuration des tests
#### Fichier `test/test_helpers.dart`
```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:dio/dio.dart';
import 'package:hive_flutter/hive_flutter.dart';
// Mocks nécessaires
class MockDio extends Mock implements Dio {}
class MockBox<T> extends Mock implements Box<T> {}
class MockApiService extends Mock implements ApiService {}
// Helper pour initialiser Hive en tests
Future<void> setupTestHive() async {
await Hive.initFlutter();
// Initialiser les boîtes de test
}
// Helper pour nettoyer après les tests
Future<void> tearDownTestHive() async {
await Hive.deleteFromDisk();
}
// Helper pour créer des données de test
class TestDataFactory {
static UserModel createTestUser() {
return UserModel(
id: 1,
username: 'test_user',
email: 'test@example.com',
// ...
);
}
static MembreModel createTestMembre() {
return MembreModel(
id: 1,
fkEntite: 100,
name: 'Test',
firstName: 'User',
// ...
);
}
}
```
### 📋 Commandes de test
```bash
# Lancer tous les tests
flutter test
# Lancer un test spécifique
flutter test test/repositories/membre_repository_test.dart
# Lancer avec coverage
flutter test --coverage
# Générer un rapport HTML de couverture
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html
```
### 🎯 Objectifs de couverture
- [ ] **Repositories** : 80% minimum
- [ ] **Services** : 90% minimum
- [ ] **Models** : 95% minimum
- [ ] **Widgets critiques** : 70% minimum
- [ ] **Couverture globale** : 75% minimum
### 📚 Dépendances de test à ajouter
Dans `pubspec.yaml` :
```yaml
dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.4.4
build_runner: ^2.4.8
test: ^1.25.2
flutter_lints: ^4.0.0
coverage: ^1.7.2
```
### 🔄 Intégration CI/CD
Ajouter dans le pipeline CI :
```yaml
test:
stage: test
script:
- flutter test --coverage
- genhtml coverage/lcov.info -o coverage/html
artifacts:
paths:
- coverage/
coverage: '/lines\.*: \d+\.\d+\%/'
```
### 📝 Bonnes pratiques
1. **Isolation** : Chaque test doit être indépendant
2. **Mocking** : Utiliser des mocks pour les dépendances externes
3. **Nommage** : Utiliser des noms descriptifs (test_should_xxx_when_yyy)
4. **AAA** : Suivre le pattern Arrange-Act-Assert
5. **Edge cases** : Tester les cas limites et erreurs
6. **Performance** : Les tests unitaires doivent être rapides (<100ms)
---
## 💬 Module Chat en ligne GEOSECTOR
### 📋 Vue d'ensemble
Le module chat est partiellement implémenté avec une architecture MQTT pour les notifications en temps réel. Il nécessite des développements supplémentaires pour être pleinement fonctionnel.
### 🏗️ Architecture existante
```
lib/chat/
├── models/ # ✅ Modèles créés avec Hive
│ ├── conversation_model.dart # Conversations (one-to-one, groupe, annonce)
│ ├── message_model.dart # Messages avec pièces jointes
│ ├── participant_model.dart # Participants aux conversations
│ └── audience_target_model.dart # Cibles pour les annonces
├── repositories/ # ⚠️ À compléter
│ └── chat_repository.dart # Logique métier du chat
├── services/ # ⚠️ Partiellement implémenté
│ ├── chat_api_service.dart # Communication avec l'API
│ ├── offline_queue_service.dart # File d'attente hors ligne
│ └── notifications/ # 🔧 MQTT configuré
│ ├── mqtt_notification_service.dart
│ └── mqtt_config.dart
├── widgets/ # ⚠️ À implémenter
│ ├── chat_screen.dart # Écran principal du chat
│ ├── conversations_list.dart # Liste des conversations
│ ├── message_bubble.dart # Bulles de messages
│ └── chat_input.dart # Zone de saisie
└── pages/ # ⚠️ À compléter
└── chat_page.dart # Page principale
```
### 📝 Tâches de développement Chat
#### 1. **Finalisation des modèles Hive**
- [ ] Compléter les adaptateurs Hive pour tous les modèles
- [ ] Ajouter la gestion des pièces jointes (images, documents)
- [ ] Implémenter le modèle `AnonymousUserModel` pour les utilisateurs temporaires
- [ ] Ajouter les timestamps et statuts de lecture
#### 2. **Repository ChatRepository**
- [ ] Implémenter `createConversation()` avec participants
- [ ] Implémenter `sendMessage()` avec queue hors ligne
- [ ] Implémenter `getConversations()` avec pagination
- [ ] Implémenter `getMessages()` avec lazy loading
- [ ] Ajouter la gestion des participants (ajout/suppression)
- [ ] Implémenter les annonces ciblées par groupe
#### 3. **Services**
- [ ] Compléter `ChatApiService` avec endpoints REST
- [ ] Implémenter la synchronisation bidirectionnelle
- [ ] Configurer `OfflineQueueService` pour messages en attente
- [ ] Implémenter le retry automatique avec exponential backoff
- [ ] Ajouter la compression des images avant envoi
#### 4. **Notifications MQTT**
- [ ] Installer et configurer Mosquitto dans le container Incus
- [ ] Configurer SSL/TLS pour MQTT (port 8883)
- [ ] Implémenter l'authentification par token JWT
- [ ] Créer les topics par utilisateur/groupe/conversation
- [ ] Implémenter le système de présence (online/offline/typing)
- [ ] Ajouter les ACLs pour sécuriser les topics
#### 5. **Interface utilisateur**
- [ ] Créer `ChatScreen` avec liste de messages
- [ ] Implémenter `ConversationsList` avec badges non-lus
- [ ] Designer `MessageBubble` (texte, images, documents)
- [ ] Créer `ChatInput` avec:
- [ ] Saisie de texte avec emoji picker
- [ ] Bouton d'envoi de fichiers
- [ ] Indicateur "en train d'écrire"
- [ ] Enregistrement vocal (optionnel)
- [ ] Ajouter les animations (apparition messages, typing indicator)
- [ ] Implémenter le swipe pour répondre
- [ ] Ajouter la recherche dans les conversations
#### 6. **Fonctionnalités avancées**
- [ ] Notifications push locales via `flutter_local_notifications`
- [ ] Chiffrement end-to-end des messages sensibles
- [ ] Réactions aux messages (emojis)
- [ ] Messages éphémères avec auto-suppression
- [ ] Partage de localisation en temps réel
- [ ] Appels audio/vidéo via WebRTC (phase 2)
### 🔧 Configuration backend requise
#### Base de données
```sql
-- Tables à créer (voir chat_tables.sql)
CREATE TABLE chat_conversations (
id INT PRIMARY KEY AUTO_INCREMENT,
type ENUM('one_to_one', 'group', 'announcement'),
created_by INT,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE chat_messages (
id INT PRIMARY KEY AUTO_INCREMENT,
conversation_id INT,
sender_id INT,
content TEXT,
type ENUM('text', 'image', 'file', 'location'),
status ENUM('sent', 'delivered', 'read'),
created_at TIMESTAMP
);
CREATE TABLE chat_participants (
conversation_id INT,
user_id INT,
role ENUM('admin', 'member'),
joined_at TIMESTAMP,
last_read_message_id INT
);
```
#### Configuration MQTT
```bash
# Installation Mosquitto
apt-get install mosquitto mosquitto-clients
# Configuration /etc/mosquitto/mosquitto.conf
listener 1883
listener 8883
cafile /etc/mosquitto/ca.crt
certfile /etc/mosquitto/server.crt
keyfile /etc/mosquitto/server.key
allow_anonymous false
password_file /etc/mosquitto/passwd
```
### 📦 Dépendances à ajouter
```yaml
dependencies:
# MQTT et notifications
mqtt5_client: ^4.0.0
flutter_local_notifications: ^17.0.0
# UI et UX
emoji_picker_flutter: ^2.0.0
cached_network_image: ^3.3.1
photo_view: ^0.14.0
file_picker: ^6.1.1
# Utilitaires
path_provider: ^2.1.2
image_picker: ^1.0.7
image: ^4.1.7 # Pour compression
intl: ^0.19.0 # Pour formatage dates
```
### 🧪 Tests à implémenter
- [ ] Tests unitaires des repositories
- [ ] Tests d'intégration MQTT
- [ ] Tests de performance (1000+ messages)
- [ ] Tests hors ligne/online
- [ ] Tests de sécurité (injection, XSS)
---
## 💳 Module de paiement Stripe
### 📋 Vue d'ensemble
Intégration de Stripe pour permettre aux amicales ayant activé `chk_stripe` d'accepter les paiements par carte bancaire avec une commission de 1.4%.
### 🎯 Objectifs
1. Permettre le paiement en ligne lors des passages
2. Gérer les comptes Stripe des amicales
3. Suivre les transactions et commissions
4. Offrir une expérience de paiement fluide
### 📝 Tâches de développement Stripe
#### 1. **Configuration initiale Stripe**
- [ ] Créer un compte Stripe Connect Platform
- [ ] Configurer les webhooks Stripe
- [ ] Mettre en place l'environnement de test (sandbox)
- [ ] Configurer les clés API (publishable/secret)
- [ ] Implémenter la gestion sécurisée des clés
#### 2. **Onboarding des amicales**
- [ ] Créer un workflow d'inscription Stripe pour les amicales
- [ ] Implémenter Stripe Connect Onboarding
- [ ] Gérer le KYC (Know Your Customer) requis par Stripe
- [ ] Stocker de manière sécurisée le `stripe_account_id`
- [ ] Créer une page de statut du compte Stripe
- [ ] Gérer les documents requis (RIB, statuts, etc.)
#### 3. **Modèles de données**
- [ ] Créer `StripeAccountModel` pour les comptes Connect
- [ ] Créer `PaymentIntentModel` pour les intentions de paiement
- [ ] Créer `TransactionModel` pour l'historique
- [ ] Ajouter les champs Stripe dans `PassageModel`
- [ ] Implémenter la table des commissions
#### 4. **Service StripeService**
```dart
class StripeService {
// Compte Connect
Future<String> createConnectAccount(AmicaleModel amicale);
Future<void> updateAccountStatus(String accountId);
Future<String> createAccountLink(String accountId);
// Paiements
Future<PaymentIntent> createPaymentIntent({
required double amount,
required String currency,
required String connectedAccountId,
});
Future<void> confirmPayment(String paymentIntentId);
Future<void> refundPayment(String paymentIntentId);
// Commissions
double calculateApplicationFee(double amount); // 1.4%
}
```
#### 5. **Interface de paiement dans PassageForm**
- [ ] Détecter si l'amicale accepte Stripe (`chk_stripe`)
- [ ] Ajouter l'option "Paiement par carte" dans le dropdown
- [ ] Intégrer Stripe Elements pour la saisie de carte
- [ ] Implémenter le flow de paiement 3D Secure
- [ ] Gérer les erreurs de paiement
- [ ] Afficher le reçu de paiement
- [ ] Permettre l'envoi du reçu par email
#### 6. **Widget StripePaymentSheet**
```dart
class StripePaymentSheet extends StatefulWidget {
final double amount;
final String currency;
final AmicaleModel amicale;
final Function(String) onSuccess;
final Function(String) onError;
// UI avec:
// - Montant à payer
// - Formulaire de carte (Stripe Elements)
// - Bouton de validation
// - Indicateur de traitement
// - Gestion 3D Secure
}
```
#### 7. **Tableau de bord financier**
- [ ] Page de suivi des transactions Stripe
- [ ] Graphiques des paiements par période
- [ ] Export des transactions (CSV/Excel)
- [ ] Calcul automatique des commissions
- [ ] Rapprochement bancaire
- [ ] Dashboard temps réel des paiements
#### 8. **Webhooks Stripe**
```php
// Backend PHP pour gérer les webhooks
class StripeWebhookHandler {
// Events à gérer:
- payment_intent.succeeded
- payment_intent.failed
- account.updated
- payout.created
- refund.created
}
```
#### 9. **Sécurité**
- [ ] Chiffrement des données sensibles
- [ ] Validation PCI DSS
- [ ] Audit trail des transactions
- [ ] Détection de fraude
- [ ] Rate limiting sur les API
- [ ] Tokenisation des cartes
#### 10. **Tests et conformité**
- [ ] Tests avec cartes de test Stripe
- [ ] Tests des cas d'erreur (carte refusée, etc.)
- [ ] Tests 3D Secure
- [ ] Tests de performance
- [ ] Conformité RGPD pour les données de paiement
- [ ] Documentation utilisateur
### 🔧 Configuration backend requise
#### Tables base de données
```sql
CREATE TABLE stripe_accounts (
id INT PRIMARY KEY AUTO_INCREMENT,
fk_entite INT NOT NULL,
stripe_account_id VARCHAR(255) ENCRYPTED,
status ENUM('pending', 'active', 'restricted'),
charges_enabled BOOLEAN DEFAULT FALSE,
payouts_enabled BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE stripe_transactions (
id INT PRIMARY KEY AUTO_INCREMENT,
fk_passage INT,
stripe_payment_intent_id VARCHAR(255),
amount DECIMAL(10,2),
currency VARCHAR(3),
status VARCHAR(50),
application_fee DECIMAL(10,2),
created_at TIMESTAMP
);
```
#### Variables d'environnement
```env
STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
STRIPE_APPLICATION_FEE_PERCENT=1.4
```
### 📦 Dépendances Stripe
```yaml
dependencies:
# Stripe
flutter_stripe: ^10.1.1
# Sécurité
flutter_secure_storage: ^9.0.0
# UI
shimmer: ^3.0.0 # Loading states
lottie: ^3.1.0 # Animations succès/échec
```
### 🚀 Roadmap d'implémentation
**Phase 1 (2 semaines)**
- Configuration Stripe Connect
- Onboarding basique des amicales
- Tests en sandbox
**Phase 2 (3 semaines)**
- Intégration dans PassageForm
- Gestion des paiements simples
- Webhooks essentiels
**Phase 3 (2 semaines)**
- Dashboard financier
- Export et rapports
- Tests complets
**Phase 4 (1 semaine)**
- Mise en production
- Monitoring
- Documentation
### 💰 Estimation des coûts
- **Stripe Connect** : 0.25 par compte actif/mois
- **Transactions** : 1.4% + 0.25 par transaction
- **Commission application** : 1.4% (reversée à GEOSECTOR)
- **Coût net pour l'amicale** : ~2.8% + 0.25 par transaction
---
**Date de création** : 2025-08-07
**Auteur** : Architecture Team
**Version** : 1.2.0

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
import 'package:geosector_app/core/services/theme_service.dart';
import 'package:go_router/go_router.dart';
@@ -44,6 +45,17 @@ class GeosectorApp extends StatelessWidget {
themeMode: themeService.themeMode,
routerConfig: _createRouter(),
debugShowCheckedModeBanner: false,
// Configuration des localisations pour le français
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('fr', 'FR'), // Français comme langue principale
Locale('en', 'US'), // Anglais en fallback
],
locale: const Locale('fr', 'FR'), // Forcer le français par défaut
);
},
);

View File

@@ -70,6 +70,15 @@ class AmicaleModel extends HiveObject {
@HiveField(21)
final DateTime? updatedAt;
@HiveField(22)
final bool chkMdpManuel;
@HiveField(23)
final bool chkUsernameManuel;
@HiveField(24)
final String? logoBase64; // Logo en base64 (data:image/png;base64,...)
AmicaleModel({
required this.id,
required this.name,
@@ -93,6 +102,9 @@ class AmicaleModel extends HiveObject {
this.chkStripe = false,
this.createdAt,
this.updatedAt,
this.chkMdpManuel = false,
this.chkUsernameManuel = false,
this.logoBase64,
});
// Factory pour convertir depuis JSON (API)
@@ -123,6 +135,17 @@ class AmicaleModel extends HiveObject {
json['chk_active'] == 1 || json['chk_active'] == true;
final bool chkStripe =
json['chk_stripe'] == 1 || json['chk_stripe'] == true;
final bool chkMdpManuel =
json['chk_mdp_manuel'] == 1 || json['chk_mdp_manuel'] == true;
final bool chkUsernameManuel =
json['chk_username_manuel'] == 1 || json['chk_username_manuel'] == true;
// Traiter le logo si présent
String? logoBase64;
if (json['logo'] != null && json['logo'] is Map) {
final logoData = json['logo'] as Map<String, dynamic>;
logoBase64 = logoData['data_url'] as String?;
}
// Traiter les dates si présentes
DateTime? createdAt;
@@ -166,6 +189,9 @@ class AmicaleModel extends HiveObject {
chkStripe: chkStripe,
createdAt: createdAt,
updatedAt: updatedAt,
chkMdpManuel: chkMdpManuel,
chkUsernameManuel: chkUsernameManuel,
logoBase64: logoBase64,
);
}
@@ -194,6 +220,9 @@ class AmicaleModel extends HiveObject {
'chk_stripe': chkStripe ? 1 : 0,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
'chk_mdp_manuel': chkMdpManuel ? 1 : 0,
'chk_username_manuel': chkUsernameManuel ? 1 : 0,
// Note: logoBase64 n'est pas envoyé via toJson (lecture seule depuis l'API)
};
}
@@ -220,6 +249,9 @@ class AmicaleModel extends HiveObject {
bool? chkStripe,
DateTime? createdAt,
DateTime? updatedAt,
bool? chkMdpManuel,
bool? chkUsernameManuel,
String? logoBase64,
}) {
return AmicaleModel(
id: id,
@@ -244,6 +276,9 @@ class AmicaleModel extends HiveObject {
chkStripe: chkStripe ?? this.chkStripe,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
chkMdpManuel: chkMdpManuel ?? this.chkMdpManuel,
chkUsernameManuel: chkUsernameManuel ?? this.chkUsernameManuel,
logoBase64: logoBase64 ?? this.logoBase64,
);
}
}

View File

@@ -39,13 +39,16 @@ class AmicaleModelAdapter extends TypeAdapter<AmicaleModel> {
chkStripe: fields[19] as bool,
createdAt: fields[20] as DateTime?,
updatedAt: fields[21] as DateTime?,
chkMdpManuel: fields[22] as bool,
chkUsernameManuel: fields[23] as bool,
logoBase64: fields[24] as String?,
);
}
@override
void write(BinaryWriter writer, AmicaleModel obj) {
writer
..writeByte(22)
..writeByte(25)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -89,7 +92,13 @@ class AmicaleModelAdapter extends TypeAdapter<AmicaleModel> {
..writeByte(20)
..write(obj.createdAt)
..writeByte(21)
..write(obj.updatedAt);
..write(obj.updatedAt)
..writeByte(22)
..write(obj.chkMdpManuel)
..writeByte(23)
..write(obj.chkUsernameManuel)
..writeByte(24)
..write(obj.logoBase64);
}
@override

View File

@@ -70,6 +70,12 @@ class ClientModel extends HiveObject {
@HiveField(21)
final DateTime? updatedAt;
@HiveField(22)
final bool? chkMdpManuel;
@HiveField(23)
final bool? chkUsernameManuel;
ClientModel({
required this.id,
required this.name,
@@ -93,6 +99,8 @@ class ClientModel extends HiveObject {
this.chkStripe,
this.createdAt,
this.updatedAt,
this.chkMdpManuel,
this.chkUsernameManuel,
});
// Factory pour convertir depuis JSON (API)
@@ -138,6 +146,8 @@ class ClientModel extends HiveObject {
chkStripe: json['chk_stripe'] == 1 || json['chk_stripe'] == true,
createdAt: json['created_at'] != null ? DateTime.parse(json['created_at']) : null,
updatedAt: json['updated_at'] != null ? DateTime.parse(json['updated_at']) : null,
chkMdpManuel: json['chk_mdp_manuel'] == 1 || json['chk_mdp_manuel'] == true,
chkUsernameManuel: json['chk_username_manuel'] == 1 || json['chk_username_manuel'] == true,
);
}
@@ -166,6 +176,8 @@ class ClientModel extends HiveObject {
'chk_stripe': chkStripe,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
'chk_mdp_manuel': chkMdpManuel,
'chk_username_manuel': chkUsernameManuel,
};
}
@@ -192,6 +204,8 @@ class ClientModel extends HiveObject {
bool? chkStripe,
DateTime? createdAt,
DateTime? updatedAt,
bool? chkMdpManuel,
bool? chkUsernameManuel,
}) {
return ClientModel(
id: id,
@@ -216,6 +230,8 @@ class ClientModel extends HiveObject {
chkStripe: chkStripe ?? this.chkStripe,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
chkMdpManuel: chkMdpManuel ?? this.chkMdpManuel,
chkUsernameManuel: chkUsernameManuel ?? this.chkUsernameManuel,
);
}
}

View File

@@ -39,13 +39,15 @@ class ClientModelAdapter extends TypeAdapter<ClientModel> {
chkStripe: fields[19] as bool?,
createdAt: fields[20] as DateTime?,
updatedAt: fields[21] as DateTime?,
chkMdpManuel: fields[22] as bool?,
chkUsernameManuel: fields[23] as bool?,
);
}
@override
void write(BinaryWriter writer, ClientModel obj) {
writer
..writeByte(22)
..writeByte(24)
..writeByte(0)
..write(obj.id)
..writeByte(1)
@@ -89,7 +91,11 @@ class ClientModelAdapter extends TypeAdapter<ClientModel> {
..writeByte(20)
..write(obj.createdAt)
..writeByte(21)
..write(obj.updatedAt);
..write(obj.updatedAt)
..writeByte(22)
..write(obj.chkMdpManuel)
..writeByte(23)
..write(obj.chkUsernameManuel);
}
@override

View File

@@ -3,6 +3,8 @@ import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/core/services/api_service.dart';
import 'package:geosector_app/core/services/logger_service.dart';
import 'package:geosector_app/core/utils/api_exception.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
class MembreRepository extends ChangeNotifier {
@@ -19,7 +21,7 @@ class MembreRepository extends ChangeNotifier {
throw Exception('La boîte ${AppKeys.membresBoxName} n\'est pas ouverte. Initialisez d\'abord l\'application.');
}
_cachedMembreBox = Hive.box<MembreModel>(AppKeys.membresBoxName);
debugPrint('MembreRepository: Box ${AppKeys.membresBoxName} mise en cache');
LoggerService.database('MembreRepository: Box ${AppKeys.membresBoxName} mise en cache');
}
return _cachedMembreBox!;
}
@@ -120,7 +122,7 @@ class MembreRepository extends ChangeNotifier {
// === MÉTHODES API ===
// Créer un membre via l'API
Future<MembreModel?> createMembre(MembreModel membre) async {
Future<MembreModel?> createMembre(MembreModel membre, {String? password}) async {
_isLoading = true;
notifyListeners();
@@ -130,48 +132,97 @@ class MembreRepository extends ChangeNotifier {
final data = userModel.toJson();
data.remove('id'); // L'API génère l'ID
data.remove('created_at'); // L'API génère created_at
// Supprimer les champs de session qui ne doivent pas être envoyés
data.remove('session_id');
data.remove('session_expiry');
data.remove('last_path');
// Convertir is_active en chk_active pour l'API
if (data.containsKey('is_active')) {
data['chk_active'] = data['is_active'] ? 1 : 0;
data.remove('is_active');
}
// Convertir role en fk_role pour l'API
if (data.containsKey('role')) {
data['fk_role'] = data['role'];
data.remove('role');
}
// Ajouter le mot de passe si fourni (sera ignoré par l'API si chk_mdp_manuel=0)
if (password != null && password.isNotEmpty) {
data['password'] = password;
debugPrint('🔑 Mot de passe inclus dans la requête');
} else {
debugPrint('⚠️ Pas de mot de passe fourni');
}
// Vérifier la présence de l'username (sera ignoré par l'API si chk_username_manuel=0)
if (data.containsKey('username') && data['username'] != null && data['username'].toString().isNotEmpty) {
debugPrint('👤 Username inclus dans la requête: ${data['username']}');
} else {
debugPrint('⚠️ Username manquant ou vide dans la requête');
// Si pas d'username, s'assurer qu'il n'est pas envoyé du tout
data.remove('username');
}
LoggerService.api('Données envoyées à l\'API pour création membre: $data');
// Appeler l'API users
final response = await ApiService.instance.post('/users', data: data);
if (response.statusCode == 201) {
// Extraire l'ID de la réponse API
final responseData = response.data;
debugPrint('🎉 Réponse API création utilisateur: $responseData');
// Vérifier d'abord si on a une réponse avec un statut d'erreur
if (response.data != null && response.data is Map<String, dynamic>) {
final responseData = response.data as Map<String, dynamic>;
// Si l'API retourne un status error, propager le message
if (responseData['status'] == 'error' && responseData['message'] != null) {
throw Exception(responseData['message']);
}
// Si succès avec code 201
if (response.statusCode == 201 && responseData['status'] == 'success') {
debugPrint('🎉 Réponse API création utilisateur: $responseData');
// L'API retourne {"status":"success","message":"Utilisateur créé avec succès","id":"10027748"}
final userId = responseData['id'] is String ? int.parse(responseData['id']) : responseData['id'] as int;
// L'API retourne {"status":"success","message":"Utilisateur créé avec succès","id":"10027748"}
final userId = responseData['id'] is String ? int.parse(responseData['id']) : responseData['id'] as int;
// Créer le nouveau membre avec l'ID retourné par l'API
final createdMember = MembreModel(
id: userId,
fkEntite: membre.fkEntite,
role: membre.role,
fkTitre: membre.fkTitre,
name: membre.name,
firstName: membre.firstName,
username: membre.username,
sectName: membre.sectName,
email: membre.email,
phone: membre.phone,
mobile: membre.mobile,
dateNaissance: membre.dateNaissance,
dateEmbauche: membre.dateEmbauche,
createdAt: DateTime.now(),
isActive: membre.isActive,
);
// Créer le nouveau membre avec l'ID retourné par l'API
final createdMember = MembreModel(
id: userId,
fkEntite: membre.fkEntite,
role: membre.role,
fkTitre: membre.fkTitre,
name: membre.name,
firstName: membre.firstName,
username: membre.username,
sectName: membre.sectName,
email: membre.email,
phone: membre.phone,
mobile: membre.mobile,
dateNaissance: membre.dateNaissance,
dateEmbauche: membre.dateEmbauche,
createdAt: DateTime.now(),
isActive: membre.isActive,
);
// Sauvegarder localement dans Hive (saveMembreBox gère déjà _resetCache)
await saveMembreBox(createdMember);
// Sauvegarder localement dans Hive (saveMembreBox gère déjà _resetCache)
await saveMembreBox(createdMember);
debugPrint('✅ Membre créé avec l\'ID: $userId et sauvegardé localement');
return createdMember;
debugPrint('✅ Membre créé avec l\'ID: $userId et sauvegardé localement');
return createdMember;
}
}
debugPrint('Échec création membre - Code: ${response.statusCode}');
return null;
LoggerService.error('Échec création membre - Code: ${response.statusCode}');
throw Exception('Erreur lors de la création du membre');
} catch (e) {
debugPrint('❌ Erreur lors de la création du membre: $e');
// Ne pas logger les détails techniques de DioException
if (e is ApiException) {
LoggerService.error('Erreur lors de la création du membre: ${e.message}');
} else {
LoggerService.error('Erreur lors de la création du membre');
}
rethrow; // Propager l'exception pour la gestion d'erreurs
} finally {
_isLoading = false;
@@ -180,27 +231,67 @@ class MembreRepository extends ChangeNotifier {
}
// Mettre à jour un membre via l'API
Future<bool> updateMembre(MembreModel membre) async {
Future<bool> updateMembre(MembreModel membre, {String? password}) async {
_isLoading = true;
notifyListeners();
try {
// Convertir en UserModel pour l'API
final userModel = membre.toUserModel();
final data = userModel.toJson();
// Supprimer les champs de session qui ne doivent pas être envoyés
data.remove('session_id');
data.remove('session_expiry');
data.remove('last_path');
// Convertir is_active en chk_active pour l'API
if (data.containsKey('is_active')) {
data['chk_active'] = data['is_active'] ? 1 : 0;
data.remove('is_active');
}
// Convertir role en fk_role pour l'API
if (data.containsKey('role')) {
data['fk_role'] = data['role'];
data.remove('role');
}
// Ajouter le mot de passe si fourni (sera ignoré par l'API si chk_mdp_manuel=0)
if (password != null && password.isNotEmpty) {
data['password'] = password;
debugPrint('🔑 Mot de passe inclus dans la requête de mise à jour');
} else {
debugPrint('⚠️ Pas de mot de passe fourni pour la mise à jour');
}
// L'username ne devrait pas être modifiable en update, mais on le garde pour l'API
if (data.containsKey('username') && data['username'] != null && data['username'].toString().isNotEmpty) {
debugPrint('👤 Username présent dans la requête de mise à jour: ${data['username']}');
} else {
debugPrint('⚠️ Username manquant dans la requête de mise à jour');
}
LoggerService.api('Données envoyées à l\'API pour mise à jour membre: $data');
// Appeler l'API users au lieu de membres
final response = await ApiService.instance.put('/users/${membre.id}', data: userModel.toJson());
final response = await ApiService.instance.put('/users/${membre.id}', data: data);
if (response.statusCode == 200) {
// Sauvegarder le membre mis à jour localement
await saveMembreBox(membre);
return true;
}
return false;
// Si on arrive ici, c'est que la requête a réussi (200)
// Sauvegarder le membre mis à jour localement
await saveMembreBox(membre);
return true;
} catch (e) {
debugPrint('Erreur lors de la mise à jour du membre: $e');
return false;
// Ne pas logger les détails techniques de DioException
if (e is ApiException) {
LoggerService.error('Erreur lors de la mise à jour du membre: ${e.message}');
} else {
LoggerService.error('Erreur lors de la mise à jour du membre');
}
// Si c'est une DioException, elle sera automatiquement convertie en ApiException
// par ApiException.fromDioException() qui extrait le message de l'API
rethrow; // Propager l'exception pour que le message d'erreur soit affiché
} finally {
_isLoading = false;
notifyListeners();
@@ -215,13 +306,26 @@ class MembreRepository extends ChangeNotifier {
try {
final response = await ApiService.instance.post('/users/$membreId/reset-password');
// Vérifier si on a une réponse avec un statut d'erreur
if (response.data != null && response.data is Map<String, dynamic>) {
final responseData = response.data as Map<String, dynamic>;
if (responseData['status'] == 'error' && responseData['message'] != null) {
throw Exception(responseData['message']);
}
}
if (response.statusCode == 200) {
return true;
}
return false;
throw Exception('Erreur lors de la réinitialisation du mot de passe');
} catch (e) {
debugPrint('Erreur lors de la réinitialisation du mot de passe: $e');
// Ne pas logger les détails techniques de DioException
if (e is ApiException) {
LoggerService.error('Erreur lors de la réinitialisation du mot de passe: ${e.message}');
} else {
LoggerService.error('Erreur lors de la réinitialisation du mot de passe');
}
rethrow;
} finally {
_isLoading = false;
@@ -254,19 +358,32 @@ class MembreRepository extends ChangeNotifier {
endpoint += '?${queryParams.join('&')}';
}
debugPrint('🔗 DELETE endpoint: $endpoint');
LoggerService.api('DELETE endpoint: $endpoint');
final response = await ApiService.instance.delete(endpoint);
// Vérifier si on a une réponse avec un statut d'erreur
if (response.data != null && response.data is Map<String, dynamic>) {
final responseData = response.data as Map<String, dynamic>;
if (responseData['status'] == 'error' && responseData['message'] != null) {
throw Exception(responseData['message']);
}
}
if (response.statusCode == 200 || response.statusCode == 204) {
// Supprimer le membre localement
await deleteMembreBox(membreId);
return true;
}
return false;
throw Exception('Erreur lors de la suppression du membre');
} catch (e) {
debugPrint('Erreur lors de la suppression du membre: $e');
// Ne pas logger les détails techniques de DioException
if (e is ApiException) {
LoggerService.error('Erreur lors de la suppression du membre: ${e.message}');
} else {
LoggerService.error('Erreur lors de la suppression du membre');
}
rethrow;
} finally {
_isLoading = false;

View File

@@ -15,12 +15,10 @@ import 'package:geosector_app/core/data/models/operation_model.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/presentation/widgets/loading_progress_overlay.dart';
import 'package:geosector_app/presentation/widgets/loading_spin_overlay.dart';
import 'package:geosector_app/core/models/loading_state.dart';
class UserRepository extends ChangeNotifier {
// Overlay pour afficher la progression du chargement
OverlayEntry? _progressOverlay;
bool _isLoading = false;
// Constructeur simplifié - plus d'injection d'ApiService
@@ -336,64 +334,39 @@ class UserRepository extends ChangeNotifier {
}
}
/// Connexion avec interface utilisateur et progression
Future<bool> loginWithUI(
/// Connexion avec spinner moderne (pour remplacer la barre de progression)
Future<bool> loginWithSpinner(
BuildContext context, String username, String password,
{required String type}) async {
OverlayEntry? spinOverlay;
try {
// Créer et afficher l'overlay de progression
_progressOverlay = LoadingProgressOverlayUtils.show(
// Déterminer le message selon le type de connexion
final message = type == 'admin'
? 'Connexion administrateur...'
: 'Connexion utilisateur...';
// Afficher le spinner moderne
spinOverlay = LoadingSpinOverlayUtils.show(
context: context,
message: 'Connexion en cours...',
progress: 0.0,
stepDescription: 'Préparation',
blurAmount: 5.0,
message: message,
blurAmount: 10.0,
showCard: true,
);
// Écouter les changements d'état du DataLoadingService
void listener() {
if (_progressOverlay != null) {
final loadingState = DataLoadingService.instance.loadingState;
LoadingProgressOverlayUtils.update(
overlayEntry: _progressOverlay!,
message: loadingState.message,
progress: loadingState.progress,
stepDescription: loadingState.stepDescription,
);
}
}
// Configurer le callback de progression
DataLoadingService.instance.setProgressCallback((_) => listener());
// Exécuter la connexion
final result = await login(username, password, type: type);
// Attendre un court instant pour que l'utilisateur voie le résultat
if (result) {
await Future.delayed(const Duration(milliseconds: 500));
} else {
await Future.delayed(const Duration(seconds: 2));
}
// Petit délai pour une meilleure UX
await Future.delayed(const Duration(milliseconds: 300));
// Supprimer l'overlay
if (_progressOverlay != null) {
_progressOverlay!.remove();
_progressOverlay = null;
}
// Nettoyer le callback
DataLoadingService.instance.setProgressCallback(null);
// Fermer le spinner
LoadingSpinOverlayUtils.hideSpecific(spinOverlay);
return result;
} catch (e) {
// En cas d'erreur, supprimer l'overlay
if (_progressOverlay != null) {
_progressOverlay!.remove();
_progressOverlay = null;
}
DataLoadingService.instance.setProgressCallback(null);
LoadingSpinOverlayUtils.hideSpecific(spinOverlay);
return false;
}
}

View File

@@ -18,6 +18,10 @@ class ApiService {
late final String _baseUrl;
late final String _appIdentifier;
String? _sessionId;
// Getters pour les propriétés (lecture seule)
String? get sessionId => _sessionId;
String get baseUrl => _baseUrl;
// Singleton thread-safe
static ApiService get instance {
@@ -142,8 +146,11 @@ class ApiService {
Future<Response> post(String path, {dynamic data}) async {
try {
return await _dio.post(path, data: data);
} on DioException catch (e) {
throw ApiException.fromDioException(e);
} catch (e) {
rethrow;
if (e is ApiException) rethrow;
throw ApiException('Erreur inattendue lors de la requête POST', originalError: e);
}
}
@@ -151,8 +158,11 @@ class ApiService {
Future<Response> get(String path, {Map<String, dynamic>? queryParameters}) async {
try {
return await _dio.get(path, queryParameters: queryParameters);
} on DioException catch (e) {
throw ApiException.fromDioException(e);
} catch (e) {
rethrow;
if (e is ApiException) rethrow;
throw ApiException('Erreur inattendue lors de la requête GET', originalError: e);
}
}
@@ -160,8 +170,11 @@ class ApiService {
Future<Response> put(String path, {dynamic data}) async {
try {
return await _dio.put(path, data: data);
} on DioException catch (e) {
throw ApiException.fromDioException(e);
} catch (e) {
rethrow;
if (e is ApiException) rethrow;
throw ApiException('Erreur inattendue lors de la requête PUT', originalError: e);
}
}
@@ -169,8 +182,81 @@ class ApiService {
Future<Response> delete(String path) async {
try {
return await _dio.delete(path);
} on DioException catch (e) {
throw ApiException.fromDioException(e);
} catch (e) {
rethrow;
if (e is ApiException) rethrow;
throw ApiException('Erreur inattendue lors de la requête DELETE', originalError: e);
}
}
// Méthode pour uploader un logo d'amicale
Future<Map<String, dynamic>> uploadLogo(int entiteId, dynamic imageFile) async {
try {
FormData formData;
// Gestion différente selon la plateforme (Web ou Mobile)
if (kIsWeb) {
// Pour le web, imageFile est un XFile
final bytes = await imageFile.readAsBytes();
// Vérification de la taille (5 Mo max)
const int maxSize = 5 * 1024 * 1024;
if (bytes.length > maxSize) {
throw ApiException(
'Le fichier est trop volumineux. Taille maximale: 5 Mo',
statusCode: 413
);
}
formData = FormData.fromMap({
'logo': MultipartFile.fromBytes(
bytes,
filename: imageFile.name,
),
});
} else {
// Pour mobile, imageFile est un File
final fileLength = await imageFile.length();
// Vérification de la taille (5 Mo max)
const int maxSize = 5 * 1024 * 1024;
if (fileLength > maxSize) {
throw ApiException(
'Le fichier est trop volumineux. Taille maximale: 5 Mo',
statusCode: 413
);
}
formData = FormData.fromMap({
'logo': await MultipartFile.fromFile(
imageFile.path,
filename: imageFile.path.split('/').last,
),
});
}
final response = await _dio.post(
'/entites/$entiteId/logo',
data: formData,
options: Options(
headers: {
'Content-Type': 'multipart/form-data',
},
),
);
if (response.statusCode == 200 || response.statusCode == 201) {
return response.data;
} else {
throw ApiException('Erreur lors de l\'upload du logo',
statusCode: response.statusCode);
}
} on DioException catch (e) {
throw ApiException.fromDioException(e);
} catch (e) {
if (e is ApiException) rethrow;
throw ApiException('Erreur inattendue lors de l\'upload du logo', originalError: e);
}
}

View File

@@ -0,0 +1,163 @@
import 'package:flutter/foundation.dart';
import 'package:geosector_app/core/services/api_service.dart';
/// Service de logging centralisé qui désactive automatiquement les logs en production
class LoggerService {
static LoggerService? _instance;
static bool? _isProduction;
// Singleton
static LoggerService get instance {
_instance ??= LoggerService._internal();
return _instance!;
}
LoggerService._internal();
/// Détermine si on est en production
static bool get isProduction {
if (_isProduction != null) return _isProduction!;
try {
// Vérifier si ApiService est initialisé
final env = ApiService.instance.getCurrentEnvironment();
_isProduction = env == 'PROD';
} catch (e) {
// Si ApiService n'est pas encore initialisé, utiliser kReleaseMode
_isProduction = kReleaseMode;
}
return _isProduction!;
}
/// Réinitialiser le cache de l'environnement (utile pour les tests)
static void resetEnvironmentCache() {
_isProduction = null;
}
/// Log simple (remplace debugPrint)
static void log(String message, {String? prefix}) {
if (!isProduction) {
if (prefix != null) {
debugPrint('$prefix $message');
} else {
debugPrint(message);
}
}
}
/// Log d'information
static void info(String message) {
if (!isProduction) {
debugPrint(' $message');
}
}
/// Log de succès
static void success(String message) {
if (!isProduction) {
debugPrint('$message');
}
}
/// Log d'avertissement
static void warning(String message) {
if (!isProduction) {
debugPrint('⚠️ $message');
}
}
/// Log d'erreur (toujours affiché même en production pour le debugging)
static void error(String message, [dynamic error, StackTrace? stackTrace]) {
// Les erreurs sont toujours loggées, même en production
debugPrint('$message');
if (error != null) {
debugPrint(' Error: $error');
}
if (stackTrace != null && !isProduction) {
// La stack trace n'est affichée qu'en DEV/REC
debugPrint(' Stack trace:\n$stackTrace');
}
}
/// Log de debug (avec emoji personnalisé)
static void debug(String message, {String emoji = '🔧'}) {
if (!isProduction) {
debugPrint('$emoji $message');
}
}
/// Log de requête API
static void api(String message) {
if (!isProduction) {
debugPrint('🔗 $message');
}
}
/// Log de base de données
static void database(String message) {
if (!isProduction) {
debugPrint('💾 $message');
}
}
/// Log de navigation
static void navigation(String message) {
if (!isProduction) {
debugPrint('🧭 $message');
}
}
/// Log de performance/timing
static void performance(String message) {
if (!isProduction) {
debugPrint('⏱️ $message');
}
}
/// Log conditionnel basé sur un flag custom
static void conditional(String message, {required bool condition}) {
if (!isProduction && condition) {
debugPrint(message);
}
}
/// Groupe de logs (pour regrouper visuellement des logs liés)
static void group(String title, List<String> messages) {
if (!isProduction) {
debugPrint('┌─ $title');
for (int i = 0; i < messages.length; i++) {
if (i == messages.length - 1) {
debugPrint('└─ ${messages[i]}');
} else {
debugPrint('├─ ${messages[i]}');
}
}
}
}
/// Log d'objet JSON (formaté)
static void json(String label, Map<String, dynamic> data) {
if (!isProduction) {
debugPrint('📋 $label:');
data.forEach((key, value) {
debugPrint(' $key: $value');
});
}
}
}
/// Extension pour faciliter l'utilisation
extension LoggerExtension on String {
void log({String? prefix}) => LoggerService.log(this, prefix: prefix);
void logInfo() => LoggerService.info(this);
void logSuccess() => LoggerService.success(this);
void logWarning() => LoggerService.warning(this);
void logError([dynamic error, StackTrace? stackTrace]) =>
LoggerService.error(this, error, stackTrace);
void logDebug({String emoji = '🔧'}) => LoggerService.debug(this, emoji: emoji);
void logApi() => LoggerService.api(this);
void logDatabase() => LoggerService.database(this);
void logNavigation() => LoggerService.navigation(this);
void logPerformance() => LoggerService.performance(this);
}

View File

@@ -97,6 +97,9 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
}
void _handleEditMembre(MembreModel membre) {
// Récupérer l'amicale actuelle
final amicale = widget.amicaleRepository.getUserAmicale(_currentUser!.fkEntite!);
showDialog(
context: context,
builder: (context) => UserFormDialog(
@@ -104,8 +107,9 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
user: membre.toUserModel(),
showRoleSelector: true,
showActiveCheckbox: true, // Activer la checkbox
allowUsernameEdit: true, // Permettre l'édition du username
// allowSectNameEdit sera automatiquement true via UserForm
allowUsernameEdit: amicale?.chkUsernameManuel == true, // Conditionnel selon amicale
amicale: amicale, // Passer l'amicale
isAdmin: true, // Car on est dans la page admin
availableRoles: const [
RoleOption(
value: 1,
@@ -118,23 +122,22 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
description: 'Peut gérer l\'amicale et ses membres',
),
],
onSubmit: (updatedUser) async {
onSubmit: (updatedUser, {String? password}) async {
try {
// Convertir le UserModel mis à jour vers MembreModel
final updatedMembre =
MembreModel.fromUserModel(updatedUser, membre);
// Utiliser directement updateMembre qui passe par l'API /users
final success =
await widget.membreRepository.updateMembre(updatedMembre);
final success = await widget.membreRepository.updateMembre(
updatedMembre,
password: password,
);
if (success && mounted) {
Navigator.of(context).pop();
ApiException.showSuccess(context,
'Membre ${updatedMembre.firstName} ${updatedMembre.name} mis à jour');
} else if (!success && mounted) {
ApiException.showError(
context, Exception('Erreur lors de la mise à jour'));
}
} catch (e) {
debugPrint('❌ Erreur mise à jour membre: $e');
@@ -557,9 +560,6 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
if (success && mounted) {
ApiException.showSuccess(context,
'Membre ${membre.firstName} ${membre.name} désactivé avec succès');
} else if (mounted) {
ApiException.showError(
context, Exception('Erreur lors de la désactivation'));
}
} catch (e) {
if (mounted) {
@@ -571,6 +571,9 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
void _handleAddMembre() {
if (_currentUser?.fkEntite == null) return;
// Récupérer l'amicale actuelle
final amicale = widget.amicaleRepository.getUserAmicale(_currentUser!.fkEntite!);
// Créer un UserModel vide avec les valeurs par défaut
final newUser = UserModel(
id: 0, // ID temporaire pour nouveau membre
@@ -596,7 +599,9 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
user: newUser,
showRoleSelector: true,
showActiveCheckbox: true,
allowUsernameEdit: true,
allowUsernameEdit: amicale?.chkUsernameManuel == true, // Conditionnel selon amicale
amicale: amicale, // Passer l'amicale
isAdmin: true, // Car on est dans la page admin
availableRoles: const [
RoleOption(
value: 1,
@@ -609,7 +614,7 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
description: 'Peut gérer l\'amicale et ses membres',
),
],
onSubmit: (newUserData) async {
onSubmit: (newUserData, {String? password}) async {
try {
// Créer un nouveau MembreModel directement
final newMembre = MembreModel(
@@ -631,8 +636,10 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
);
// Créer le membre via l'API (retourne maintenant le membre créé)
final createdMembre =
await widget.membreRepository.createMembre(newMembre);
final createdMembre = await widget.membreRepository.createMembre(
newMembre,
password: password,
);
if (createdMembre != null && mounted) {
// Fermer le dialog

View File

@@ -434,7 +434,8 @@ class _LoginPageState extends State<LoginPage> {
'Login: Tentative avec type: $_loginType');
final success =
await userRepository.login(
await userRepository.loginWithSpinner(
context,
_usernameController.text.trim(),
_passwordController.text,
type: _loginType,
@@ -575,9 +576,9 @@ class _LoginPageState extends State<LoginPage> {
print(
'Login: Tentative avec type: $_loginType');
// Utiliser directement userRepository avec l'overlay de chargement
// Utiliser le nouveau spinner moderne pour la connexion
final success = await userRepository
.loginWithUI(
.loginWithSpinner(
context,
_usernameController.text.trim(),
_passwordController.text,

View File

@@ -1,11 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:typed_data';
import 'dart:convert';
import 'package:flutter_map/flutter_map.dart';
import 'package:geosector_app/core/data/models/amicale_model.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/services/api_service.dart';
import 'package:geosector_app/presentation/widgets/mapbox_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io';
import 'package:geosector_app/presentation/widgets/loading_spin_overlay.dart';
import 'custom_text_field.dart';
class AmicaleForm extends StatefulWidget {
@@ -52,6 +58,13 @@ class _AmicaleFormState extends State<AmicaleForm> {
bool _chkAcceptSms = false;
bool _chkActive = true;
bool _chkStripe = false;
bool _chkMdpManuel = false;
bool _chkUsernameManuel = false;
// Pour l'upload du logo
final ImagePicker _picker = ImagePicker();
XFile? _selectedImage;
String? _logoUrl;
@override
void initState() {
@@ -78,6 +91,10 @@ class _AmicaleFormState extends State<AmicaleForm> {
_chkAcceptSms = amicale?.chkAcceptSms ?? false;
_chkActive = amicale?.chkActive ?? true;
_chkStripe = amicale?.chkStripe ?? false;
_chkMdpManuel = amicale?.chkMdpManuel ?? false;
_chkUsernameManuel = amicale?.chkUsernameManuel ?? false;
// Note : Le logo sera chargé dynamiquement depuis l'API
}
@override
@@ -133,6 +150,8 @@ class _AmicaleFormState extends State<AmicaleForm> {
'chk_copie_mail_recu': amicale.chkCopieMailRecu ? 1 : 0,
'chk_accept_sms': amicale.chkAcceptSms ? 1 : 0,
'chk_stripe': amicale.chkStripe ? 1 : 0,
'chk_mdp_manuel': amicale.chkMdpManuel ? 1 : 0,
'chk_username_manuel': amicale.chkUsernameManuel ? 1 : 0,
};
// Ajouter les champs réservés aux administrateurs si l'utilisateur est admin
@@ -232,6 +251,115 @@ class _AmicaleFormState extends State<AmicaleForm> {
}
}
// Méthode pour sélectionner une image
Future<void> _selectImage() async {
try {
final XFile? image = await _picker.pickImage(
source: ImageSource.gallery,
maxWidth: 1024,
maxHeight: 1024,
imageQuality: 85,
);
if (image != null) {
// Vérifier la taille du fichier (limite 5 Mo)
final int fileSize = await image.length();
const int maxSize = 5 * 1024 * 1024; // 5 Mo en octets
if (fileSize > maxSize) {
// Fichier trop volumineux
final double sizeMB = fileSize / (1024 * 1024);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Le fichier est trop volumineux (${sizeMB.toStringAsFixed(2)} Mo). '
'La taille maximale autorisée est de 5 Mo.',
),
backgroundColor: Colors.orange,
duration: const Duration(seconds: 5),
),
);
}
return;
}
setState(() {
_selectedImage = image;
});
// Upload immédiatement après sélection
if (widget.amicale?.id != null) {
await _uploadLogo();
}
}
} catch (e) {
debugPrint('Erreur lors de la sélection de l\'image: $e');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur lors de la sélection de l\'image: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
// Méthode pour uploader le logo
Future<void> _uploadLogo() async {
if (_selectedImage == null || widget.amicale?.id == null) return;
OverlayEntry? spinOverlay;
try {
// Afficher le spinner
spinOverlay = LoadingSpinOverlayUtils.show(
context: context,
message: 'Upload du logo en cours...',
blurAmount: 10.0,
showCard: true,
);
// Appeler l'API pour uploader le logo
final response = await widget.apiService?.uploadLogo(
widget.amicale!.id,
_selectedImage!,
);
if (response != null && response['status'] == 'success') {
// Succès
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Logo uploadé avec succès'),
backgroundColor: Colors.green,
),
);
}
// Mettre à jour l'amicale avec le nouveau logo en base64
// Note : Le serveur devrait aussi mettre à jour le logo dans la session
// Pour l'instant on garde l'image sélectionnée en preview
setState(() {
// L'image reste en preview jusqu'au prochain login
});
}
} catch (e) {
debugPrint('Erreur lors de l\'upload du logo: $e');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur lors de l\'upload: ${e.toString()}'),
backgroundColor: Colors.red,
),
);
}
} finally {
// Fermer le spinner
LoadingSpinOverlayUtils.hideSpecific(spinOverlay);
}
}
void _submitForm() {
debugPrint('🔧 _submitForm appelée');
@@ -271,6 +399,8 @@ class _AmicaleFormState extends State<AmicaleForm> {
chkAcceptSms: _chkAcceptSms,
chkActive: _chkActive,
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
) ??
AmicaleModel(
id: 0, // Sera remplacé par l'API
@@ -291,6 +421,9 @@ class _AmicaleFormState extends State<AmicaleForm> {
chkCopieMailRecu: _chkCopieMailRecu,
chkAcceptSms: _chkAcceptSms,
chkActive: _chkActive,
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
);
debugPrint('🔧 AmicaleModel créé: ${amicale.name}');
@@ -305,6 +438,10 @@ class _AmicaleFormState extends State<AmicaleForm> {
// Construire la section logo
Widget _buildLogoSection() {
// Vérifier si on est admin d'amicale (role 2)
final userRole = widget.userRepository.getUserRole();
final canUploadLogo = userRole == 2 && !widget.readOnly;
return Container(
width: 150,
height: 150,
@@ -323,40 +460,40 @@ class _AmicaleFormState extends State<AmicaleForm> {
borderRadius: BorderRadius.circular(8),
child: Stack(
children: [
// Image par défaut
// Afficher l'image sélectionnée, ou le logo depuis l'API, ou l'image par défaut
Center(
child: Image.asset(
'assets/images/logo_recu.png',
width: 150,
height: 150,
fit: BoxFit.contain,
),
child: _buildLogoImage(),
),
// Overlay pour indiquer que l'image est modifiable (si non en lecture seule)
if (!widget.readOnly)
// Overlay pour indiquer que l'image est modifiable (si admin d'amicale)
if (canUploadLogo)
Positioned.fill(
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
// TODO: Implémenter la sélection d'image
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Fonctionnalité de modification du logo à venir'),
),
);
},
onTap: _selectImage,
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.1),
color: Colors.black.withOpacity(0.3),
),
child: const Center(
child: Icon(
Icons.camera_alt,
color: Colors.white,
size: 40,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.camera_alt,
color: Colors.white,
size: 32,
),
SizedBox(height: 4),
Text(
'Modifier',
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
@@ -367,6 +504,102 @@ class _AmicaleFormState extends State<AmicaleForm> {
),
);
}
// Méthode pour construire l'image du logo
Widget _buildLogoImage() {
// 1. Si une image a été sélectionnée localement (preview)
if (_selectedImage != null) {
if (kIsWeb) {
return FutureBuilder<Uint8List>(
future: _selectedImage!.readAsBytes(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Image.memory(
snapshot.data!,
width: 150,
height: 150,
fit: BoxFit.contain,
);
}
return const CircularProgressIndicator();
},
);
} else {
return Image.file(
File(_selectedImage!.path),
width: 150,
height: 150,
fit: BoxFit.contain,
);
}
}
// 2. Si l'amicale a un logo en base64 stocké dans Hive
if (widget.amicale?.logoBase64 != null && widget.amicale!.logoBase64!.isNotEmpty) {
try {
// Le logoBase64 contient déjà le data URL complet (data:image/png;base64,...)
final dataUrl = widget.amicale!.logoBase64!;
// Extraire le base64 du data URL
final base64Data = dataUrl.split(',').last;
final bytes = base64Decode(base64Data);
return Image.memory(
bytes,
width: 150,
height: 150,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
debugPrint('Erreur affichage logo base64: $error');
// En cas d'erreur, essayer l'API
return _buildLogoFromApi();
},
);
} catch (e) {
debugPrint('Erreur décodage base64: $e');
// En cas d'erreur, essayer l'API
return _buildLogoFromApi();
}
}
// 3. Sinon, essayer de charger depuis l'API
return _buildLogoFromApi();
}
// Méthode pour charger le logo depuis l'API
Widget _buildLogoFromApi() {
if (widget.amicale?.id != null && widget.apiService != null) {
// Construire l'URL complète du logo
final logoUrl = '${widget.apiService!.baseUrl}/entites/${widget.amicale!.id}/logo';
return Image.network(
logoUrl,
width: 150,
height: 150,
fit: BoxFit.contain,
headers: {
'Authorization': 'Bearer ${widget.apiService!.sessionId ?? ""}',
},
errorBuilder: (context, error, stackTrace) {
// En cas d'erreur, afficher l'image par défaut
return Image.asset(
'assets/images/logo_recu.png',
width: 150,
height: 150,
fit: BoxFit.contain,
);
},
);
}
// Par défaut, afficher l'image locale
return Image.asset(
'assets/images/logo_recu.png',
width: 150,
height: 150,
fit: BoxFit.contain,
);
}
// Construire la minimap
Widget _buildMiniMap() {
@@ -825,59 +1058,106 @@ class _AmicaleFormState extends State<AmicaleForm> {
),
const SizedBox(height: 8),
// Checkbox Demo
_buildCheckboxOption(
label: "Mode démo",
value: _chkDemo,
onChanged: restrictedFieldsReadOnly
? null
: (value) {
setState(() {
_chkDemo = value!;
});
},
),
const SizedBox(height: 8),
// Options organisées sur 2 colonnes
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Colonne de gauche
Expanded(
child: Column(
children: [
// Checkbox Demo
_buildCheckboxOption(
label: "Mode démo",
value: _chkDemo,
onChanged: restrictedFieldsReadOnly
? null
: (value) {
setState(() {
_chkDemo = value!;
});
},
),
const SizedBox(height: 8),
// Checkbox Copie Mail Reçu
_buildCheckboxOption(
label: "Copie des mails reçus",
value: _chkCopieMailRecu,
onChanged: widget.readOnly
? null
: (value) {
setState(() {
_chkCopieMailRecu = value!;
});
},
),
const SizedBox(height: 8),
// Checkbox Copie Mail Reçu
_buildCheckboxOption(
label: "Copie des mails reçus",
value: _chkCopieMailRecu,
onChanged: widget.readOnly
? null
: (value) {
setState(() {
_chkCopieMailRecu = value!;
});
},
),
const SizedBox(height: 8),
// Checkbox Accept SMS
_buildCheckboxOption(
label: "Accepte les SMS",
value: _chkAcceptSms,
onChanged: widget.readOnly
? null
: (value) {
setState(() {
_chkAcceptSms = value!;
});
},
),
const SizedBox(height: 8),
// Checkbox Accept SMS
_buildCheckboxOption(
label: "Accepte les SMS",
value: _chkAcceptSms,
onChanged: widget.readOnly
? null
: (value) {
setState(() {
_chkAcceptSms = value!;
});
},
),
],
),
),
const SizedBox(width: 32),
// Colonne de droite
Expanded(
child: Column(
children: [
// Checkbox Active
_buildCheckboxOption(
label: "Actif",
value: _chkActive,
onChanged: restrictedFieldsReadOnly
? null
: (value) {
setState(() {
_chkActive = value!;
});
},
),
const SizedBox(height: 8),
// Checkbox Active
_buildCheckboxOption(
label: "Actif",
value: _chkActive,
onChanged: restrictedFieldsReadOnly
? null
: (value) {
setState(() {
_chkActive = value!;
});
},
// Checkbox Mot de passe manuel
_buildCheckboxOption(
label: "Saisie manuelle des mots de passe",
value: _chkMdpManuel,
onChanged: widget.readOnly
? null
: (value) {
setState(() {
_chkMdpManuel = value!;
});
},
),
const SizedBox(height: 8),
// Checkbox Username manuel
_buildCheckboxOption(
label: "Saisie manuelle des identifiants",
value: _chkUsernameManuel,
onChanged: widget.readOnly
? null
: (value) {
setState(() {
_chkUsernameManuel = value!;
});
},
),
],
),
),
],
),
const SizedBox(height: 25),

View File

@@ -171,9 +171,10 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {
user: user,
readOnly: false,
showRoleSelector: false,
onSubmit: (updatedUser) async {
onSubmit: (updatedUser, {String? password}) async {
try {
// Sauvegarder les modifications de l'utilisateur
// Note: password est ignoré ici car l'utilisateur normal ne peut pas changer son mot de passe
await userRepository.updateUser(updatedUser);
if (context.mounted) {

View File

@@ -1,199 +0,0 @@
import 'package:flutter/material.dart';
import 'dart:ui';
/// Widget d'overlay de chargement amélioré qui affiche une barre de progression
/// avec un effet de flou sur l'arrière-plan et un message détaillé sur l'étape en cours
class LoadingProgressOverlay extends StatefulWidget {
final String? message;
final double progress;
final String? stepDescription;
final Color backgroundColor;
final Color progressColor;
final Color textColor;
final double blurAmount;
final bool showPercentage;
const LoadingProgressOverlay({
super.key,
this.message,
required this.progress,
this.stepDescription,
this.backgroundColor = Colors.black54,
this.progressColor = Colors.white,
this.textColor = Colors.white,
this.blurAmount = 5.0,
this.showPercentage = true,
});
@override
State<LoadingProgressOverlay> createState() => _LoadingProgressOverlayState();
}
class _LoadingProgressOverlayState extends State<LoadingProgressOverlay>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _progressAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_progressAnimation = Tween<double>(begin: 0, end: widget.progress).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
_animationController.forward();
}
@override
void didUpdateWidget(LoadingProgressOverlay oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.progress != widget.progress) {
_progressAnimation = Tween<double>(
begin: oldWidget.progress,
end: widget.progress,
).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
_animationController.reset();
_animationController.forward();
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BackdropFilter(
filter: ImageFilter.blur(
sigmaX: widget.blurAmount, sigmaY: widget.blurAmount),
child: Container(
color: widget.backgroundColor,
child: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.85,
padding: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.9),
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.black45,
blurRadius: 15,
spreadRadius: 5,
offset: Offset(0, 5),
),
],
border: Border.all(
color: Colors.white.withOpacity(0.1),
width: 1.5,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.message != null) ...[
Text(
widget.message!,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: widget.textColor,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
],
AnimatedBuilder(
animation: _progressAnimation,
builder: (context, child) {
return Column(
children: [
LinearProgressIndicator(
value: _progressAnimation.value,
backgroundColor:
widget.progressColor.withOpacity(0.3),
valueColor: AlwaysStoppedAnimation<Color>(
widget.progressColor),
minHeight: 15,
borderRadius: BorderRadius.circular(8),
),
if (widget.showPercentage) ...[
const SizedBox(height: 8),
Text(
'${(_progressAnimation.value * 100).toInt()}%',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: widget.textColor,
letterSpacing: 1.2,
),
),
],
],
);
},
),
if (widget.stepDescription != null) ...[
const SizedBox(height: 16),
Text(
widget.stepDescription!,
style: TextStyle(
fontSize: 16,
color: widget.textColor.withOpacity(0.9),
fontStyle: FontStyle.italic,
),
textAlign: TextAlign.center,
),
],
],
),
),
),
),
);
}
}
/// Classe utilitaire pour gérer l'overlay de chargement avec progression
class LoadingProgressOverlayUtils {
/// Méthode pour afficher l'overlay de chargement avec progression
static OverlayEntry show({
required BuildContext context,
String? message,
double progress = 0.0,
String? stepDescription,
double blurAmount = 5.0,
bool showPercentage = true,
}) {
final overlayEntry = OverlayEntry(
builder: (context) => LoadingProgressOverlay(
message: message,
progress: progress,
stepDescription: stepDescription,
blurAmount: blurAmount,
showPercentage: showPercentage,
),
);
Overlay.of(context).insert(overlayEntry);
return overlayEntry;
}
/// Méthode pour mettre à jour l'overlay existant
static void update({
required OverlayEntry overlayEntry,
String? message,
required double progress,
String? stepDescription,
}) {
overlayEntry.markNeedsBuild();
}
}

View File

@@ -0,0 +1,229 @@
import 'package:flutter/material.dart';
import 'dart:ui';
/// Widget d'overlay de chargement moderne avec spinner circulaire
/// Affiche un spinner animé avec fond flou et message optionnel
class LoadingSpinOverlay extends StatefulWidget {
final String? message;
final Color backgroundColor;
final Color spinnerColor;
final Color textColor;
final double blurAmount;
final double spinnerSize;
final bool showCard;
const LoadingSpinOverlay({
super.key,
this.message,
this.backgroundColor = Colors.black54,
this.spinnerColor = Colors.blue,
this.textColor = Colors.white,
this.blurAmount = 8.0,
this.spinnerSize = 50.0,
this.showCard = true,
});
@override
State<LoadingSpinOverlay> createState() => _LoadingSpinOverlayState();
}
class _LoadingSpinOverlayState extends State<LoadingSpinOverlay>
with TickerProviderStateMixin {
late AnimationController _fadeController;
late AnimationController _rotationController;
late Animation<double> _fadeAnimation;
late Animation<double> _rotationAnimation;
@override
void initState() {
super.initState();
_fadeController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_rotationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _fadeController,
curve: Curves.easeInOut,
));
_rotationAnimation = Tween<double>(
begin: 0.0,
end: 2 * 3.14159,
).animate(CurvedAnimation(
parent: _rotationController,
curve: Curves.linear,
));
_fadeController.forward();
_rotationController.repeat();
}
@override
void dispose() {
_fadeController.dispose();
_rotationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _fadeAnimation,
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: widget.blurAmount,
sigmaY: widget.blurAmount,
),
child: Container(
color: widget.backgroundColor,
child: Center(
child: widget.showCard
? _buildCardContent()
: _buildSimpleContent(),
),
),
),
);
}
Widget _buildCardContent() {
return Material(
color: Colors.transparent,
child: Container(
padding: const EdgeInsets.all(32),
constraints: const BoxConstraints(
maxWidth: 280,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.92), // Semi-transparent
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 20,
spreadRadius: 2,
offset: const Offset(0, 8),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Spinner simple de Flutter
SizedBox(
width: widget.spinnerSize,
height: widget.spinnerSize,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(widget.spinnerColor),
),
),
if (widget.message != null) ...[
const SizedBox(height: 24),
Text(
widget.message!,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey[800],
letterSpacing: 0.3,
),
textAlign: TextAlign.center,
),
],
],
),
),
);
}
Widget _buildSimpleContent() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: widget.spinnerSize,
height: widget.spinnerSize,
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(widget.spinnerColor),
),
),
if (widget.message != null) ...[
const SizedBox(height: 20),
Text(
widget.message!,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: widget.textColor,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
),
],
],
);
}
}
/// Classe utilitaire pour gérer l'overlay de chargement avec spinner
class LoadingSpinOverlayUtils {
static OverlayEntry? _currentOverlay;
/// Affiche l'overlay de chargement avec spinner
static OverlayEntry show({
required BuildContext context,
String? message,
double blurAmount = 8.0,
bool showCard = true,
Color? spinnerColor,
}) {
// Fermer l'overlay existant s'il y en a un
hide();
final theme = Theme.of(context);
final overlayEntry = OverlayEntry(
builder: (context) => LoadingSpinOverlay(
message: message,
blurAmount: blurAmount,
showCard: showCard,
spinnerColor: spinnerColor ?? theme.colorScheme.primary,
),
);
_currentOverlay = overlayEntry;
Overlay.of(context).insert(overlayEntry);
return overlayEntry;
}
/// Met à jour le message de l'overlay existant
static void updateMessage({
required OverlayEntry overlayEntry,
String? message,
}) {
overlayEntry.markNeedsBuild();
}
/// Cache l'overlay de chargement
static void hide() {
_currentOverlay?.remove();
_currentOverlay = null;
}
/// Cache un overlay spécifique
static void hideSpecific(OverlayEntry? overlayEntry) {
overlayEntry?.remove();
if (_currentOverlay == overlayEntry) {
_currentOverlay = null;
}
}
}

View File

@@ -8,6 +8,7 @@ class MembreRowWidget extends StatelessWidget {
final Function(MembreModel)? onResetPassword;
final bool isAlternate;
final VoidCallback? onTap;
final bool isMobile;
const MembreRowWidget({
super.key,
@@ -17,6 +18,7 @@ class MembreRowWidget extends StatelessWidget {
this.onResetPassword,
this.isAlternate = false,
this.onTap,
this.isMobile = false,
});
@override
@@ -37,20 +39,29 @@ class MembreRowWidget extends StatelessWidget {
),
child: Row(
children: [
// ... existing row content ...
// ID
Expanded(
flex: 1,
child: Text(
membre.id.toString() ?? '',
style: theme.textTheme.bodyMedium,
// ID - masqué en mobile
if (!isMobile)
Expanded(
flex: 1,
child: Text(
membre.id.toString() ?? '',
style: theme.textTheme.bodyMedium,
),
),
// Identifiant (username) - masqué en mobile
if (!isMobile)
Expanded(
flex: 2,
child: Text(
membre.username ?? '',
style: theme.textTheme.bodyMedium,
),
),
),
// Prénom
Expanded(
flex: 2,
flex: isMobile ? 2 : 2,
child: Text(
membre.firstName ?? '',
style: theme.textTheme.bodyMedium,
@@ -59,47 +70,44 @@ class MembreRowWidget extends StatelessWidget {
// Nom
Expanded(
flex: 2,
flex: isMobile ? 2 : 2,
child: Text(
membre.name ?? '',
style: theme.textTheme.bodyMedium,
),
),
// Email
Expanded(
flex: 3,
child: Text(
membre.email ?? '',
style: theme.textTheme.bodyMedium,
// Email - masqué en mobile
if (!isMobile)
Expanded(
flex: 3,
child: Text(
membre.email ?? '',
style: theme.textTheme.bodyMedium,
),
),
),
// Rôle
Expanded(
flex: 1,
child: Text(
_getRoleName(membre.role),
style: theme.textTheme.bodyMedium,
// Rôle - masqué en mobile
if (!isMobile)
Expanded(
flex: 1,
child: Text(
_getRoleName(membre.role),
style: theme.textTheme.bodyMedium,
),
),
),
// Statut
Expanded(
flex: 1,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
decoration: BoxDecoration(
color: _getStatusColor(membre.isActive),
borderRadius: BorderRadius.circular(12.0),
),
child: Text(
membre.isActive == true ? 'Actif' : 'Inactif',
style: theme.textTheme.bodySmall?.copyWith(
color: Colors.white,
fontWeight: FontWeight.w500,
child: Center(
child: Tooltip(
message: membre.isActive == true ? 'Actif' : 'Inactif',
child: Icon(
membre.isActive == true ? Icons.check_circle : Icons.cancel,
color: membre.isActive == true ? Colors.green : Colors.red,
size: 24,
),
textAlign: TextAlign.center,
),
),
),
@@ -107,21 +115,21 @@ class MembreRowWidget extends StatelessWidget {
// Actions
if (onEdit != null || onDelete != null || onResetPassword != null)
Expanded(
flex: 2,
flex: isMobile ? 2 : 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
// Bouton reset password (uniquement pour les membres actifs)
if (onResetPassword != null && membre.isActive == true)
IconButton(
icon: const Icon(Icons.lock_reset, size: 22),
icon: Icon(Icons.lock_reset, size: isMobile ? 20 : 22),
onPressed: () => onResetPassword!(membre),
tooltip: 'Réinitialiser le mot de passe',
color: theme.colorScheme.primary,
),
if (onDelete != null)
IconButton(
icon: const Icon(Icons.delete, size: 22),
icon: Icon(Icons.delete, size: isMobile ? 20 : 22),
onPressed: () => onDelete!(membre),
tooltip: 'Supprimer',
color: theme.colorScheme.error,

View File

@@ -32,6 +32,8 @@ class MembreTableWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final screenWidth = MediaQuery.of(context).size.width;
final isMobile = screenWidth < 768;
return Container(
height: height,
@@ -61,21 +63,35 @@ class MembreTableWidget extends StatelessWidget {
),
child: Row(
children: [
// ID
Expanded(
flex: 1,
child: Text(
'ID',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
// ID - masqué en mobile
if (!isMobile)
Expanded(
flex: 1,
child: Text(
'ID',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
),
// Identifiant (username) - masqué en mobile
if (!isMobile)
Expanded(
flex: 2,
child: Text(
'Identifiant',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
),
),
// Prénom (firstName)
Expanded(
flex: 2,
flex: isMobile ? 2 : 2,
child: Text(
'Prénom',
style: theme.textTheme.titleSmall?.copyWith(
@@ -87,7 +103,7 @@ class MembreTableWidget extends StatelessWidget {
// Nom (name)
Expanded(
flex: 2,
flex: isMobile ? 2 : 2,
child: Text(
'Nom',
style: theme.textTheme.titleSmall?.copyWith(
@@ -97,29 +113,31 @@ class MembreTableWidget extends StatelessWidget {
),
),
// Email
Expanded(
flex: 3,
child: Text(
'Email',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
// Email - masqué en mobile
if (!isMobile)
Expanded(
flex: 3,
child: Text(
'Email',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
),
),
// Rôle (fkRole)
Expanded(
flex: 1,
child: Text(
'Rôle',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
// Rôle (fkRole) - masqué en mobile
if (!isMobile)
Expanded(
flex: 1,
child: Text(
'Rôle',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
),
),
// Statut
Expanded(
@@ -136,7 +154,7 @@ class MembreTableWidget extends StatelessWidget {
// Actions (si onEdit, onDelete ou onResetPassword sont fournis)
if (onEdit != null || onDelete != null || onResetPassword != null)
Expanded(
flex: 2,
flex: isMobile ? 2 : 2,
child: Text(
'Actions',
style: theme.textTheme.titleSmall?.copyWith(
@@ -152,14 +170,14 @@ class MembreTableWidget extends StatelessWidget {
// Corps du tableau
Expanded(
child: _buildTableContent(context),
child: _buildTableContent(context, isMobile),
),
],
),
);
}
Widget _buildTableContent(BuildContext context) {
Widget _buildTableContent(BuildContext context, bool isMobile) {
// Afficher un indicateur de chargement si isLoading est true
if (isLoading) {
return const Center(child: CircularProgressIndicator());
@@ -193,6 +211,7 @@ class MembreTableWidget extends StatelessWidget {
onResetPassword: onResetPassword,
isAlternate: index % 2 == 1,
onTap: onEdit != null ? () => onEdit!(membre) : null,
isMobile: isMobile,
);
},
);

View File

@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'dart:math';
import 'package:geosector_app/core/data/models/user_model.dart';
import 'package:geosector_app/core/data/models/amicale_model.dart';
import 'package:geosector_app/core/services/api_service.dart';
import 'custom_text_field.dart';
class UserForm extends StatefulWidget {
@@ -10,6 +13,8 @@ class UserForm extends StatefulWidget {
final bool readOnly;
final bool allowUsernameEdit;
final bool allowSectNameEdit;
final AmicaleModel? amicale; // Nouveau paramètre pour l'amicale
final bool isAdmin; // Nouveau paramètre pour savoir si c'est un admin
const UserForm({
super.key,
@@ -18,6 +23,8 @@ class UserForm extends StatefulWidget {
this.readOnly = false,
this.allowUsernameEdit = false,
this.allowSectNameEdit = false,
this.amicale,
this.isAdmin = false,
});
@override
@@ -37,11 +44,19 @@ class _UserFormState extends State<UserForm> {
late final TextEditingController _emailController;
late final TextEditingController _dateNaissanceController;
late final TextEditingController _dateEmbaucheController;
late final TextEditingController _passwordController; // Nouveau controller pour le mot de passe
// Form values
int _fkTitre = 1; // 1 = M., 2 = Mme
DateTime? _dateNaissance;
DateTime? _dateEmbauche;
// Pour la génération automatique d'username
bool _isGeneratingUsername = false;
final Random _random = Random();
// Pour afficher/masquer le mot de passe
bool _obscurePassword = true;
@override
void initState() {
@@ -64,11 +79,34 @@ class _UserFormState extends State<UserForm> {
_dateEmbaucheController = TextEditingController(text: _dateEmbauche != null ? DateFormat('dd/MM/yyyy').format(_dateEmbauche!) : '');
_passwordController = TextEditingController(); // Initialiser le controller du mot de passe
_fkTitre = user?.fkTitre ?? 1;
// Ajouter des listeners pour auto-générer l'username en création
if (widget.user?.id == 0 && widget.isAdmin && widget.amicale?.chkUsernameManuel == true) {
_nameController.addListener(_onNameOrSectNameChanged);
_sectNameController.addListener(_onNameOrSectNameChanged);
}
}
void _onNameOrSectNameChanged() {
// Auto-générer username seulement en création et si le champ username est vide
if (widget.user?.id == 0 &&
_usernameController.text.isEmpty &&
(_nameController.text.isNotEmpty || _sectNameController.text.isNotEmpty)) {
_generateAndCheckUsername();
}
}
@override
void dispose() {
// Retirer les listeners si ajoutés
if (widget.user?.id == 0 && widget.isAdmin && widget.amicale?.chkUsernameManuel == true) {
_nameController.removeListener(_onNameOrSectNameChanged);
_sectNameController.removeListener(_onNameOrSectNameChanged);
}
_usernameController.dispose();
_firstNameController.dispose();
_nameController.dispose();
@@ -78,6 +116,7 @@ class _UserFormState extends State<UserForm> {
_emailController.dispose();
_dateNaissanceController.dispose();
_dateEmbaucheController.dispose();
_passwordController.dispose();
super.dispose();
}
@@ -98,13 +137,49 @@ class _UserFormState extends State<UserForm> {
void _selectDate(BuildContext context, bool isDateNaissance) {
// Utiliser un bloc try-catch pour capturer toutes les erreurs possibles
try {
// Afficher le sélecteur de date sans spécifier de locale
// Déterminer la date initiale
DateTime initialDate;
if (isDateNaissance) {
initialDate = _dateNaissance ?? DateTime.now().subtract(const Duration(days: 365 * 30)); // 30 ans par défaut
} else {
initialDate = _dateEmbauche ?? DateTime.now();
}
// S'assurer que la date initiale est dans la plage autorisée
if (initialDate.isAfter(DateTime.now())) {
initialDate = DateTime.now();
}
if (initialDate.isBefore(DateTime(1900))) {
initialDate = DateTime(1950);
}
// Afficher le sélecteur de date avec locale française
showDatePicker(
context: context,
initialDate: DateTime.now(), // Toujours utiliser la date actuelle
initialDate: initialDate,
firstDate: DateTime(1900),
lastDate: DateTime.now(),
// Ne pas spécifier de locale pour éviter les problèmes
locale: const Locale('fr', 'FR'), // Forcer la locale française
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: Theme.of(context).colorScheme.copyWith(
primary: Theme.of(context).colorScheme.primary,
onPrimary: Colors.white,
surface: Colors.white,
onSurface: Colors.black,
),
),
child: child!,
);
},
helpText: isDateNaissance ? 'SÉLECTIONNER LA DATE DE NAISSANCE' : 'SÉLECTIONNER LA DATE D\'EMBAUCHE',
cancelText: 'ANNULER',
confirmText: 'VALIDER',
fieldLabelText: 'Entrer une date',
fieldHintText: 'jj/mm/aaaa',
errorFormatText: 'Format de date invalide',
errorInvalidText: 'Date invalide',
).then((DateTime? picked) {
// Vérifier si une date a été sélectionnée
if (picked != null) {
@@ -141,30 +216,247 @@ class _UserFormState extends State<UserForm> {
}
}
// Nettoyer une chaîne pour l'username
String _cleanString(String input) {
return input.toLowerCase()
.replaceAll(RegExp(r'[^a-z0-9]'), ''); // Garder seulement lettres et chiffres
}
// Extraire une partie aléatoire d'une chaîne
String _extractRandomPart(String input, int minLength, int maxLength) {
if (input.isEmpty) return '';
final cleaned = _cleanString(input);
if (cleaned.isEmpty) return '';
final length = minLength + _random.nextInt(maxLength - minLength + 1);
if (cleaned.length <= length) return cleaned;
// Prendre les premiers caractères jusqu'à la longueur désirée
return cleaned.substring(0, length);
}
// Générer un username selon l'algorithme spécifié
String _generateUsername() {
// Récupérer les données nécessaires
final nom = _nameController.text.isNotEmpty ? _nameController.text : _sectNameController.text;
final codePostal = widget.amicale?.codePostal ?? '';
final ville = widget.amicale?.ville ?? '';
// Nettoyer et extraire les parties
final nomPart = _extractRandomPart(nom, 2, 5);
final cpPart = _extractRandomPart(codePostal, 2, 3);
final villePart = _extractRandomPart(ville, 2, 4);
final nombreAleatoire = 10 + _random.nextInt(990); // 10 à 999
// Choisir des séparateurs aléatoires (uniquement ceux autorisés: ., _, -)
final separateurs = ['', '.', '_', '-'];
final sep1 = separateurs[_random.nextInt(separateurs.length)];
final sep2 = separateurs[_random.nextInt(separateurs.length)];
// Assembler l'username
String username = '$nomPart$sep1$cpPart$sep2$villePart$nombreAleatoire';
// Si trop court, ajouter des chiffres pour atteindre minimum 10 caractères
while (username.length < 10) {
username += _random.nextInt(10).toString();
}
// S'assurer que l'username ne contient que des caractères autorisés (a-z, 0-9, ., -, _)
// Normalement déjà le cas avec notre algorithme, mais au cas où
username = username.toLowerCase().replaceAll(RegExp(r'[^a-z0-9._-]'), '');
return username;
}
// Vérifier la disponibilité d'un username via l'API
Future<Map<String, dynamic>> _checkUsernameAvailability(String username) async {
try {
final response = await ApiService.instance.post(
'/users/check-username',
data: {'username': username},
);
if (response.statusCode == 200) {
return response.data;
}
return {'available': false};
} catch (e) {
debugPrint('Erreur lors de la vérification de l\'username: $e');
return {'available': false};
}
}
// Générer et vérifier un username jusqu'à en trouver un disponible
Future<void> _generateAndCheckUsername() async {
if (_isGeneratingUsername) return; // Éviter les appels multiples
setState(() {
_isGeneratingUsername = true;
});
try {
int attempts = 0;
const maxAttempts = 10;
while (attempts < maxAttempts) {
final username = _generateUsername();
debugPrint('Tentative ${attempts + 1}: Vérification de $username');
final result = await _checkUsernameAvailability(username);
if (result['available'] == true) {
// Username disponible, l'utiliser
setState(() {
_usernameController.text = username;
});
debugPrint('✅ Username disponible trouvé: $username');
break;
} else {
// Si l'API propose des suggestions, essayer la première
if (result['suggestions'] != null && result['suggestions'].isNotEmpty) {
final suggestion = result['suggestions'][0];
debugPrint('Vérification de la suggestion: $suggestion');
final suggestionResult = await _checkUsernameAvailability(suggestion);
if (suggestionResult['available'] == true) {
setState(() {
_usernameController.text = suggestion;
});
debugPrint('✅ Suggestion disponible utilisée: $suggestion');
break;
}
}
}
attempts++;
}
if (attempts >= maxAttempts) {
debugPrint('⚠️ Impossible de trouver un username disponible après $maxAttempts tentatives');
}
} finally {
setState(() {
_isGeneratingUsername = false;
});
}
}
// Valider le mot de passe selon les règles
String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
// Pour un nouveau membre, le mot de passe est obligatoire si le champ est affiché
if (widget.user?.id == 0) {
return "Veuillez entrer un mot de passe";
}
return null; // En modification, vide = garder l'ancien
}
// Faire un trim pour retirer les espaces en début/fin
final trimmedValue = value.trim();
if (trimmedValue.isEmpty) {
return "Le mot de passe ne peut pas être vide";
}
// Vérifier qu'il n'y a pas d'espaces dans le mot de passe
if (trimmedValue.contains(' ')) {
return "Le mot de passe ne doit pas contenir d'espaces";
}
// Vérifier la longueur
if (trimmedValue.length < 12) {
return "Le mot de passe doit contenir au moins 12 caractères";
}
if (trimmedValue.length > 16) {
return "Le mot de passe ne doit pas dépasser 16 caractères";
}
// Vérifier qu'il n'est pas égal au username (après trim des deux)
if (trimmedValue == _usernameController.text.trim()) {
return "Le mot de passe ne doit pas être identique au nom d'utilisateur";
}
// Vérifier la présence d'au moins une minuscule
if (!trimmedValue.contains(RegExp(r'[a-z]'))) {
return "Le mot de passe doit contenir au moins une lettre minuscule";
}
// Vérifier la présence d'au moins une majuscule
if (!trimmedValue.contains(RegExp(r'[A-Z]'))) {
return "Le mot de passe doit contenir au moins une lettre majuscule";
}
// Vérifier la présence d'au moins un chiffre
if (!trimmedValue.contains(RegExp(r'[0-9]'))) {
return "Le mot de passe doit contenir au moins un chiffre";
}
// Vérifier la présence d'au moins un caractère spécial
if (!trimmedValue.contains(RegExp(r'[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]'))) {
return "Le mot de passe doit contenir au moins un caractère spécial (!@#\$%^&*()_+-=[]{}|;:,.<>?)";
}
return null;
}
// Générer un mot de passe aléatoire respectant les règles
String _generatePassword() {
const String lowercase = 'abcdefghijklmnopqrstuvwxyz';
const String uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const String digits = '0123456789';
const String special = '!@#\$%^&*()_+-=[]{}|;:,.<>?';
// Longueur aléatoire entre 12 et 16
final length = 12 + _random.nextInt(5);
// S'assurer d'avoir au moins un caractère de chaque type
List<String> password = [];
password.add(lowercase[_random.nextInt(lowercase.length)]);
password.add(uppercase[_random.nextInt(uppercase.length)]);
password.add(digits[_random.nextInt(digits.length)]);
password.add(special[_random.nextInt(special.length)]);
// Compléter avec des caractères aléatoires
const String allChars = lowercase + uppercase + digits + special;
for (int i = password.length; i < length; i++) {
password.add(allChars[_random.nextInt(allChars.length)]);
}
// Mélanger les caractères
password.shuffle(_random);
return password.join('');
}
// Méthode publique pour récupérer le mot de passe si défini
String? getPassword() {
final password = _passwordController.text.trim();
return password.isNotEmpty ? password : null;
}
// Méthode publique pour valider et récupérer l'utilisateur
UserModel? validateAndGetUser() {
if (_formKey.currentState!.validate()) {
return widget.user?.copyWith(
username: _usernameController.text,
firstName: _firstNameController.text,
name: _nameController.text,
sectName: _sectNameController.text,
phone: _phoneController.text,
mobile: _mobileController.text,
email: _emailController.text,
username: _usernameController.text.trim(), // Appliquer trim
firstName: _firstNameController.text.trim(),
name: _nameController.text.trim(),
sectName: _sectNameController.text.trim(),
phone: _phoneController.text.trim(),
mobile: _mobileController.text.trim(),
email: _emailController.text.trim(),
fkTitre: _fkTitre,
dateNaissance: _dateNaissance,
dateEmbauche: _dateEmbauche,
) ??
UserModel(
id: 0,
username: _usernameController.text,
firstName: _firstNameController.text,
name: _nameController.text,
sectName: _sectNameController.text,
phone: _phoneController.text,
mobile: _mobileController.text,
email: _emailController.text,
username: _usernameController.text.trim(), // Appliquer trim
firstName: _firstNameController.text.trim(),
name: _nameController.text.trim(),
sectName: _sectNameController.text.trim(),
phone: _phoneController.text.trim(),
mobile: _mobileController.text.trim(),
email: _emailController.text.trim(),
fkTitre: _fkTitre,
dateNaissance: _dateNaissance,
dateEmbauche: _dateEmbauche,
@@ -180,90 +472,36 @@ class _UserFormState extends State<UserForm> {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isWideScreen = MediaQuery.of(context).size.width > 900;
// Déterminer si on doit afficher le champ username selon les règles
final bool shouldShowUsernameField = widget.isAdmin && widget.amicale?.chkUsernameManuel == true;
// Déterminer si le username est éditable (seulement en création, jamais en modification)
final bool canEditUsername = shouldShowUsernameField && widget.allowUsernameEdit && widget.user?.id == 0;
// Déterminer si on doit afficher le champ mot de passe
final bool shouldShowPasswordField = widget.isAdmin && widget.amicale?.chkMdpManuel == true;
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Ligne 1: Username et Email (si écran large)
if (isWideScreen)
Row(
children: [
Expanded(
child: CustomTextField(
controller: _usernameController,
label: "Nom d'utilisateur",
readOnly: !widget.allowUsernameEdit, // Utiliser le paramètre
prefixIcon: Icons.account_circle,
isRequired: widget.allowUsernameEdit,
validator: widget.allowUsernameEdit
? (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer le nom d'utilisateur";
}
return null;
}
: null,
),
),
const SizedBox(width: 16),
Expanded(
child: CustomTextField(
controller: _emailController,
label: "Email",
keyboardType: TextInputType.emailAddress,
readOnly: widget.readOnly,
isRequired: true, // Email toujours obligatoire
validator: (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer l'adresse email";
}
if (!value.contains('@') || !value.contains('.')) {
return "Veuillez entrer une adresse email valide";
}
return null;
},
),
),
],
)
else ...[
// Version mobile: Username seul
CustomTextField(
controller: _usernameController,
label: "Nom d'utilisateur",
readOnly: !widget.allowUsernameEdit, // Utiliser le paramètre
prefixIcon: Icons.account_circle,
isRequired: widget.allowUsernameEdit, // Obligatoire si éditable
validator: widget.allowUsernameEdit
? (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer le nom d'utilisateur";
}
return null;
}
: null,
),
const SizedBox(height: 16),
// Email seul en mobile
CustomTextField(
controller: _emailController,
label: "Email",
keyboardType: TextInputType.emailAddress,
readOnly: widget.readOnly,
isRequired: true, // Email toujours obligatoire
validator: (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer l'adresse email";
}
if (!value.contains('@') || !value.contains('.')) {
return "Veuillez entrer une adresse email valide";
}
return null;
},
),
],
// Email seul sur la première ligne
CustomTextField(
controller: _emailController,
label: "Email",
keyboardType: TextInputType.emailAddress,
readOnly: widget.readOnly,
isRequired: true,
validator: (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer l'adresse email";
}
if (!value.contains('@') || !value.contains('.')) {
return "Veuillez entrer une adresse email valide";
}
return null;
},
),
const SizedBox(height: 16),
// Titre (M. ou Mme)
@@ -378,7 +616,7 @@ class _UserFormState extends State<UserForm> {
const SizedBox(height: 16),
],
// Ligne 3: Téléphones (fixe et mobile)
// Ligne 2: Téléphones (fixe et mobile)
if (isWideScreen)
Row(
children: [
@@ -458,6 +696,224 @@ class _UserFormState extends State<UserForm> {
),
],
const SizedBox(height: 16),
// Ligne 3: Username et Password (si applicable)
if (shouldShowUsernameField || shouldShowPasswordField) ...[
if (isWideScreen)
Row(
children: [
if (shouldShowUsernameField)
Expanded(
child: CustomTextField(
controller: _usernameController,
label: "Nom d'utilisateur",
readOnly: !canEditUsername,
prefixIcon: Icons.account_circle,
isRequired: canEditUsername,
suffixIcon: (widget.user?.id == 0 && canEditUsername)
? _isGeneratingUsername
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
)
: IconButton(
icon: Icon(Icons.refresh),
onPressed: _generateAndCheckUsername,
tooltip: "Générer un nom d'utilisateur",
)
: null,
helperText: canEditUsername
? "Min. 10 caractères (a-z, 0-9, . - _)"
: null,
validator: canEditUsername
? (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer le nom d'utilisateur";
}
// Faire un trim pour retirer les espaces en début/fin
final trimmedValue = value.trim();
if (trimmedValue.isEmpty) {
return "Le nom d'utilisateur ne peut pas être vide";
}
// Vérifier qu'il n'y a pas d'espaces dans l'username
if (trimmedValue.contains(' ')) {
return "Le nom d'utilisateur ne doit pas contenir d'espaces";
}
// Vérifier la longueur minimale
if (trimmedValue.length < 10) {
return "Le nom d'utilisateur doit contenir au moins 10 caractères";
}
// Vérifier les caractères autorisés (a-z, 0-9, ., -, _)
if (!RegExp(r'^[a-z0-9._-]+$').hasMatch(trimmedValue)) {
return "Caractères autorisés : lettres minuscules, chiffres, . - _";
}
return null;
}
: null,
),
),
if (shouldShowUsernameField && shouldShowPasswordField)
const SizedBox(width: 16),
if (shouldShowPasswordField)
Expanded(
child: CustomTextField(
controller: _passwordController,
label: "Mot de passe",
obscureText: _obscurePassword,
readOnly: widget.readOnly,
prefixIcon: Icons.lock,
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Bouton pour afficher/masquer le mot de passe
IconButton(
icon: Icon(_obscurePassword ? Icons.visibility : Icons.visibility_off),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
tooltip: _obscurePassword ? "Afficher le mot de passe" : "Masquer le mot de passe",
),
// Bouton pour générer un mot de passe (seulement si éditable)
if (!widget.readOnly)
IconButton(
icon: Icon(Icons.auto_awesome),
onPressed: () {
final newPassword = _generatePassword();
setState(() {
_passwordController.text = newPassword;
_obscurePassword = false; // Afficher le mot de passe généré
});
// Revalider le formulaire
_formKey.currentState?.validate();
},
tooltip: "Générer un mot de passe sécurisé",
),
],
),
helperText: widget.user?.id != 0
? "Laissez vide pour conserver le mot de passe actuel"
: "12-16 car. avec min/maj, chiffres et spéciaux (!@#\$%^&*()_+-=[]{}|;:,.<>?)",
validator: _validatePassword,
),
),
// Si seulement un des deux est affiché, ajouter un Expanded vide pour garder l'alignement
if ((shouldShowUsernameField && !shouldShowPasswordField) || (!shouldShowUsernameField && shouldShowPasswordField))
const Expanded(child: SizedBox()),
],
)
else ...[
// Version mobile: Username et Password séparés
if (shouldShowUsernameField) ...[
CustomTextField(
controller: _usernameController,
label: "Nom d'utilisateur",
readOnly: !canEditUsername,
prefixIcon: Icons.account_circle,
isRequired: canEditUsername,
suffixIcon: (widget.user?.id == 0 && canEditUsername)
? _isGeneratingUsername
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
)
: IconButton(
icon: Icon(Icons.refresh),
onPressed: _generateAndCheckUsername,
tooltip: "Générer un nom d'utilisateur",
)
: null,
helperText: canEditUsername
? "Min. 10 caractères (a-z, 0-9, . - _)"
: null,
validator: canEditUsername
? (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer le nom d'utilisateur";
}
// Faire un trim pour retirer les espaces en début/fin
final trimmedValue = value.trim();
if (trimmedValue.isEmpty) {
return "Le nom d'utilisateur ne peut pas être vide";
}
// Vérifier qu'il n'y a pas d'espaces dans l'username
if (trimmedValue.contains(' ')) {
return "Le nom d'utilisateur ne doit pas contenir d'espaces";
}
// Vérifier la longueur minimale
if (trimmedValue.length < 10) {
return "Le nom d'utilisateur doit contenir au moins 10 caractères";
}
// Vérifier les caractères autorisés (a-z, 0-9, ., -, _)
if (!RegExp(r'^[a-z0-9._-]+$').hasMatch(trimmedValue)) {
return "Caractères autorisés : lettres minuscules, chiffres, . - _";
}
return null;
}
: null,
),
const SizedBox(height: 16),
],
if (shouldShowPasswordField) ...[
CustomTextField(
controller: _passwordController,
label: "Mot de passe",
obscureText: _obscurePassword,
readOnly: widget.readOnly,
prefixIcon: Icons.lock,
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Bouton pour afficher/masquer le mot de passe
IconButton(
icon: Icon(_obscurePassword ? Icons.visibility : Icons.visibility_off),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
tooltip: _obscurePassword ? "Afficher le mot de passe" : "Masquer le mot de passe",
),
// Bouton pour générer un mot de passe (seulement si éditable)
if (!widget.readOnly)
IconButton(
icon: Icon(Icons.auto_awesome),
onPressed: () {
final newPassword = _generatePassword();
setState(() {
_passwordController.text = newPassword;
_obscurePassword = false; // Afficher le mot de passe généré
});
// Revalider le formulaire
_formKey.currentState?.validate();
},
tooltip: "Générer un mot de passe sécurisé",
),
],
),
helperText: widget.user?.id != 0
? "Laissez vide pour conserver le mot de passe actuel"
: "12-16 car. avec min/maj, chiffres et spéciaux (!@#\$%^&*()_+-=[]{}|;:,.<>?)",
validator: _validatePassword,
),
const SizedBox(height: 16),
],
],
const SizedBox(height: 16),
],
// Ligne 4: Dates (naissance et embauche)
if (isWideScreen)

View File

@@ -1,16 +1,19 @@
import 'package:flutter/material.dart';
import 'package:geosector_app/core/data/models/user_model.dart';
import 'package:geosector_app/core/data/models/amicale_model.dart';
import 'package:geosector_app/presentation/widgets/user_form.dart';
class UserFormDialog extends StatefulWidget {
final UserModel? user;
final String title;
final bool readOnly;
final Function(UserModel)? onSubmit;
final Function(UserModel, {String? password})? onSubmit; // Modifié pour inclure le mot de passe
final bool showRoleSelector;
final List<RoleOption>? availableRoles;
final bool showActiveCheckbox;
final bool allowUsernameEdit;
final AmicaleModel? amicale; // Nouveau paramètre
final bool isAdmin; // Nouveau paramètre
const UserFormDialog({
super.key,
@@ -22,6 +25,8 @@ class UserFormDialog extends StatefulWidget {
this.availableRoles,
this.showActiveCheckbox = false,
this.allowUsernameEdit = false,
this.amicale,
this.isAdmin = false,
});
@override
@@ -55,6 +60,7 @@ class _UserFormDialogState extends State<UserFormDialog> {
void _handleSubmit() async {
// Utiliser la méthode validateAndGetUser du UserForm
final userData = _userFormKey.currentState?.validateAndGetUser();
final password = _userFormKey.currentState?.getPassword(); // Récupérer le mot de passe
if (userData != null) {
var finalUser = userData;
@@ -70,7 +76,7 @@ class _UserFormDialogState extends State<UserFormDialog> {
}
if (widget.onSubmit != null) {
widget.onSubmit!(finalUser);
widget.onSubmit!(finalUser, password: password); // Passer le mot de passe
}
}
}
@@ -200,6 +206,8 @@ class _UserFormDialogState extends State<UserFormDialog> {
readOnly: widget.readOnly,
allowUsernameEdit: widget.allowUsernameEdit,
allowSectNameEdit: widget.allowUsernameEdit,
amicale: widget.amicale, // Passer l'amicale
isAdmin: widget.isAdmin, // Passer isAdmin
onSubmit: null, // Pas besoin de callback
),
],

View File

@@ -0,0 +1 @@
/home/pierre/.pub-cache/hosted/pub.dev/file_selector_linux-0.9.3+2/

View File

@@ -0,0 +1 @@
/home/pierre/.pub-cache/hosted/pub.dev/image_picker_linux-0.2.1+2/

View File

@@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
url_launcher_linux
)

View File

@@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation
import connectivity_plus
import file_selector_macos
import flutter_local_notifications
import geolocator_apple
import package_info_plus
@@ -15,6 +16,7 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))

View File

@@ -3,8 +3,8 @@ FLUTTER_ROOT=/home/pierre/dev/flutter
FLUTTER_APPLICATION_PATH=/home/pierre/dev/geosector/app
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=3.0.4
FLUTTER_BUILD_NUMBER=304
FLUTTER_BUILD_NAME=3.0.6
FLUTTER_BUILD_NUMBER=306
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false

View File

@@ -4,8 +4,8 @@ export "FLUTTER_ROOT=/home/pierre/dev/flutter"
export "FLUTTER_APPLICATION_PATH=/home/pierre/dev/geosector/app"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=3.0.4"
export "FLUTTER_BUILD_NUMBER=304"
export "FLUTTER_BUILD_NAME=3.0.6"
export "FLUTTER_BUILD_NUMBER=306"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"

View File

@@ -114,10 +114,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "0b1b12a0a549605e5f04476031cd0bc91ead1d7c8e830773a18ee54179b3cb62"
sha256: ba95c961bafcd8686d1cf63be864eb59447e795e124d98d6a27d91fcd13602fb
url: "https://pub.dev"
source: hosted
version: "8.11.0"
version: "8.11.1"
characters:
dependency: transitive
description:
@@ -198,6 +198,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.4+2"
crypto:
dependency: transitive
description:
@@ -258,10 +266,10 @@ packages:
dependency: "direct main"
description:
name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
url: "https://pub.dev"
source: hosted
version: "5.8.0+1"
version: "5.9.0"
dio_cache_interceptor:
dependency: transitive
description:
@@ -318,6 +326,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
file_selector_linux:
dependency: transitive
description:
name: file_selector_linux
sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
url: "https://pub.dev"
source: hosted
version: "0.9.3+2"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711"
url: "https://pub.dev"
source: hosted
version: "0.9.4+3"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
url: "https://pub.dev"
source: hosted
version: "2.6.2"
file_selector_windows:
dependency: transitive
description:
name: file_selector_windows
sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b"
url: "https://pub.dev"
source: hosted
version: "0.9.3+4"
fixnum:
dependency: transitive
description:
@@ -387,6 +427,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_map:
dependency: "direct main"
description:
@@ -403,6 +448,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0+1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab"
url: "https://pub.dev"
source: hosted
version: "2.0.29"
flutter_svg:
dependency: "direct main"
description:
@@ -569,10 +622,10 @@ packages:
dependency: transitive
description:
name: http
sha256: "85ab0074f9bf2b24625906d8382bbec84d3d6919d285ba9c106b07b65791fb99"
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
url: "https://pub.dev"
source: hosted
version: "1.5.0-beta.2"
version: "1.5.0"
http_cache_core:
dependency: transitive
description:
@@ -613,6 +666,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.5.4"
image_picker:
dependency: "direct main"
description:
name: image_picker
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
sha256: b08e9a04d0f8d91f4a6e767a745b9871bfbc585410205c311d0492de20a7ccd6
url: "https://pub.dev"
source: hosted
version: "0.8.12+25"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
url: "https://pub.dev"
source: hosted
version: "0.8.12+2"
image_picker_linux:
dependency: transitive
description:
name: image_picker_linux
sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9"
url: "https://pub.dev"
source: hosted
version: "0.2.1+2"
image_picker_macos:
dependency: transitive
description:
name: image_picker_macos
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
url: "https://pub.dev"
source: hosted
version: "0.2.1+2"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
url: "https://pub.dev"
source: hosted
version: "2.10.1"
image_picker_windows:
dependency: transitive
description:
name: image_picker_windows
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
url: "https://pub.dev"
source: hosted
version: "0.2.1+1"
intl:
dependency: "direct main"
description:
@@ -945,10 +1062,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e"
url: "https://pub.dev"
source: hosted
version: "2.4.10"
version: "2.4.11"
shared_preferences_foundation:
dependency: transitive
description:
@@ -1078,18 +1195,18 @@ packages:
dependency: "direct main"
description:
name: syncfusion_flutter_charts
sha256: "55bc6210265483eba4ea168931e4edd3df24510d1ea99c177df46d69ab0deb6f"
sha256: a2263d6221b17d49ea9c23709df8796abde4cd2d2664960d48fac62b9d8c3eb8
url: "https://pub.dev"
source: hosted
version: "30.1.42"
version: "30.2.4"
syncfusion_flutter_core:
dependency: transitive
description:
name: syncfusion_flutter_core
sha256: f999f11af9c54bd46ceb478273e06774d28723ffa2c622d2d6e7e84a358c23e0
sha256: "9d6b7722331e8c84e837d852da03f3735bf436fe6ebf2acc3455a2a21a0d47ff"
url: "https://pub.dev"
source: hosted
version: "30.1.42"
version: "30.2.4"
synchronized:
dependency: transitive
description:
@@ -1174,10 +1291,10 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79"
sha256: "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656"
url: "https://pub.dev"
source: hosted
version: "6.3.16"
version: "6.3.17"
url_launcher_ios:
dependency: transitive
description:

View File

@@ -1,7 +1,7 @@
name: geosector_app
description: 'GEOSECTOR - Gestion de distribution des calendriers par secteurs géographiques pour les amicales de pompiers'
publish_to: 'none'
version: 3.0.4+304
version: 3.0.6+306
environment:
sdk: '>=3.0.0 <4.0.0'
@@ -9,6 +9,8 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
cupertino_icons: ^1.0.6
# Navigation
@@ -49,6 +51,9 @@ dependencies:
mqtt5_client: ^4.11.0
flutter_local_notifications: ^19.0.1
# Upload d'images
image_picker: ^1.1.2
dev_dependencies:
flutter_test:
sdk: flutter

View File

@@ -0,0 +1 @@
/home/pierre/.pub-cache/hosted/pub.dev/file_selector_windows-0.9.3+4/

View File

@@ -0,0 +1 @@
/home/pierre/.pub-cache/hosted/pub.dev/image_picker_windows-0.2.1+1/

View File

@@ -7,12 +7,15 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <geolocator_windows/geolocator_windows.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
file_selector_windows
geolocator_windows
url_launcher_windows
)