feat: Implement audio recording and transcription for rapid order creation across platforms.
This commit is contained in:
@@ -50,6 +50,11 @@ public final class GeneratedPluginRegistrant {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Error registering plugin path_provider_android, io.flutter.plugins.pathprovider.PathProviderPlugin", e);
|
Log.e(TAG, "Error registering plugin path_provider_android, io.flutter.plugins.pathprovider.PathProviderPlugin", e);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
flutterEngine.getPlugins().add(new com.llfbandit.record.RecordPlugin());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error registering plugin record_android, com.llfbandit.record.RecordPlugin", e);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
|
flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -36,6 +36,12 @@
|
|||||||
@import image_picker_ios;
|
@import image_picker_ios;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<record_darwin/RecordPlugin.h>)
|
||||||
|
#import <record_darwin/RecordPlugin.h>
|
||||||
|
#else
|
||||||
|
@import record_darwin;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
||||||
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
||||||
#else
|
#else
|
||||||
@@ -56,6 +62,7 @@
|
|||||||
[FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]];
|
[FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]];
|
||||||
[FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]];
|
[FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]];
|
||||||
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
||||||
|
[RecordPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordPlugin"]];
|
||||||
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
||||||
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,16 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
|
#include <record_linux/record_linux_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
||||||
|
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
|
record_linux
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import file_selector_macos
|
|||||||
import firebase_app_check
|
import firebase_app_check
|
||||||
import firebase_auth
|
import firebase_auth
|
||||||
import firebase_core
|
import firebase_core
|
||||||
|
import record_darwin
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <firebase_auth/firebase_auth_plugin_c_api.h>
|
#include <firebase_auth/firebase_auth_plugin_c_api.h>
|
||||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
|
#include <record_windows/record_windows_plugin_c_api.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) {
|
||||||
@@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi"));
|
registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi"));
|
||||||
FirebaseCorePluginCApiRegisterWithRegistrar(
|
FirebaseCorePluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
||||||
|
RecordWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
file_selector_windows
|
file_selector_windows
|
||||||
firebase_auth
|
firebase_auth
|
||||||
firebase_core
|
firebase_core
|
||||||
|
record_windows
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ public final class GeneratedPluginRegistrant {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Error registering plugin permission_handler_android, com.baseflow.permissionhandler.PermissionHandlerPlugin", e);
|
Log.e(TAG, "Error registering plugin permission_handler_android, com.baseflow.permissionhandler.PermissionHandlerPlugin", e);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
flutterEngine.getPlugins().add(new com.llfbandit.record.RecordPlugin());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error registering plugin record_android, com.llfbandit.record.RecordPlugin", e);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
|
flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -54,6 +54,12 @@
|
|||||||
@import permission_handler_apple;
|
@import permission_handler_apple;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<record_darwin/RecordPlugin.h>)
|
||||||
|
#import <record_darwin/RecordPlugin.h>
|
||||||
|
#else
|
||||||
|
@import record_darwin;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
||||||
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
||||||
#else
|
#else
|
||||||
@@ -77,6 +83,7 @@
|
|||||||
[FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]];
|
[FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]];
|
||||||
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
||||||
[PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]];
|
[PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]];
|
||||||
|
[RecordPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordPlugin"]];
|
||||||
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
||||||
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,16 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
|
#include <record_linux/record_linux_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
||||||
|
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
|
record_linux
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import firebase_app_check
|
|||||||
import firebase_auth
|
import firebase_auth
|
||||||
import firebase_core
|
import firebase_core
|
||||||
import geolocator_apple
|
import geolocator_apple
|
||||||
|
import record_darwin
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
||||||
|
RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
#include <geolocator_windows/geolocator_windows.h>
|
#include <geolocator_windows/geolocator_windows.h>
|
||||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
|
#include <record_windows/record_windows_plugin_c_api.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) {
|
||||||
@@ -24,6 +25,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("GeolocatorWindows"));
|
registry->GetRegistrarForPlugin("GeolocatorWindows"));
|
||||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
|
RecordWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
firebase_core
|
firebase_core
|
||||||
geolocator_windows
|
geolocator_windows
|
||||||
permission_handler_windows
|
permission_handler_windows
|
||||||
|
record_windows
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -31,3 +31,4 @@ export 'src/services/device/camera/camera_service.dart';
|
|||||||
export 'src/services/device/gallery/gallery_service.dart';
|
export 'src/services/device/gallery/gallery_service.dart';
|
||||||
export 'src/services/device/file/file_picker_service.dart';
|
export 'src/services/device/file/file_picker_service.dart';
|
||||||
export 'src/services/device/file_upload/device_file_upload_service.dart';
|
export 'src/services/device/file_upload/device_file_upload_service.dart';
|
||||||
|
export 'src/services/device/audio/audio_recorder_service.dart';
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class CoreModule extends Module {
|
|||||||
i.addSingleton<CameraService>(() => CameraService(i.get<ImagePicker>()));
|
i.addSingleton<CameraService>(() => CameraService(i.get<ImagePicker>()));
|
||||||
i.addSingleton<GalleryService>(() => GalleryService(i.get<ImagePicker>()));
|
i.addSingleton<GalleryService>(() => GalleryService(i.get<ImagePicker>()));
|
||||||
i.addSingleton<FilePickerService>(FilePickerService.new);
|
i.addSingleton<FilePickerService>(FilePickerService.new);
|
||||||
|
i.addSingleton<AudioRecorderService>(AudioRecorderService.new);
|
||||||
i.addSingleton<DeviceFileUploadService>(
|
i.addSingleton<DeviceFileUploadService>(
|
||||||
() => DeviceFileUploadService(
|
() => DeviceFileUploadService(
|
||||||
cameraService: i.get<CameraService>(),
|
cameraService: i.get<CameraService>(),
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:record/record.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
|
/// Service for recording audio using the device microphone.
|
||||||
|
class AudioRecorderService extends BaseDeviceService {
|
||||||
|
/// Creates an [AudioRecorderService].
|
||||||
|
AudioRecorderService() : _recorder = AudioRecorder();
|
||||||
|
|
||||||
|
final AudioRecorder _recorder;
|
||||||
|
|
||||||
|
/// Starts recording audio to a temporary file.
|
||||||
|
///
|
||||||
|
/// Returns the path where the audio is being recorded.
|
||||||
|
Future<void> startRecording() async {
|
||||||
|
return action(() async {
|
||||||
|
if (await _recorder.hasPermission()) {
|
||||||
|
final Directory tempDir = await getTemporaryDirectory();
|
||||||
|
final String path =
|
||||||
|
'${tempDir.path}/rapid_order_audio_${DateTime.now().millisecondsSinceEpoch}.m4a';
|
||||||
|
|
||||||
|
// Configure the recording
|
||||||
|
const RecordConfig config = RecordConfig(
|
||||||
|
encoder: AudioEncoder.aacLc, // Good balance of quality and size
|
||||||
|
bitRate: 128000,
|
||||||
|
sampleRate: 44100,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _recorder.start(config, path: path);
|
||||||
|
} else {
|
||||||
|
throw Exception('Microphone permission not granted');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the current recording.
|
||||||
|
///
|
||||||
|
/// Returns the path to the recorded audio file, or null if no recording was active.
|
||||||
|
Future<String?> stopRecording() async {
|
||||||
|
return action(() async {
|
||||||
|
return await _recorder.stop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the recorder is currently recording.
|
||||||
|
Future<bool> isRecording() async {
|
||||||
|
return await _recorder.isRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disposes the recorder resources.
|
||||||
|
void dispose() {
|
||||||
|
_recorder.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,4 +25,6 @@ dependencies:
|
|||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
path_provider: ^2.1.3
|
path_provider: ^2.1.3
|
||||||
file_picker: ^8.1.7
|
file_picker: ^8.1.7
|
||||||
|
record: ^5.2.0
|
||||||
firebase_auth: ^6.1.4
|
firebase_auth: ^6.1.4
|
||||||
|
|
||||||
|
|||||||
@@ -285,4 +285,7 @@ class UiIcons {
|
|||||||
|
|
||||||
/// Circle dollar icon
|
/// Circle dollar icon
|
||||||
static const IconData circleDollar = _IconLib.circleDollarSign;
|
static const IconData circleDollar = _IconLib.circleDollarSign;
|
||||||
|
|
||||||
|
/// Microphone icon
|
||||||
|
static const IconData microphone = _IconLib.mic;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'domain/usecases/create_permanent_order_usecase.dart';
|
|||||||
import 'domain/usecases/create_recurring_order_usecase.dart';
|
import 'domain/usecases/create_recurring_order_usecase.dart';
|
||||||
import 'domain/usecases/create_rapid_order_usecase.dart';
|
import 'domain/usecases/create_rapid_order_usecase.dart';
|
||||||
import 'domain/usecases/get_order_details_for_reorder_usecase.dart';
|
import 'domain/usecases/get_order_details_for_reorder_usecase.dart';
|
||||||
|
import 'domain/usecases/transcribe_rapid_order_usecase.dart';
|
||||||
import 'presentation/blocs/index.dart';
|
import 'presentation/blocs/index.dart';
|
||||||
import 'presentation/pages/create_order_page.dart';
|
import 'presentation/pages/create_order_page.dart';
|
||||||
import 'presentation/pages/one_time_order_page.dart';
|
import 'presentation/pages/one_time_order_page.dart';
|
||||||
@@ -23,7 +24,7 @@ import 'presentation/pages/recurring_order_page.dart';
|
|||||||
/// presentation layer BLoCs.
|
/// presentation layer BLoCs.
|
||||||
class ClientCreateOrderModule extends Module {
|
class ClientCreateOrderModule extends Module {
|
||||||
@override
|
@override
|
||||||
List<Module> get imports => <Module>[DataConnectModule()];
|
List<Module> get imports => <Module>[DataConnectModule(), CoreModule()];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
@@ -37,10 +38,17 @@ class ClientCreateOrderModule extends Module {
|
|||||||
i.addLazySingleton(CreatePermanentOrderUseCase.new);
|
i.addLazySingleton(CreatePermanentOrderUseCase.new);
|
||||||
i.addLazySingleton(CreateRecurringOrderUseCase.new);
|
i.addLazySingleton(CreateRecurringOrderUseCase.new);
|
||||||
i.addLazySingleton(CreateRapidOrderUseCase.new);
|
i.addLazySingleton(CreateRapidOrderUseCase.new);
|
||||||
|
i.addLazySingleton(TranscribeRapidOrderUseCase.new);
|
||||||
i.addLazySingleton(GetOrderDetailsForReorderUseCase.new);
|
i.addLazySingleton(GetOrderDetailsForReorderUseCase.new);
|
||||||
|
|
||||||
// BLoCs
|
// BLoCs
|
||||||
i.add<RapidOrderBloc>(RapidOrderBloc.new);
|
i.add<RapidOrderBloc>(
|
||||||
|
(Injector i) => RapidOrderBloc(
|
||||||
|
i.get<CreateRapidOrderUseCase>(),
|
||||||
|
i.get<TranscribeRapidOrderUseCase>(),
|
||||||
|
i.get<AudioRecorderService>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
|
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
|
||||||
i.add<PermanentOrderBloc>(PermanentOrderBloc.new);
|
i.add<PermanentOrderBloc>(PermanentOrderBloc.new);
|
||||||
i.add<RecurringOrderBloc>(RecurringOrderBloc.new);
|
i.add<RecurringOrderBloc>(RecurringOrderBloc.new);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:krow_core/core.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||||
import '../../domain/repositories/client_create_order_repository_interface.dart';
|
import '../../domain/repositories/client_create_order_repository_interface.dart';
|
||||||
@@ -13,10 +14,14 @@ import '../../domain/repositories/client_create_order_repository_interface.dart'
|
|||||||
/// on delegation and data mapping, without business logic.
|
/// on delegation and data mapping, without business logic.
|
||||||
class ClientCreateOrderRepositoryImpl
|
class ClientCreateOrderRepositoryImpl
|
||||||
implements ClientCreateOrderRepositoryInterface {
|
implements ClientCreateOrderRepositoryInterface {
|
||||||
ClientCreateOrderRepositoryImpl({required dc.DataConnectService service})
|
ClientCreateOrderRepositoryImpl({
|
||||||
: _service = service;
|
required dc.DataConnectService service,
|
||||||
|
required RapidOrderService rapidOrderService,
|
||||||
|
}) : _service = service,
|
||||||
|
_rapidOrderService = rapidOrderService;
|
||||||
|
|
||||||
final dc.DataConnectService _service;
|
final dc.DataConnectService _service;
|
||||||
|
final RapidOrderService _rapidOrderService;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> createOneTimeOrder(domain.OneTimeOrder order) async {
|
Future<void> createOneTimeOrder(domain.OneTimeOrder order) async {
|
||||||
@@ -367,6 +372,13 @@ class ClientCreateOrderRepositoryImpl
|
|||||||
throw UnimplementedError('Rapid order IA is not connected yet.');
|
throw UnimplementedError('Rapid order IA is not connected yet.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> transcribeRapidOrder(String audioPath) async {
|
||||||
|
final RapidOrderTranscriptionResponse response = await _rapidOrderService
|
||||||
|
.transcribeAudio(audioFileUri: audioPath);
|
||||||
|
return response.transcript;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> reorder(String previousOrderId, DateTime newDate) async {
|
Future<void> reorder(String previousOrderId, DateTime newDate) async {
|
||||||
// TODO: Implement reorder functionality to fetch the previous order and create a new one with the updated date.
|
// TODO: Implement reorder functionality to fetch the previous order and create a new one with the updated date.
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ abstract interface class ClientCreateOrderRepositoryInterface {
|
|||||||
/// [description] is the text message (or transcribed voice) describing the need.
|
/// [description] is the text message (or transcribed voice) describing the need.
|
||||||
Future<void> createRapidOrder(String description);
|
Future<void> createRapidOrder(String description);
|
||||||
|
|
||||||
|
/// Transcribes the audio file for a rapid order.
|
||||||
|
///
|
||||||
|
/// [audioPath] is the local path to the recorded audio file.
|
||||||
|
Future<String> transcribeRapidOrder(String audioPath);
|
||||||
|
|
||||||
/// Reorders an existing staffing order with a new date.
|
/// Reorders an existing staffing order with a new date.
|
||||||
///
|
///
|
||||||
/// [previousOrderId] is the ID of the order to reorder.
|
/// [previousOrderId] is the ID of the order to reorder.
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import '../repositories/client_create_order_repository_interface.dart';
|
||||||
|
|
||||||
|
/// Use case for transcribing audio for a rapid order.
|
||||||
|
class TranscribeRapidOrderUseCase {
|
||||||
|
/// Creates a [TranscribeRapidOrderUseCase].
|
||||||
|
TranscribeRapidOrderUseCase(this._repository);
|
||||||
|
|
||||||
|
final ClientCreateOrderRepositoryInterface _repository;
|
||||||
|
|
||||||
|
/// Executes the use case.
|
||||||
|
///
|
||||||
|
/// [audioPath] is the local path to the audio file.
|
||||||
|
Future<String> call(String audioPath) async {
|
||||||
|
return _repository.transcribeRapidOrder(audioPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:client_create_order/src/domain/arguments/rapid_order_arguments.dart';
|
import 'package:client_create_order/src/domain/arguments/rapid_order_arguments.dart';
|
||||||
import 'package:client_create_order/src/domain/usecases/create_rapid_order_usecase.dart';
|
import 'package:client_create_order/src/domain/usecases/create_rapid_order_usecase.dart';
|
||||||
|
import 'package:client_create_order/src/domain/usecases/transcribe_rapid_order_usecase.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
|
||||||
@@ -9,8 +10,11 @@ import 'rapid_order_state.dart';
|
|||||||
/// BLoC for managing the rapid (urgent) order creation flow.
|
/// BLoC for managing the rapid (urgent) order creation flow.
|
||||||
class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
||||||
with BlocErrorHandler<RapidOrderState> {
|
with BlocErrorHandler<RapidOrderState> {
|
||||||
RapidOrderBloc(this._createRapidOrderUseCase)
|
RapidOrderBloc(
|
||||||
: super(
|
this._createRapidOrderUseCase,
|
||||||
|
this._transcribeRapidOrderUseCase,
|
||||||
|
this._audioRecorderService,
|
||||||
|
) : super(
|
||||||
const RapidOrderInitial(
|
const RapidOrderInitial(
|
||||||
examples: <String>[
|
examples: <String>[
|
||||||
'"We had a call out. Need 2 cooks ASAP"',
|
'"We had a call out. Need 2 cooks ASAP"',
|
||||||
@@ -25,6 +29,8 @@ class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
|||||||
on<RapidOrderExampleSelected>(_onExampleSelected);
|
on<RapidOrderExampleSelected>(_onExampleSelected);
|
||||||
}
|
}
|
||||||
final CreateRapidOrderUseCase _createRapidOrderUseCase;
|
final CreateRapidOrderUseCase _createRapidOrderUseCase;
|
||||||
|
final TranscribeRapidOrderUseCase _transcribeRapidOrderUseCase;
|
||||||
|
final AudioRecorderService _audioRecorderService;
|
||||||
|
|
||||||
void _onMessageChanged(
|
void _onMessageChanged(
|
||||||
RapidOrderMessageChanged event,
|
RapidOrderMessageChanged event,
|
||||||
@@ -43,19 +49,31 @@ class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
|||||||
final RapidOrderInitial currentState = state as RapidOrderInitial;
|
final RapidOrderInitial currentState = state as RapidOrderInitial;
|
||||||
final bool newListeningState = !currentState.isListening;
|
final bool newListeningState = !currentState.isListening;
|
||||||
|
|
||||||
emit(currentState.copyWith(isListening: newListeningState));
|
|
||||||
|
|
||||||
// Simulate voice recognition
|
|
||||||
if (newListeningState) {
|
if (newListeningState) {
|
||||||
await Future<void>.delayed(const Duration(seconds: 2));
|
// Start Recording
|
||||||
if (state is RapidOrderInitial) {
|
await _audioRecorderService.startRecording();
|
||||||
emit(
|
emit(currentState.copyWith(isListening: true));
|
||||||
(state as RapidOrderInitial).copyWith(
|
} else {
|
||||||
message: 'Need 2 servers for a banquet right now.',
|
// Stop Recording and Transcribe
|
||||||
isListening: false,
|
emit(currentState.copyWith(isListening: false));
|
||||||
),
|
|
||||||
);
|
await handleError(
|
||||||
}
|
emit: emit.call,
|
||||||
|
action: () async {
|
||||||
|
final String? path = await _audioRecorderService.stopRecording();
|
||||||
|
if (path != null) {
|
||||||
|
final String transcript = await _transcribeRapidOrderUseCase(
|
||||||
|
path,
|
||||||
|
);
|
||||||
|
if (state is RapidOrderInitial) {
|
||||||
|
emit(
|
||||||
|
(state as RapidOrderInitial).copyWith(message: transcript),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (String errorKey) => RapidOrderFailure(errorKey),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,5 +109,10 @@ class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
|||||||
emit((state as RapidOrderInitial).copyWith(message: cleanedExample));
|
emit((state as RapidOrderInitial).copyWith(message: cleanedExample));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_audioRecorderService.dispose();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class RapidOrderView extends StatelessWidget {
|
|||||||
title: labels.success_title,
|
title: labels.success_title,
|
||||||
message: labels.success_message,
|
message: labels.success_message,
|
||||||
buttonLabel: labels.back_to_orders,
|
buttonLabel: labels.back_to_orders,
|
||||||
onDone: () => Modular.to.navigate(ClientPaths.orders),
|
onDone: () => Modular.to.toClientOrders(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ class _RapidOrderActions extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: UiButton.secondary(
|
child: UiButton.secondary(
|
||||||
text: isListening ? labels.listening : labels.speak,
|
text: isListening ? labels.listening : labels.speak,
|
||||||
leadingIcon: UiIcons.bell, // Placeholder for mic
|
leadingIcon: UiIcons.microphone,
|
||||||
onPressed: () => BlocProvider.of<RapidOrderBloc>(
|
onPressed: () => BlocProvider.of<RapidOrderBloc>(
|
||||||
context,
|
context,
|
||||||
).add(const RapidOrderVoiceToggled()),
|
).add(const RapidOrderVoiceToggled()),
|
||||||
|
|||||||
@@ -7,9 +7,13 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
|
#include <record_linux/record_linux_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
||||||
|
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
|
record_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import file_selector_macos
|
|||||||
import firebase_app_check
|
import firebase_app_check
|
||||||
import firebase_auth
|
import firebase_auth
|
||||||
import firebase_core
|
import firebase_core
|
||||||
|
import record_darwin
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
@@ -18,5 +19,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <firebase_auth/firebase_auth_plugin_c_api.h>
|
#include <firebase_auth/firebase_auth_plugin_c_api.h>
|
||||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
|
#include <record_windows/record_windows_plugin_c_api.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
@@ -17,4 +18,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi"));
|
registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi"));
|
||||||
FirebaseCorePluginCApiRegisterWithRegistrar(
|
FirebaseCorePluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
||||||
|
RecordWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
file_selector_windows
|
file_selector_windows
|
||||||
firebase_auth
|
firebase_auth
|
||||||
firebase_core
|
firebase_core
|
||||||
|
record_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -1221,6 +1221,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.0"
|
||||||
|
record:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record
|
||||||
|
sha256: "2e3d56d196abcd69f1046339b75e5f3855b2406fc087e5991f6703f188aa03a6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.2.1"
|
||||||
|
record_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_android
|
||||||
|
sha256: "94783f08403aed33ffb68797bf0715b0812eb852f3c7985644c945faea462ba1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.1"
|
||||||
|
record_darwin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_darwin
|
||||||
|
sha256: e487eccb19d82a9a39cd0126945cfc47b9986e0df211734e2788c95e3f63c82c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
|
record_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_linux
|
||||||
|
sha256: "74d41a9ebb1eb498a38e9a813dd524e8f0b4fdd627270bda9756f437b110a3e3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.2"
|
||||||
|
record_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_platform_interface
|
||||||
|
sha256: "8a81dbc4e14e1272a285bbfef6c9136d070a47d9b0d1f40aa6193516253ee2f6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.0"
|
||||||
|
record_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_web
|
||||||
|
sha256: "7e9846981c1f2d111d86f0ae3309071f5bba8b624d1c977316706f08fc31d16d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
record_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_windows
|
||||||
|
sha256: "223258060a1d25c62bae18282c16783f28581ec19401d17e56b5205b9f039d78"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.7"
|
||||||
rename:
|
rename:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user