diff options
author | Anuj-S62 <anuj603362@gmail.com> | 2024-07-11 15:18:31 +0530 |
---|---|---|
committer | Anuj-S62 <anuj603362@gmail.com> | 2024-07-21 19:52:11 +0530 |
commit | 0b59086cdddd40689e57969aa7914ba38f1ec2dd (patch) | |
tree | 975c38663f57d497eeab0e2649e51308aef9a755 | |
parent | ecd34435c1a74b39bf41d59ad479fdc85d0afb7b (diff) |
Update Voice Agent Flutter App
- update voice-agent flutter app to use whisper AI for
speech-to-text functionality.
- Integrated SharedPreferences to store the application state.
Bug-AGL: SPEC-5200
Change-Id: I9a05b1d135c1fa07949333391ff828f166b7fe8e
Signed-off-by: Anuj-S62 <anuj603362@gmail.com>
-rw-r--r-- | .metadata | 25 | ||||
-rw-r--r-- | lib/grpc/generated/voice_agent.pb.dart | 70 | ||||
-rw-r--r-- | lib/grpc/generated/voice_agent.pbenum.dart | 30 | ||||
-rw-r--r-- | lib/grpc/generated/voice_agent.pbgrpc.dart | 176 | ||||
-rw-r--r-- | lib/grpc/generated/voice_agent.pbjson.dart | 74 | ||||
-rw-r--r-- | lib/main.dart | 3 | ||||
-rw-r--r-- | lib/models/app_state.dart | 2 | ||||
-rw-r--r-- | lib/protos/voice_agent.proto | 14 | ||||
-rw-r--r-- | lib/screens/home_screen.dart | 380 | ||||
-rw-r--r-- | lib/screens/loading_screen.dart | 81 | ||||
-rw-r--r-- | lib/widgets/assistant_mode_choice.dart | 8 | ||||
-rw-r--r-- | lib/widgets/nlu_engine_choice.dart | 6 | ||||
-rw-r--r-- | lib/widgets/online_mode_choice.dart | 148 | ||||
-rw-r--r-- | lib/widgets/stt_model_choice.dart | 148 | ||||
-rw-r--r-- | pubspec.lock | 205 | ||||
-rw-r--r-- | pubspec.yaml | 1 |
16 files changed, 1132 insertions, 239 deletions
@@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a" + revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" channel: "stable" project_type: app @@ -13,11 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + - platform: android + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + - platform: ios + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 - platform: linux - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + - platform: macos + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + - platform: web + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + - platform: windows + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 # User provided section diff --git a/lib/grpc/generated/voice_agent.pb.dart b/lib/grpc/generated/voice_agent.pb.dart index 2c0f9fa..892b95e 100644 --- a/lib/grpc/generated/voice_agent.pb.dart +++ b/lib/grpc/generated/voice_agent.pb.dart @@ -9,6 +9,7 @@ // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import +import 'dart:async' as $async; import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; @@ -274,6 +275,7 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { VoiceAudio? audioStream, NLUModel? nluModel, $core.String? streamId, + STTFramework? sttFramework, }) { final $result = create(); if (audioStream != null) { @@ -285,6 +287,9 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { if (streamId != null) { $result.streamId = streamId; } + if (sttFramework != null) { + $result.sttFramework = sttFramework; + } return $result; } S_RecognizeVoiceControl._() : super(); @@ -295,6 +300,7 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { ..aOM<VoiceAudio>(1, _omitFieldNames ? '' : 'audioStream', subBuilder: VoiceAudio.create) ..e<NLUModel>(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) ..aOS(3, _omitFieldNames ? '' : 'streamId') + ..e<STTFramework>(4, _omitFieldNames ? '' : 'sttFramework', $pb.PbFieldType.OE, defaultOrMaker: STTFramework.VOSK, valueOf: STTFramework.valueOf, enumValues: STTFramework.values) ..hasRequiredFields = false ; @@ -347,6 +353,15 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { $core.bool hasStreamId() => $_has(2); @$pb.TagNumber(3) void clearStreamId() => clearField(3); + + @$pb.TagNumber(4) + STTFramework get sttFramework => $_getN(3); + @$pb.TagNumber(4) + set sttFramework(STTFramework v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasSttFramework() => $_has(3); + @$pb.TagNumber(4) + void clearSttFramework() => clearField(4); } class RecognizeVoiceControl extends $pb.GeneratedMessage { @@ -355,6 +370,8 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { NLUModel? nluModel, RecordMode? recordMode, $core.String? streamId, + STTFramework? sttFramework, + OnlineMode? onlineMode, }) { final $result = create(); if (action != null) { @@ -369,6 +386,12 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { if (streamId != null) { $result.streamId = streamId; } + if (sttFramework != null) { + $result.sttFramework = sttFramework; + } + if (onlineMode != null) { + $result.onlineMode = onlineMode; + } return $result; } RecognizeVoiceControl._() : super(); @@ -380,6 +403,8 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { ..e<NLUModel>(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) ..e<RecordMode>(3, _omitFieldNames ? '' : 'recordMode', $pb.PbFieldType.OE, defaultOrMaker: RecordMode.MANUAL, valueOf: RecordMode.valueOf, enumValues: RecordMode.values) ..aOS(4, _omitFieldNames ? '' : 'streamId') + ..e<STTFramework>(5, _omitFieldNames ? '' : 'sttFramework', $pb.PbFieldType.OE, defaultOrMaker: STTFramework.VOSK, valueOf: STTFramework.valueOf, enumValues: STTFramework.values) + ..e<OnlineMode>(6, _omitFieldNames ? '' : 'onlineMode', $pb.PbFieldType.OE, defaultOrMaker: OnlineMode.ONLINE, valueOf: OnlineMode.valueOf, enumValues: OnlineMode.values) ..hasRequiredFields = false ; @@ -439,6 +464,24 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { $core.bool hasStreamId() => $_has(3); @$pb.TagNumber(4) void clearStreamId() => clearField(4); + + @$pb.TagNumber(5) + STTFramework get sttFramework => $_getN(4); + @$pb.TagNumber(5) + set sttFramework(STTFramework v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasSttFramework() => $_has(4); + @$pb.TagNumber(5) + void clearSttFramework() => clearField(5); + + @$pb.TagNumber(6) + OnlineMode get onlineMode => $_getN(5); + @$pb.TagNumber(6) + set onlineMode(OnlineMode v) { setField(6, v); } + @$pb.TagNumber(6) + $core.bool hasOnlineMode() => $_has(5); + @$pb.TagNumber(6) + void clearOnlineMode() => clearField(6); } class RecognizeTextControl extends $pb.GeneratedMessage { @@ -791,6 +834,33 @@ class ExecuteResult extends $pb.GeneratedMessage { void clearStatus() => clearField(2); } +class VoiceAgentServiceApi { + $pb.RpcClient _client; + VoiceAgentServiceApi(this._client); + + $async.Future<ServiceStatus> checkServiceStatus($pb.ClientContext? ctx, Empty request) => + _client.invoke<ServiceStatus>(ctx, 'VoiceAgentService', 'CheckServiceStatus', request, ServiceStatus()) + ; + $async.Future<WakeWordStatus> s_DetectWakeWord($pb.ClientContext? ctx, VoiceAudio request) => + _client.invoke<WakeWordStatus>(ctx, 'VoiceAgentService', 'S_DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future<WakeWordStatus> detectWakeWord($pb.ClientContext? ctx, Empty request) => + _client.invoke<WakeWordStatus>(ctx, 'VoiceAgentService', 'DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future<RecognizeResult> s_RecognizeVoiceCommand($pb.ClientContext? ctx, S_RecognizeVoiceControl request) => + _client.invoke<RecognizeResult>(ctx, 'VoiceAgentService', 'S_RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future<RecognizeResult> recognizeVoiceCommand($pb.ClientContext? ctx, RecognizeVoiceControl request) => + _client.invoke<RecognizeResult>(ctx, 'VoiceAgentService', 'RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future<RecognizeResult> recognizeTextCommand($pb.ClientContext? ctx, RecognizeTextControl request) => + _client.invoke<RecognizeResult>(ctx, 'VoiceAgentService', 'RecognizeTextCommand', request, RecognizeResult()) + ; + $async.Future<ExecuteResult> executeCommand($pb.ClientContext? ctx, ExecuteInput request) => + _client.invoke<ExecuteResult>(ctx, 'VoiceAgentService', 'ExecuteCommand', request, ExecuteResult()) + ; +} + const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/grpc/generated/voice_agent.pbenum.dart b/lib/grpc/generated/voice_agent.pbenum.dart index 78da13f..51e7427 100644 --- a/lib/grpc/generated/voice_agent.pbenum.dart +++ b/lib/grpc/generated/voice_agent.pbenum.dart @@ -13,6 +13,36 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; +class STTFramework extends $pb.ProtobufEnum { + static const STTFramework VOSK = STTFramework._(0, _omitEnumNames ? '' : 'VOSK'); + static const STTFramework WHISPER = STTFramework._(1, _omitEnumNames ? '' : 'WHISPER'); + + static const $core.List<STTFramework> values = <STTFramework> [ + VOSK, + WHISPER, + ]; + + static final $core.Map<$core.int, STTFramework> _byValue = $pb.ProtobufEnum.initByValue(values); + static STTFramework? valueOf($core.int value) => _byValue[value]; + + const STTFramework._($core.int v, $core.String n) : super(v, n); +} + +class OnlineMode extends $pb.ProtobufEnum { + static const OnlineMode ONLINE = OnlineMode._(0, _omitEnumNames ? '' : 'ONLINE'); + static const OnlineMode OFFLINE = OnlineMode._(1, _omitEnumNames ? '' : 'OFFLINE'); + + static const $core.List<OnlineMode> values = <OnlineMode> [ + ONLINE, + OFFLINE, + ]; + + static final $core.Map<$core.int, OnlineMode> _byValue = $pb.ProtobufEnum.initByValue(values); + static OnlineMode? valueOf($core.int value) => _byValue[value]; + + const OnlineMode._($core.int v, $core.String n) : super(v, n); +} + class RecordAction extends $pb.ProtobufEnum { static const RecordAction START = RecordAction._(0, _omitEnumNames ? '' : 'START'); static const RecordAction STOP = RecordAction._(1, _omitEnumNames ? '' : 'STOP'); diff --git a/lib/grpc/generated/voice_agent.pbgrpc.dart b/lib/grpc/generated/voice_agent.pbgrpc.dart index 502c797..5b31b44 100644 --- a/lib/grpc/generated/voice_agent.pbgrpc.dart +++ b/lib/grpc/generated/voice_agent.pbgrpc.dart @@ -21,92 +21,66 @@ export 'voice_agent.pb.dart'; // @$pb.GrpcServiceName('VoiceAgentService') class VoiceAgentServiceClient extends $grpc.Client { - static final _$checkServiceStatus = - $grpc.ClientMethod<$0.Empty, $0.ServiceStatus>( - '/VoiceAgentService/CheckServiceStatus', - ($0.Empty value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.ServiceStatus.fromBuffer(value)); - static final _$s_DetectWakeWord = - $grpc.ClientMethod<$0.VoiceAudio, $0.WakeWordStatus>( - '/VoiceAgentService/S_DetectWakeWord', - ($0.VoiceAudio value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); - static final _$detectWakeWord = - $grpc.ClientMethod<$0.Empty, $0.WakeWordStatus>( - '/VoiceAgentService/DetectWakeWord', - ($0.Empty value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); - static final _$s_RecognizeVoiceCommand = - $grpc.ClientMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( - '/VoiceAgentService/S_RecognizeVoiceCommand', - ($0.S_RecognizeVoiceControl value) => value.writeToBuffer(), - ($core.List<$core.int> value) => - $0.RecognizeResult.fromBuffer(value)); - static final _$recognizeVoiceCommand = - $grpc.ClientMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( - '/VoiceAgentService/RecognizeVoiceCommand', - ($0.RecognizeVoiceControl value) => value.writeToBuffer(), - ($core.List<$core.int> value) => - $0.RecognizeResult.fromBuffer(value)); - static final _$recognizeTextCommand = - $grpc.ClientMethod<$0.RecognizeTextControl, $0.RecognizeResult>( - '/VoiceAgentService/RecognizeTextCommand', - ($0.RecognizeTextControl value) => value.writeToBuffer(), - ($core.List<$core.int> value) => - $0.RecognizeResult.fromBuffer(value)); - static final _$executeCommand = - $grpc.ClientMethod<$0.ExecuteInput, $0.ExecuteResult>( - '/VoiceAgentService/ExecuteCommand', - ($0.ExecuteInput value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.ExecuteResult.fromBuffer(value)); + static final _$checkServiceStatus = $grpc.ClientMethod<$0.Empty, $0.ServiceStatus>( + '/VoiceAgentService/CheckServiceStatus', + ($0.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ServiceStatus.fromBuffer(value)); + static final _$s_DetectWakeWord = $grpc.ClientMethod<$0.VoiceAudio, $0.WakeWordStatus>( + '/VoiceAgentService/S_DetectWakeWord', + ($0.VoiceAudio value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); + static final _$detectWakeWord = $grpc.ClientMethod<$0.Empty, $0.WakeWordStatus>( + '/VoiceAgentService/DetectWakeWord', + ($0.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); + static final _$s_RecognizeVoiceCommand = $grpc.ClientMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( + '/VoiceAgentService/S_RecognizeVoiceCommand', + ($0.S_RecognizeVoiceControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$recognizeVoiceCommand = $grpc.ClientMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( + '/VoiceAgentService/RecognizeVoiceCommand', + ($0.RecognizeVoiceControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$recognizeTextCommand = $grpc.ClientMethod<$0.RecognizeTextControl, $0.RecognizeResult>( + '/VoiceAgentService/RecognizeTextCommand', + ($0.RecognizeTextControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$executeCommand = $grpc.ClientMethod<$0.ExecuteInput, $0.ExecuteResult>( + '/VoiceAgentService/ExecuteCommand', + ($0.ExecuteInput value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ExecuteResult.fromBuffer(value)); VoiceAgentServiceClient($grpc.ClientChannel channel, {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) - : super(channel, options: options, interceptors: interceptors); + : super(channel, options: options, + interceptors: interceptors); - $grpc.ResponseFuture<$0.ServiceStatus> checkServiceStatus($0.Empty request, - {$grpc.CallOptions? options}) { + $grpc.ResponseFuture<$0.ServiceStatus> checkServiceStatus($0.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$checkServiceStatus, request, options: options); } - $grpc.ResponseStream<$0.WakeWordStatus> s_DetectWakeWord( - $async.Stream<$0.VoiceAudio> request, - {$grpc.CallOptions? options}) { + $grpc.ResponseStream<$0.WakeWordStatus> s_DetectWakeWord($async.Stream<$0.VoiceAudio> request, {$grpc.CallOptions? options}) { return $createStreamingCall(_$s_DetectWakeWord, request, options: options); } - $grpc.ResponseStream<$0.WakeWordStatus> detectWakeWord($0.Empty request, - {$grpc.CallOptions? options}) { - return $createStreamingCall( - _$detectWakeWord, $async.Stream.fromIterable([request]), - options: options); + $grpc.ResponseStream<$0.WakeWordStatus> detectWakeWord($0.Empty request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$detectWakeWord, $async.Stream.fromIterable([request]), options: options); } - $grpc.ResponseFuture<$0.RecognizeResult> s_RecognizeVoiceCommand( - $async.Stream<$0.S_RecognizeVoiceControl> request, - {$grpc.CallOptions? options}) { - return $createStreamingCall(_$s_RecognizeVoiceCommand, request, - options: options) - .single; + $grpc.ResponseFuture<$0.RecognizeResult> s_RecognizeVoiceCommand($async.Stream<$0.S_RecognizeVoiceControl> request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$s_RecognizeVoiceCommand, request, options: options).single; } - $grpc.ResponseFuture<$0.RecognizeResult> recognizeVoiceCommand( - $async.Stream<$0.RecognizeVoiceControl> request, - {$grpc.CallOptions? options}) { - return $createStreamingCall(_$recognizeVoiceCommand, request, - options: options) - .single; + $grpc.ResponseFuture<$0.RecognizeResult> recognizeVoiceCommand($async.Stream<$0.RecognizeVoiceControl> request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$recognizeVoiceCommand, request, options: options).single; } - $grpc.ResponseFuture<$0.RecognizeResult> recognizeTextCommand( - $0.RecognizeTextControl request, - {$grpc.CallOptions? options}) { + $grpc.ResponseFuture<$0.RecognizeResult> recognizeTextCommand($0.RecognizeTextControl request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$recognizeTextCommand, request, options: options); } - $grpc.ResponseFuture<$0.ExecuteResult> executeCommand($0.ExecuteInput request, - {$grpc.CallOptions? options}) { + $grpc.ResponseFuture<$0.ExecuteResult> executeCommand($0.ExecuteInput request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$executeCommand, request, options: options); } } @@ -137,31 +111,26 @@ abstract class VoiceAgentServiceBase extends $grpc.Service { true, ($core.List<$core.int> value) => $0.Empty.fromBuffer(value), ($0.WakeWordStatus value) => value.writeToBuffer())); - $addMethod( - $grpc.ServiceMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( - 'S_RecognizeVoiceCommand', - s_RecognizeVoiceCommand, - true, - false, - ($core.List<$core.int> value) => - $0.S_RecognizeVoiceControl.fromBuffer(value), - ($0.RecognizeResult value) => value.writeToBuffer())); - $addMethod( - $grpc.ServiceMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( - 'RecognizeVoiceCommand', - recognizeVoiceCommand, - true, - false, - ($core.List<$core.int> value) => - $0.RecognizeVoiceControl.fromBuffer(value), - ($0.RecognizeResult value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( + 'S_RecognizeVoiceCommand', + s_RecognizeVoiceCommand, + true, + false, + ($core.List<$core.int> value) => $0.S_RecognizeVoiceControl.fromBuffer(value), + ($0.RecognizeResult value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( + 'RecognizeVoiceCommand', + recognizeVoiceCommand, + true, + false, + ($core.List<$core.int> value) => $0.RecognizeVoiceControl.fromBuffer(value), + ($0.RecognizeResult value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.RecognizeTextControl, $0.RecognizeResult>( 'RecognizeTextCommand', recognizeTextCommand_Pre, false, false, - ($core.List<$core.int> value) => - $0.RecognizeTextControl.fromBuffer(value), + ($core.List<$core.int> value) => $0.RecognizeTextControl.fromBuffer(value), ($0.RecognizeResult value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.ExecuteInput, $0.ExecuteResult>( 'ExecuteCommand', @@ -172,40 +141,27 @@ abstract class VoiceAgentServiceBase extends $grpc.Service { ($0.ExecuteResult value) => value.writeToBuffer())); } - $async.Future<$0.ServiceStatus> checkServiceStatus_Pre( - $grpc.ServiceCall call, $async.Future<$0.Empty> request) async { + $async.Future<$0.ServiceStatus> checkServiceStatus_Pre($grpc.ServiceCall call, $async.Future<$0.Empty> request) async { return checkServiceStatus(call, await request); } - $async.Stream<$0.WakeWordStatus> detectWakeWord_Pre( - $grpc.ServiceCall call, $async.Future<$0.Empty> request) async* { + $async.Stream<$0.WakeWordStatus> detectWakeWord_Pre($grpc.ServiceCall call, $async.Future<$0.Empty> request) async* { yield* detectWakeWord(call, await request); } - $async.Future<$0.RecognizeResult> recognizeTextCommand_Pre( - $grpc.ServiceCall call, - $async.Future<$0.RecognizeTextControl> request) async { + $async.Future<$0.RecognizeResult> recognizeTextCommand_Pre($grpc.ServiceCall call, $async.Future<$0.RecognizeTextControl> request) async { return recognizeTextCommand(call, await request); } - $async.Future<$0.ExecuteResult> executeCommand_Pre( - $grpc.ServiceCall call, $async.Future<$0.ExecuteInput> request) async { + $async.Future<$0.ExecuteResult> executeCommand_Pre($grpc.ServiceCall call, $async.Future<$0.ExecuteInput> request) async { return executeCommand(call, await request); } - $async.Future<$0.ServiceStatus> checkServiceStatus( - $grpc.ServiceCall call, $0.Empty request); - $async.Stream<$0.WakeWordStatus> s_DetectWakeWord( - $grpc.ServiceCall call, $async.Stream<$0.VoiceAudio> request); - $async.Stream<$0.WakeWordStatus> detectWakeWord( - $grpc.ServiceCall call, $0.Empty request); - $async.Future<$0.RecognizeResult> s_RecognizeVoiceCommand( - $grpc.ServiceCall call, - $async.Stream<$0.S_RecognizeVoiceControl> request); - $async.Future<$0.RecognizeResult> recognizeVoiceCommand( - $grpc.ServiceCall call, $async.Stream<$0.RecognizeVoiceControl> request); - $async.Future<$0.RecognizeResult> recognizeTextCommand( - $grpc.ServiceCall call, $0.RecognizeTextControl request); - $async.Future<$0.ExecuteResult> executeCommand( - $grpc.ServiceCall call, $0.ExecuteInput request); + $async.Future<$0.ServiceStatus> checkServiceStatus($grpc.ServiceCall call, $0.Empty request); + $async.Stream<$0.WakeWordStatus> s_DetectWakeWord($grpc.ServiceCall call, $async.Stream<$0.VoiceAudio> request); + $async.Stream<$0.WakeWordStatus> detectWakeWord($grpc.ServiceCall call, $0.Empty request); + $async.Future<$0.RecognizeResult> s_RecognizeVoiceCommand($grpc.ServiceCall call, $async.Stream<$0.S_RecognizeVoiceControl> request); + $async.Future<$0.RecognizeResult> recognizeVoiceCommand($grpc.ServiceCall call, $async.Stream<$0.RecognizeVoiceControl> request); + $async.Future<$0.RecognizeResult> recognizeTextCommand($grpc.ServiceCall call, $0.RecognizeTextControl request); + $async.Future<$0.ExecuteResult> executeCommand($grpc.ServiceCall call, $0.ExecuteInput request); } diff --git a/lib/grpc/generated/voice_agent.pbjson.dart b/lib/grpc/generated/voice_agent.pbjson.dart index 6fa9502..1b8365d 100644 --- a/lib/grpc/generated/voice_agent.pbjson.dart +++ b/lib/grpc/generated/voice_agent.pbjson.dart @@ -13,6 +13,32 @@ import 'dart:convert' as $convert; import 'dart:core' as $core; import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use sTTFrameworkDescriptor instead') +const STTFramework$json = { + '1': 'STTFramework', + '2': [ + {'1': 'VOSK', '2': 0}, + {'1': 'WHISPER', '2': 1}, + ], +}; + +/// Descriptor for `STTFramework`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List sTTFrameworkDescriptor = $convert.base64Decode( + 'CgxTVFRGcmFtZXdvcmsSCAoEVk9TSxAAEgsKB1dISVNQRVIQAQ=='); + +@$core.Deprecated('Use onlineModeDescriptor instead') +const OnlineMode$json = { + '1': 'OnlineMode', + '2': [ + {'1': 'ONLINE', '2': 0}, + {'1': 'OFFLINE', '2': 1}, + ], +}; + +/// Descriptor for `OnlineMode`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List onlineModeDescriptor = $convert.base64Decode( + 'CgpPbmxpbmVNb2RlEgoKBk9OTElORRAAEgsKB09GRkxJTkUQAQ=='); + @$core.Deprecated('Use recordActionDescriptor instead') const RecordAction$json = { '1': 'RecordAction', @@ -151,6 +177,7 @@ const S_RecognizeVoiceControl$json = { {'1': 'audio_stream', '3': 1, '4': 1, '5': 11, '6': '.VoiceAudio', '10': 'audioStream'}, {'1': 'nlu_model', '3': 2, '4': 1, '5': 14, '6': '.NLUModel', '10': 'nluModel'}, {'1': 'stream_id', '3': 3, '4': 1, '5': 9, '10': 'streamId'}, + {'1': 'stt_framework', '3': 4, '4': 1, '5': 14, '6': '.STTFramework', '10': 'sttFramework'}, ], }; @@ -158,7 +185,8 @@ const S_RecognizeVoiceControl$json = { final $typed_data.Uint8List s_RecognizeVoiceControlDescriptor = $convert.base64Decode( 'ChdTX1JlY29nbml6ZVZvaWNlQ29udHJvbBIuCgxhdWRpb19zdHJlYW0YASABKAsyCy5Wb2ljZU' 'F1ZGlvUgthdWRpb1N0cmVhbRImCglubHVfbW9kZWwYAiABKA4yCS5OTFVNb2RlbFIIbmx1TW9k' - 'ZWwSGwoJc3RyZWFtX2lkGAMgASgJUghzdHJlYW1JZA=='); + 'ZWwSGwoJc3RyZWFtX2lkGAMgASgJUghzdHJlYW1JZBIyCg1zdHRfZnJhbWV3b3JrGAQgASgOMg' + '0uU1RURnJhbWV3b3JrUgxzdHRGcmFtZXdvcms='); @$core.Deprecated('Use recognizeVoiceControlDescriptor instead') const RecognizeVoiceControl$json = { @@ -168,6 +196,8 @@ const RecognizeVoiceControl$json = { {'1': 'nlu_model', '3': 2, '4': 1, '5': 14, '6': '.NLUModel', '10': 'nluModel'}, {'1': 'record_mode', '3': 3, '4': 1, '5': 14, '6': '.RecordMode', '10': 'recordMode'}, {'1': 'stream_id', '3': 4, '4': 1, '5': 9, '10': 'streamId'}, + {'1': 'stt_framework', '3': 5, '4': 1, '5': 14, '6': '.STTFramework', '10': 'sttFramework'}, + {'1': 'online_mode', '3': 6, '4': 1, '5': 14, '6': '.OnlineMode', '10': 'onlineMode'}, ], }; @@ -176,7 +206,8 @@ final $typed_data.Uint8List recognizeVoiceControlDescriptor = $convert.base64Dec 'ChVSZWNvZ25pemVWb2ljZUNvbnRyb2wSJQoGYWN0aW9uGAEgASgOMg0uUmVjb3JkQWN0aW9uUg' 'ZhY3Rpb24SJgoJbmx1X21vZGVsGAIgASgOMgkuTkxVTW9kZWxSCG5sdU1vZGVsEiwKC3JlY29y' 'ZF9tb2RlGAMgASgOMgsuUmVjb3JkTW9kZVIKcmVjb3JkTW9kZRIbCglzdHJlYW1faWQYBCABKA' - 'lSCHN0cmVhbUlk'); + 'lSCHN0cmVhbUlkEjIKDXN0dF9mcmFtZXdvcmsYBSABKA4yDS5TVFRGcmFtZXdvcmtSDHN0dEZy' + 'YW1ld29yaxIsCgtvbmxpbmVfbW9kZRgGIAEoDjILLk9ubGluZU1vZGVSCm9ubGluZU1vZGU='); @$core.Deprecated('Use recognizeTextControlDescriptor instead') const RecognizeTextControl$json = { @@ -252,3 +283,42 @@ final $typed_data.Uint8List executeResultDescriptor = $convert.base64Decode( 'Cg1FeGVjdXRlUmVzdWx0EhoKCHJlc3BvbnNlGAEgASgJUghyZXNwb25zZRIqCgZzdGF0dXMYAi' 'ABKA4yEi5FeGVjdXRlU3RhdHVzVHlwZVIGc3RhdHVz'); +const $core.Map<$core.String, $core.dynamic> VoiceAgentServiceBase$json = { + '1': 'VoiceAgentService', + '2': [ + {'1': 'CheckServiceStatus', '2': '.Empty', '3': '.ServiceStatus'}, + {'1': 'S_DetectWakeWord', '2': '.VoiceAudio', '3': '.WakeWordStatus', '5': true, '6': true}, + {'1': 'DetectWakeWord', '2': '.Empty', '3': '.WakeWordStatus', '6': true}, + {'1': 'S_RecognizeVoiceCommand', '2': '.S_RecognizeVoiceControl', '3': '.RecognizeResult', '5': true}, + {'1': 'RecognizeVoiceCommand', '2': '.RecognizeVoiceControl', '3': '.RecognizeResult', '5': true}, + {'1': 'RecognizeTextCommand', '2': '.RecognizeTextControl', '3': '.RecognizeResult'}, + {'1': 'ExecuteCommand', '2': '.ExecuteInput', '3': '.ExecuteResult'}, + ], +}; + +@$core.Deprecated('Use voiceAgentServiceDescriptor instead') +const $core.Map<$core.String, $core.Map<$core.String, $core.dynamic>> VoiceAgentServiceBase$messageJson = { + '.Empty': Empty$json, + '.ServiceStatus': ServiceStatus$json, + '.VoiceAudio': VoiceAudio$json, + '.WakeWordStatus': WakeWordStatus$json, + '.S_RecognizeVoiceControl': S_RecognizeVoiceControl$json, + '.RecognizeResult': RecognizeResult$json, + '.IntentSlot': IntentSlot$json, + '.RecognizeVoiceControl': RecognizeVoiceControl$json, + '.RecognizeTextControl': RecognizeTextControl$json, + '.ExecuteInput': ExecuteInput$json, + '.ExecuteResult': ExecuteResult$json, +}; + +/// Descriptor for `VoiceAgentService`. Decode as a `google.protobuf.ServiceDescriptorProto`. +final $typed_data.Uint8List voiceAgentServiceDescriptor = $convert.base64Decode( + 'ChFWb2ljZUFnZW50U2VydmljZRIsChJDaGVja1NlcnZpY2VTdGF0dXMSBi5FbXB0eRoOLlNlcn' + 'ZpY2VTdGF0dXMSNAoQU19EZXRlY3RXYWtlV29yZBILLlZvaWNlQXVkaW8aDy5XYWtlV29yZFN0' + 'YXR1cygBMAESKwoORGV0ZWN0V2FrZVdvcmQSBi5FbXB0eRoPLldha2VXb3JkU3RhdHVzMAESRw' + 'oXU19SZWNvZ25pemVWb2ljZUNvbW1hbmQSGC5TX1JlY29nbml6ZVZvaWNlQ29udHJvbBoQLlJl' + 'Y29nbml6ZVJlc3VsdCgBEkMKFVJlY29nbml6ZVZvaWNlQ29tbWFuZBIWLlJlY29nbml6ZVZvaW' + 'NlQ29udHJvbBoQLlJlY29nbml6ZVJlc3VsdCgBEj8KFFJlY29nbml6ZVRleHRDb21tYW5kEhUu' + 'UmVjb2duaXplVGV4dENvbnRyb2waEC5SZWNvZ25pemVSZXN1bHQSLwoORXhlY3V0ZUNvbW1hbm' + 'QSDS5FeGVjdXRlSW5wdXQaDi5FeGVjdXRlUmVzdWx0'); + diff --git a/lib/main.dart b/lib/main.dart index 9dfbd54..6349d8d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_voiceassistant/screens/loading_screen.dart'; import 'package:provider/provider.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'models/app_state.dart'; @@ -103,7 +104,7 @@ class App extends StatelessWidget { Consumer<ServiceStatusProvider>( builder: (context, provider, child) { return provider.isServiceOnline - ? HomePage(config: config, wakeWord: provider.wakeWord) + ? Loading(config: config, wakeWord: provider.wakeWord) : ErrorScreen( onRetry: onRetry, ); diff --git a/lib/models/app_state.dart b/lib/models/app_state.dart index 709b445..8325b50 100644 --- a/lib/models/app_state.dart +++ b/lib/models/app_state.dart @@ -7,4 +7,6 @@ class AppState extends ChangeNotifier { String streamId = ""; bool isCommandProcessing = false; String commandProcessingText = "Processing..."; + String sttFramework = "vosk"; + bool onlineMode = false; } diff --git a/lib/protos/voice_agent.proto b/lib/protos/voice_agent.proto index 40dfe6a..09b9461 100644 --- a/lib/protos/voice_agent.proto +++ b/lib/protos/voice_agent.proto @@ -11,6 +11,15 @@ service VoiceAgentService { rpc ExecuteCommand(ExecuteInput) returns (ExecuteResult); } +enum STTFramework { + VOSK = 0; + WHISPER = 1; +} + +enum OnlineMode { + ONLINE = 0; + OFFLINE = 1; +} enum RecordAction { START = 0; @@ -50,7 +59,7 @@ message Empty {} message ServiceStatus { string version = 1; - bool status = 2; + bool status = 2; string wake_word = 3; } @@ -69,6 +78,7 @@ message S_RecognizeVoiceControl { VoiceAudio audio_stream = 1; NLUModel nlu_model = 2; string stream_id = 3; + STTFramework stt_framework = 4; } message RecognizeVoiceControl { @@ -76,6 +86,8 @@ message RecognizeVoiceControl { NLUModel nlu_model = 2; RecordMode record_mode = 3; string stream_id = 4; + STTFramework stt_framework = 5; + OnlineMode online_mode = 6; } message RecognizeTextControl { diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index a9d567b..4c2407f 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,8 +1,10 @@ import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:flutter_voiceassistant/widgets/online_mode_choice.dart'; import 'package:flutter_voiceassistant/widgets/try_commands.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'dart:async'; import '../models/app_state.dart'; import '../widgets/nlu_engine_choice.dart'; @@ -13,6 +15,7 @@ import '../widgets/chat_section.dart'; import '../grpc/generated/voice_agent.pbgrpc.dart'; import '../grpc/voice_agent_client.dart'; import '../utils/app_config.dart'; +import '../widgets/stt_model_choice.dart'; class HomePage extends StatefulWidget { final AppConfig config; @@ -36,18 +39,30 @@ class HomePageState extends State<HomePage> { super.initState(); _config = widget.config; // Initialize _config in the initState _wakeWord = widget.wakeWord; // Initialize _wakeWord in the initState - addChatMessage( - "Assistant in Manual mode. You can send commands directly by pressing the record button."); + final appState = context.read<AppState>(); + if(appState.isWakeWordMode){ + addChatMessage( + 'Switched to Wake Word mode. I\'ll listen for the wake word "$_wakeWord" before responding.'); + _startWakeWordDetection(context); + } + else{ + addChatMessage( + "Assistant in Manual mode. You can send commands directly by pressing the record button."); + } + } - void changeAssistantMode(BuildContext context, AssistantMode newMode) { + Future<void> changeAssistantMode(BuildContext context, AssistantMode newMode) async { final appState = context.read<AppState>(); clearChatMessages(); appState.streamId = ""; appState.isWakeWordDetected = false; appState.isCommandProcessing = false; + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (newMode == AssistantMode.wakeWord) { + await prefs.setBool('isWakeWordMode', true); addChatMessage( 'Switched to Wake Word mode. I\'ll listen for the wake word "$_wakeWord" before responding.'); @@ -62,6 +77,7 @@ class HomePageState extends State<HomePage> { toggleWakeWordDetection(context, true); } } else if (newMode == AssistantMode.manual) { + prefs.setBool('isWakeWordMode', false); addChatMessage( 'Switched to Manual mode. You can send commands directly by pressing record button.'); @@ -74,15 +90,17 @@ class HomePageState extends State<HomePage> { setState(() {}); // Trigger a rebuild } - void changeIntentEngine(BuildContext context, NLUEngine newEngine) { + Future<void> changeIntentEngine(BuildContext context, NLUEngine newEngine) async { final appState = context.read<AppState>(); - + SharedPreferences prefs = await SharedPreferences.getInstance(); if (newEngine == NLUEngine.snips) { appState.intentEngine = "snips"; + await prefs.setString("intentEngine","snips"); addChatMessage( 'Switched to 🚀 Snips engine. Lets be precise and accurate.'); } else if (newEngine == NLUEngine.rasa) { appState.intentEngine = "rasa"; + await prefs.setString("intentEngine","rasa"); addChatMessage( 'Switched to 🤖 RASA engine. Conversations just got smarter!'); } @@ -90,6 +108,42 @@ class HomePageState extends State<HomePage> { setState(() {}); // Trigger a rebuild } + Future<void> changeSTTFramework(BuildContext context, STTModel newModel) async { + final appState = context.read<AppState>(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (newModel == STTModel.vosk) { + appState.sttFramework = "vosk"; + await prefs.setString("sttFramework", "vosk"); + // vosk is fast and efficient + addChatMessage( + 'Switched to 🚀 Vosk framework. Lets be quick and efficient.'); + } else if (newModel == STTModel.whisper) { + appState.sttFramework = "whisper"; + await prefs.setString("sttFramework", "whisper"); + addChatMessage( + 'Switched to 🤖 Whisper framework. Conversations just got smarter!'); + } + print(appState.sttFramework); + setState(() {}); // Trigger a rebuild + } + + Future<void> toggleOnlineMode(BuildContext context, OnlineModeEnum mode) async { + final appState = context.read<AppState>(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (mode == OnlineModeEnum.enabled) { + appState.onlineMode = true; + await prefs.setBool('onlineMode', true); + addChatMessage( + 'Switched to Online mode. I\'ll be connected to the internet for better results.'); + } else { + appState.onlineMode = false; + await prefs.setBool('onlineMode', false); + addChatMessage( + 'Switched to Offline mode. I\'ll be disconnected from the internet.'); + } + setState(() {}); // Trigger a rebuild + } + void addChatMessage(String text, {bool isUserMessage = false}) { final newMessage = ChatMessage(text: text, isUserMessage: isUserMessage); setState(() { @@ -123,7 +177,7 @@ class HomePageState extends State<HomePage> { setState( () {}); // Trigger a rebuild to ensure the loading indicator is shown, tis a bad practice though but deosn't heavily affect the performance final response = - await stopRecording(appState.streamId, appState.intentEngine); + await stopRecording(appState.streamId, appState.intentEngine,appState.sttFramework,appState.onlineMode); // Process and store the result if (response.status == RecognizeStatusType.REC_SUCCESS) { appState.commandProcessingText = "Executing command..."; @@ -204,19 +258,31 @@ class HomePageState extends State<HomePage> { } Future<RecognizeResult> stopRecording( - String streamId, String nluModel) async { + String streamId, String nluModel, String stt,bool isOnlineMode) async { + try { NLUModel model = NLUModel.RASA; if (nluModel == "snips") { model = NLUModel.SNIPS; } + STTFramework sttFramework = STTFramework.VOSK; + if (stt == "whisper") { + sttFramework = STTFramework.WHISPER; + } + OnlineMode onlineMode = OnlineMode.OFFLINE; + if (isOnlineMode) { + onlineMode = OnlineMode.ONLINE; + } // Create a RecognizeControl message to stop recording final controlMessage = RecognizeVoiceControl() ..action = RecordAction.STOP ..nluModel = model ..streamId = streamId // Use the same stream ID as when starting recording - ..recordMode = RecordMode.MANUAL; + ..recordMode = RecordMode.MANUAL + ..sttFramework = sttFramework + ..onlineMode = onlineMode; + // Create a Stream with the control message final controlStream = Stream.fromIterable([controlMessage]); @@ -375,116 +441,234 @@ class HomePageState extends State<HomePage> { style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold), ), SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.center, + Column( children: [ - Flexible( - flex: 1, - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - child: Card( - color: _config.theme == "textured-dark" || - _config.theme == "textured-light" - ? Colors.transparent - : null, - elevation: 4, // Add elevation for shadow - shadowColor: _config.theme == "textured-dark" || - _config.theme == "textured-light" - ? Colors.transparent - : null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Assistant Mode', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Assistant Mode', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer<AppState>( + builder: (context, appState, _) { + return AssistantModeChoice( + onModeChanged: (newMode) { + changeAssistantMode( + context, newMode); + print(newMode); + }, + theme: _config.theme, + ); + }, + ), + ), + ], ), - SizedBox(height: 16), // Add spacing if needed - Center( - child: Consumer<AppState>( - builder: (context, appState, _) { - return AssistantModeChoice( - onModeChanged: (newMode) { - changeAssistantMode( - context, newMode); - print(newMode); + ), + ), + ), + ), + ), + + SizedBox(width: 20), // Add spacing between buttons + + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Intent Engine', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer<AppState>( + builder: (context, appState, _) { + return NLUEngineChoice( + onEngineChanged: (newEngine) { + changeIntentEngine( + context, newEngine); + print(newEngine); + }, + theme: _config.theme, + ); }, - theme: _config.theme, - ); - }, - ), + ), + ), + ], ), - ], + ), ), ), ), ), - ), + ], ), - SizedBox(width: 20), // Add spacing between buttons - - Flexible( - flex: 1, - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - child: Card( - color: _config.theme == "textured-dark" || + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || _config.theme == "textured-light" - ? Colors.transparent - : null, - elevation: 4, // Add elevation for shadow - shadowColor: _config.theme == "textured-dark" || + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || _config.theme == "textured-light" - ? Colors.transparent - : null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Intent Engine', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Speech-to-Text Model', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer<AppState>( + builder: (context, appState, _) { + return STTModelChoice( + onModelChanged: (newModel) { + changeSTTFramework( + context, newModel); + print(newModel); + }, + theme: _config.theme, + ); + }, + ), + ), + ], ), - SizedBox(height: 16), // Add spacing if needed - Center( - child: Consumer<AppState>( - builder: (context, appState, _) { - return NLUEngineChoice( - onEngineChanged: (newEngine) { - changeIntentEngine( - context, newEngine); - print(newEngine); + ), + ), + ), + ), + ), + + SizedBox(width: 20), // Add spacing between buttons + + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Online Mode', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer<AppState>( + builder: (context, appState, _) { + return OnlineModeChoice( + onModeChanged: (mode) { + toggleOnlineMode(context, mode); + print(mode); + }, + theme: _config.theme, + ); }, - theme: _config.theme, - ); - }, - ), + ), + ), + ], ), - ], + ), ), ), ), ), - ), + ], ), + + ], ), SizedBox(height: 15), diff --git a/lib/screens/loading_screen.dart b/lib/screens/loading_screen.dart new file mode 100644 index 0000000..f4d6c6d --- /dev/null +++ b/lib/screens/loading_screen.dart @@ -0,0 +1,81 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter_voiceassistant/screens/home_screen.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../models/app_state.dart'; +import '../utils/app_config.dart'; + +class Loading extends StatelessWidget { + final AppConfig config; + final String wakeWord; + Loading({required this.config, required this.wakeWord}); + + Future<void> initialize(BuildContext context) async { + // Initialize the app + final appState = context.read<AppState>(); + bool isWakeWordMode = false; + String intentEngine = "snips"; + String streamId = ""; + bool isCommandProcessing = false; + String commandProcessingText = "Processing..."; + String sttFramework = "vosk"; + bool onlineMode = false; + + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.reload(); + if(prefs.containsKey('isWakeWordMode')) { + isWakeWordMode = await prefs.getBool('isWakeWordMode')!; + } + if(prefs.containsKey('intentEngine')) { + intentEngine = await prefs.getString('intentEngine')!; + } + if(prefs.containsKey('streamId')) { + streamId = await prefs.getString('streamId')!; + } + if(prefs.containsKey('isCommandProcessing')) { + isCommandProcessing = await prefs.getBool('isCommandProcessing')!; + } + if(prefs.containsKey('commandProcessingText')) { + commandProcessingText = await prefs.getString('commandProcessingText')!; + } + if(prefs.containsKey('sttFramework')) { + sttFramework = await prefs.getString('sttFramework')!; + } + if(prefs.containsKey('onlineMode')) { + onlineMode = await prefs.getBool('onlineMode')!; + } + + appState.isWakeWordMode = isWakeWordMode; + appState.intentEngine = intentEngine; + appState.streamId = streamId; + appState.isCommandProcessing = isCommandProcessing; + appState.commandProcessingText = commandProcessingText; + appState.sttFramework = sttFramework; + appState.onlineMode = onlineMode; + + print('isWakeWordMode: $isWakeWordMode'); + print('intentEngine: $intentEngine'); + print('streamId: $streamId'); + print('isCommandProcessing: $isCommandProcessing'); + print('commandProcessingText: $commandProcessingText'); + print('sttFramework: $sttFramework'); + print('onlineMode: $onlineMode'); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: initialize(context), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return HomePage(config: config, wakeWord: wakeWord); + } else { + return CupertinoActivityIndicator(); + } + }, + ); + } +} diff --git a/lib/widgets/assistant_mode_choice.dart b/lib/widgets/assistant_mode_choice.dart index c2afe5b..2c776df 100644 --- a/lib/widgets/assistant_mode_choice.dart +++ b/lib/widgets/assistant_mode_choice.dart @@ -70,6 +70,9 @@ // } import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; enum AssistantMode { wakeWord, manual } @@ -92,7 +95,10 @@ class AssistantModeChoiceState extends State<AssistantModeChoice> { @override void initState() { super.initState(); - _selectedMode = AssistantMode.manual; // Initialize the selection + final appState = context.read<AppState>(); + _selectedMode = appState.isWakeWordMode + ? AssistantMode.wakeWord + : AssistantMode.manual; // Initialize the selection _theme = widget.theme; print(widget.theme); } diff --git a/lib/widgets/nlu_engine_choice.dart b/lib/widgets/nlu_engine_choice.dart index 32db7dd..53a4aaa 100644 --- a/lib/widgets/nlu_engine_choice.dart +++ b/lib/widgets/nlu_engine_choice.dart @@ -68,6 +68,9 @@ // } import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; enum NLUEngine { snips, rasa } @@ -90,7 +93,8 @@ class _NLUEngineChoiceState extends State<NLUEngineChoice> { @override void initState() { super.initState(); - _selectedEngine = NLUEngine.snips; // Initialize the selection + final appState = context.read<AppState>(); + _selectedEngine = appState.intentEngine == "snips" ? NLUEngine.snips : NLUEngine.rasa; _theme = widget.theme; } diff --git a/lib/widgets/online_mode_choice.dart b/lib/widgets/online_mode_choice.dart new file mode 100644 index 0000000..3146d15 --- /dev/null +++ b/lib/widgets/online_mode_choice.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; + +enum OnlineModeEnum{ + enabled, + disabled +} + +class OnlineModeChoice extends StatefulWidget { + final Function(OnlineModeEnum) onModeChanged; + final String theme; + + const OnlineModeChoice({ + Key? key, + required this.onModeChanged, + required this.theme + }) : super(key: key); + + @override + State<OnlineModeChoice> createState() => _OnlineModeChoiceState(); +} + +class _OnlineModeChoiceState extends State<OnlineModeChoice> { + late OnlineModeEnum _selectedMode; + late String _theme; + + @override + void initState() { + super.initState(); + final appState = context.read<AppState>(); + _selectedMode = appState.onlineMode ? OnlineModeEnum.enabled : OnlineModeEnum.disabled; + _theme = widget.theme; + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + InkWell( + onTap: () => _onModelChanged(OnlineModeEnum.disabled), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + color: _selectedMode == OnlineModeEnum.disabled + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedMode == OnlineModeEnum.disabled + ? Icons.check + : Icons.cloud_off_outlined, + color: _selectedMode == OnlineModeEnum.disabled + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Disabled', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedMode == OnlineModeEnum.disabled + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + InkWell( + onTap: () => _onModelChanged(OnlineModeEnum.enabled), + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + color: _selectedMode == OnlineModeEnum.enabled + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedMode == OnlineModeEnum.enabled + ? Icons.check + : Icons.cloud_done_outlined, + color: _selectedMode == OnlineModeEnum.enabled + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Enabled', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedMode == OnlineModeEnum.enabled + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + ], + ); + } + + void _onModelChanged(OnlineModeEnum newMode) { + setState(() { + _selectedMode = newMode; + }); + + // Call the callback function to notify the engine change + widget.onModeChanged(newMode); + } +}
\ No newline at end of file diff --git a/lib/widgets/stt_model_choice.dart b/lib/widgets/stt_model_choice.dart new file mode 100644 index 0000000..fc0e099 --- /dev/null +++ b/lib/widgets/stt_model_choice.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; + +enum STTModel{ + vosk, + whisper +} + +class STTModelChoice extends StatefulWidget { + final Function(STTModel) onModelChanged; + final String theme; + + const STTModelChoice({ + Key? key, + required this.onModelChanged, + required this.theme + }) : super(key: key); + + @override + State<STTModelChoice> createState() => _STTModelChoiceState(); +} + +class _STTModelChoiceState extends State<STTModelChoice> { + late STTModel _selectedModel; + late String _theme; + + @override + void initState() { + super.initState(); + final appState = context.read<AppState>(); + _selectedModel = appState.sttFramework == "vosk" ? STTModel.vosk : STTModel.whisper; + _theme = widget.theme; + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + InkWell( + onTap: () => _onModelChanged(STTModel.vosk), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + color: _selectedModel == STTModel.vosk + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedModel == STTModel.vosk + ? Icons.check + : Icons.transcribe_sharp, + color: _selectedModel == STTModel.vosk + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Vosk', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedModel == STTModel.vosk + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + InkWell( + onTap: () => _onModelChanged(STTModel.whisper), + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + color: _selectedModel == STTModel.whisper + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedModel == STTModel.whisper + ? Icons.check + : Icons.transcribe_sharp, + color: _selectedModel == STTModel.whisper + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Whisper', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedModel == STTModel.whisper + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + ], + ); + } + + void _onModelChanged(STTModel newModel) { + setState(() { + _selectedModel = newModel; + }); + + // Call the callback function to notify the engine change + widget.onModelChanged(newModel); + } +}
\ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 76b76e9..805d507 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" convert: dependency: transitive description: @@ -81,6 +81,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" fixnum: dependency: transitive description: @@ -115,6 +131,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" googleapis_auth: dependency: transitive description: @@ -163,6 +184,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -175,26 +220,26 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.12.0" nested: dependency: transitive description: @@ -207,10 +252,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_drawing: dependency: transitive description: @@ -227,6 +272,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" petitparser: dependency: transitive description: @@ -235,6 +304,22 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.0" + platform: + dependency: transitive + description: + name: platform + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" pointycastle: dependency: transitive description: @@ -259,6 +344,62 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.5" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" sky_engine: dependency: transitive description: flutter @@ -268,26 +409,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -308,10 +449,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -328,6 +469,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" xml: dependency: transitive description: @@ -337,5 +502,5 @@ packages: source: hosted version: "6.3.0" sdks: - dart: ">=3.0.0 <4.0.0" - flutter: ">=2.11.0-0.1.pre" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 58186da..1e39f1c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: grpc: ^3.1.0 protobuf: ^2.1.0 flutter_svg: ^1.1.6 + shared_preferences: ^2.2.3 dev_dependencies: flutter_test: |