feat: add QR code scanning and file upload capabilities with permissions

This commit is contained in:
2026-04-22 19:21:57 +02:00
parent 8438e0804f
commit ba54267b77
11 changed files with 119 additions and 8 deletions

View File

@@ -24,6 +24,12 @@
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<intent-filter android:label="flux_deep_link">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="fluxapp" />
</intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
@@ -31,6 +37,17 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<!-- Required to query activities that can process text, see: <!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT. https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
@@ -42,4 +59,5 @@
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
</queries> </queries>
</manifest> </manifest>

View File

@@ -66,5 +66,20 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>NSCameraUsageDescription</key>
<string>FLUX ha bisogno della fotocamera per scansionare i QR e caricare i documenti.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>FLUX ha bisogno di accedere alla galleria per permetterti di allegare file esistenti.</string>
<key>NSMicrophoneUsageDescription</key>
<string>FLUX ha bisogno del microfono (se intendi registrare video o note vocali).</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fluxapp</string>
</array>
</dict>
</array>
</dict> </dict>
</plist> </plist>

View File

@@ -57,7 +57,7 @@ class ProvidersCubit extends Cubit<ProvidersState> {
ProvidersCubit() : super(const ProvidersState()); ProvidersCubit() : super(const ProvidersState());
// Carica i provider della company e quelli associati a uno store specifico // Carica i provider della company e quelli associati a uno store specifico
Future<void> loadProviders(StoreModel? store) async { Future<void> loadProviders({StoreModel? store}) async {
emit(state.copyWith(isLoading: true)); emit(state.copyWith(isLoading: true));
try { try {
final all = await _repository.fetchAllCompanyProviders( final all = await _repository.fetchAllCompanyProviders(
@@ -149,7 +149,7 @@ class ProvidersCubit extends Cubit<ProvidersState> {
await _repository.syncProviderStores(pId!, selectedStoreIds); await _repository.syncProviderStores(pId!, selectedStoreIds);
// 3. Ricarichiamo tutto // 3. Ricarichiamo tutto
await loadProviders(null); await loadProviders();
} catch (e) { } catch (e) {
emit(state.copyWith(isLoading: false, errorMessage: e.toString())); emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
} }
@@ -168,7 +168,7 @@ class ProvidersCubit extends Cubit<ProvidersState> {
// o fare un confronto tra i presenti e i nuovi) // o fare un confronto tra i presenti e i nuovi)
await _repository.syncProviderStores(provider.id!, storeIds); await _repository.syncProviderStores(provider.id!, storeIds);
await loadProviders(null); await loadProviders();
} catch (e) { } catch (e) {
emit(state.copyWith(isLoading: false, errorMessage: e.toString())); emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
} }

View File

@@ -124,7 +124,20 @@ class _FluxAppState extends State<FluxApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<SessionCubit, SessionState>( // Il BlocConsumer unisce Listener e Builder in un colpo solo!
return BlocConsumer<SessionCubit, SessionState>(
// --- PARTE LISTENER (Il colpo di clacson in background) ---
listenWhen: (previous, current) =>
previous.status != SessionStatus.authenticated &&
current.status == SessionStatus.authenticated,
listener: (context, state) {
// BAM! L'utente è dentro. Pre-carichiamo i Cubit leggeri.
context.read<StoreCubit>().loadStores();
context.read<StaffCubit>().loadAllStaff();
context.read<ProvidersCubit>().loadProviders();
},
// --- PARTE BUILDER (La UI che viene disegnata a schermo) ---
builder: (context, sessionState) { builder: (context, sessionState) {
if (sessionState.status == SessionStatus.initial) { if (sessionState.status == SessionStatus.initial) {
return _buildLoadingScreen(); return _buildLoadingScreen();

View File

@@ -18,5 +18,10 @@
<key>com.apple.security.network.client</key> <key>com.apple.security.network.client</key>
<true/> <true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@@ -28,5 +28,7 @@
<string>MainMenu</string> <string>MainMenu</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
<key>NSCameraUsageDescription</key>
<string>FLUX ha bisogno della fotocamera per scansionare i QR Code e acquisire documenti.</string>
</dict> </dict>
</plist> </plist>

View File

@@ -6,13 +6,18 @@
<true/> <true/>
<key>com.apple.security.network.client</key> <key>com.apple.security.network.client</key>
<true/> <true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key> <key>com.apple.security.files.downloads.read-write</key>
<true/> <true/>
<key>com.apple.security.device.camera</key>
<key>com.apple.security.network.client</key>
<true/> <true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@@ -544,6 +544,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.9.2" version: "2.9.2"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
url: "https://pub.dev"
source: hosted
version: "12.0.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6"
url: "https://pub.dev"
source: hosted
version: "13.0.1"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
url: "https://pub.dev"
source: hosted
version: "9.4.7"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
url: "https://pub.dev"
source: hosted
version: "4.3.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:

View File

@@ -21,6 +21,7 @@ dependencies:
internet_file: ^1.3.0 internet_file: ^1.3.0
intl: ^0.20.2 intl: ^0.20.2
pdfx: ^2.9.2 pdfx: ^2.9.2
permission_handler: ^12.0.1
qr_flutter: ^4.1.0 qr_flutter: ^4.1.0
shared_preferences: ^2.5.5 shared_preferences: ^2.5.5
supabase_flutter: ^2.12.2 supabase_flutter: ^2.12.2

View File

@@ -8,6 +8,7 @@
#include <app_links/app_links_plugin_c_api.h> #include <app_links/app_links_plugin_c_api.h>
#include <pdfx/pdfx_plugin.h> #include <pdfx/pdfx_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
@@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("AppLinksPluginCApi")); registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
PdfxPluginRegisterWithRegistrar( PdfxPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PdfxPlugin")); registry->GetRegistrarForPlugin("PdfxPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View File

@@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
app_links app_links
pdfx pdfx
permission_handler_windows
url_launcher_windows url_launcher_windows
) )