aboutsummaryrefslogtreecommitdiffstats
path: root/lib/presentation/screens
diff options
context:
space:
mode:
authorAnuj Solanki <anuj603362@gmail.com>2024-09-29 21:31:03 +0530
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>2024-10-07 10:52:41 +0000
commitf870bbe3c49d421ff8ea561752b3b0a38ad04e96 (patch)
treee6607974a3ea6591f622a3ebe8010561b0a6ad26 /lib/presentation/screens
parent29ae7d2d9e04bd8e3a7d37dcfa87a02dd1ab385f (diff)
Integrate voice assistant into flutter-ics-homescreen
- Implement voice-agent client to connect with agl-service-voiceagent for command execution, wake word detection. - ⁠Add a setting tile on the settings page for configuring voice assistant settings. - Add toggle buttons for wake word mode, online mode, overlay and speech-to-text model in the voice assistant settings. - Add a button on the homepage to start the voice assistant. - Update gRPC protos to retrieve online-mode status from the voice service. - Make online-mode tile conditional in voice-assistant settings, removing it from the UI if not enabled in the service. - Automatically hide the overlay 3 seconds after command execution. Bug-AGL: SPEC-5200 Change-Id: I4efaaf16ebc570b28816dc7203364efe2b658c2e Signed-off-by: Anuj Solanki <anuj603362@gmail.com>
Diffstat (limited to 'lib/presentation/screens')
-rw-r--r--lib/presentation/screens/home/home.dart11
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart28
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart120
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart251
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart111
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart102
-rw-r--r--lib/presentation/screens/settings/widgets/settings_content.dart9
7 files changed, 632 insertions, 0 deletions
diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart
index 0ee52ac..6e3e119 100644
--- a/lib/presentation/screens/home/home.dart
+++ b/lib/presentation/screens/home/home.dart
@@ -1,4 +1,6 @@
import 'package:flutter_ics_homescreen/export.dart';
+
+import '../../common_widget/voice_assistant_button.dart';
// import 'package:media_kit_video/media_kit_video.dart';
final bkgImageProvider = Provider((ref) {
@@ -76,6 +78,15 @@ class HomeScreenState extends ConsumerState<HomeScreen> {
height: 500,
child: const VolumeFanControl()),
),
+ // Voice Assistant Button
+ if (appState != AppState.splash && ref.watch(voiceAssistantStateProvider.select((value)=>value.isVoiceAssistantEnable)))
+ Positioned(
+ top: MediaQuery.of(context).size.height * 0.82,
+ child: Container(
+ padding: const EdgeInsets.only(left: 8),
+ child: const VoiceAssistantButton()
+ ),
+ ),
],
),
bottomNavigationBar:
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart
new file mode 100644
index 0000000..e1f38ae
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart
@@ -0,0 +1,28 @@
+
+import 'package:flutter_ics_homescreen/export.dart';
+import 'widgets/voice_assistant_content.dart';
+
+class VoiceAssistantPage extends ConsumerWidget{
+ const VoiceAssistantPage({super.key});
+
+ static Page<void> page() => const MaterialPage<void>(child: VoiceAssistantPage());
+ @override
+ Widget build(BuildContext context,WidgetRef ref) {
+
+ return Scaffold(
+ body: Column(
+ children: [
+ CommonTitle(
+ title: 'Voice Assistant',
+ hasBackButton: true,
+ onPressed: () {
+ ref.read(appProvider.notifier).back();
+ },
+ ),
+ Expanded(child: VoiceAssistantContent()),
+ ],
+ ),
+ );
+ }
+}
+
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart
new file mode 100644
index 0000000..614763d
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart
@@ -0,0 +1,120 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+import '../../../../../../../data/models/voice_assistant_state.dart';
+
+class STTModelPage extends ConsumerWidget {
+ const STTModelPage({super.key});
+
+ static Page<void> page() =>
+ const MaterialPage<void>(child: STTModelPage());
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final SttModel sttModel = ref.watch(voiceAssistantStateProvider.select((value) => value.sttModel));
+
+ return Scaffold(
+ body: Column(
+ children: [
+ CommonTitle(
+ title: 'Speech to Text Model',
+ hasBackButton: true,
+ onPressed: () {
+ context.flow<AppState>().update((state) => AppState.voiceAssistant);
+ },
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 144),
+ child: ListView(
+ children: [
+ Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: sttModel == SttModel.whisper
+ ? [0, 0.01, 0.8]
+ : [0.1, 1],
+ colors: sttModel == SttModel.whisper
+ ? <Color>[
+ Colors.white,
+ Colors.blue,
+ const Color.fromARGB(16, 41, 98, 255)
+ ]
+ : <Color>[Colors.black, Colors.black12]),
+ ),
+ child: ListTile(
+ minVerticalPadding: 0.0,
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16.0, vertical: 40.0),
+ leading: Text(
+ 'Whisper AI',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ trailing: sttModel == SttModel.whisper
+ ? const Icon(
+ Icons.done,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ )
+ : null,
+ onTap: () {
+ ref
+ .read(voiceAssistantStateProvider.notifier)
+ .updateSttModel(SttModel.whisper);
+ }),
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+ Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: sttModel == SttModel.vosk
+ ? [0, 0.01, 0.8]
+ : [0.1, 1],
+ colors: sttModel == SttModel.vosk
+ ? <Color>[
+ Colors.white,
+ Colors.blue,
+ const Color.fromARGB(16, 41, 98, 255)
+ ]
+ : <Color>[Colors.black, Colors.black12]),
+ ),
+ child: ListTile(
+ minVerticalPadding: 0.0,
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16.0, vertical: 40.0),
+ leading: Text(
+ 'Vosk',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ //title: Text(widget.title),
+ //enabled: isSwitchOn,
+ trailing: sttModel == SttModel.vosk
+ ? const Icon(
+ Icons.done,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ )
+ : null,
+
+ onTap: () {
+ ref
+ .read(voiceAssistantStateProvider.notifier)
+ .updateSttModel(SttModel.vosk);
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart
new file mode 100644
index 0000000..924a219
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart
@@ -0,0 +1,251 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+import 'package:flutter_ics_homescreen/export.dart';
+import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart';
+
+import '../../../../../../core/utils/helpers.dart';
+import '../../../../../../data/models/voice_assistant_state.dart';
+
+@immutable
+class VoiceAssistantContent extends ConsumerWidget {
+ VoiceAssistantContent({Key? key}) : super(key: key);
+ bool isWakeWordMode = false;
+ bool isVoiceAssistantOverlay = false;
+ bool isOnlineMode = false;
+ SttModel sttModel = SttModel.whisper;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ isWakeWordMode =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.isWakeWordMode));
+ isVoiceAssistantOverlay =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.voiceAssistantOverlay));
+ isOnlineMode =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.isOnlineMode));
+ sttModel =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.sttModel));
+
+ final wakeWordCallback = () {
+ bool status = ref.read(voiceAssistantStateProvider.notifier).toggleWakeWordMode();
+ if(status){
+ var voiceAgentClient = ref.read(voiceAgentClientProvider);
+ voiceAgentClient.startWakeWordDetection();
+ }
+ };
+
+ final voiceAssistantOverlayCallback = () {
+ ref.read(voiceAssistantStateProvider.notifier).toggleVoiceAssistantOverlay();
+ };
+
+ final onlineModeCallback = () {
+ ref.read(voiceAssistantStateProvider.notifier).toggleOnlineMode();
+ };
+
+
+ return Column(
+ children: [
+ Expanded(
+ child: ListView(
+ padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 144),
+ children: [
+ VoiceAssistantTile(
+ icon: Icons.insert_comment_outlined,
+ title: "Voice Assistant Overlay",
+ hasSwitch: true,
+ voidCallback: voiceAssistantOverlayCallback,
+ isSwitchOn: isVoiceAssistantOverlay
+ ),
+ if(ref.watch(voiceAssistantStateProvider.select((value) => value.isOnlineModeAvailable)))
+ VoiceAssistantTile(
+ icon: Icons.cloud_circle,
+ title: "Online Mode",
+ hasSwitch: true,
+ voidCallback: onlineModeCallback,
+ isSwitchOn: isOnlineMode
+ ),
+ VoiceAssistantTile(
+ icon: Icons.mic_none_outlined,
+ title: "Wake Word Mode",
+ hasSwitch: true,
+ voidCallback: wakeWordCallback,
+ isSwitchOn: isWakeWordMode
+ ),
+ if(ref.watch(voiceAssistantStateProvider.select((value) => value.isWakeWordMode)))
+ WakeWordTile(),
+ SttTile(
+ title: " Speech To Text",
+ sttName: sttModel==SttModel.whisper ? "Whisper AI" : "Vosk",
+ hasSwich: true,
+ voidCallback: () async {
+ context
+ .flow<AppState>()
+ .update((next) => AppState.sttModel);
+ }),
+ ],
+ )
+ ),
+ ],
+ );
+ }
+}
+
+class SttTile extends ConsumerStatefulWidget {
+ final IconData? icon;
+ final String title;
+ final String sttName;
+ final bool hasSwich;
+ final VoidCallback voidCallback;
+ final String? image;
+ const SttTile({
+ Key? key,
+ this.icon,
+ required this.title,
+ required this.sttName,
+ required this.hasSwich,
+ required this.voidCallback,
+ this.image,
+ }) : super(key: key);
+
+ @override
+ SttTileState createState() => SttTileState();
+}
+
+class SttTileState extends ConsumerState<SttTile> {
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ Container(
+ margin: const EdgeInsets.symmetric(vertical: 8),
+ padding: const EdgeInsets.symmetric(vertical: 15),
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: [0.3, 1],
+ colors: <Color>[Colors.black, Colors.black12]),
+ ),
+ //color: Color(0xFF0D113F),
+ child: ListTile(
+ contentPadding:
+ const EdgeInsets.symmetric(vertical: 17, horizontal: 24),
+ leading: Icon(
+ Icons.transcribe_outlined,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ title: Text(
+ widget.title,
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40),
+ ),
+ trailing: Row(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Text(
+ widget.sttName,
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40,
+ ),
+ ),
+ const SizedBox(
+ width: 24,
+ ),
+ const Icon(
+ Icons.arrow_forward_ios,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ ],
+ ),
+ onTap: widget.voidCallback,
+ ),
+ ),
+ const SizedBox(
+ height: 8,
+ )
+ ],
+ );
+ }
+}
+
+
+
+class WakeWordTile extends ConsumerStatefulWidget {
+ const WakeWordTile({Key? key}) : super(key: key);
+
+ @override
+ WakeWordTileState createState() => WakeWordTileState();
+}
+
+class WakeWordTileState extends ConsumerState<WakeWordTile> {
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ Container(
+ margin: const EdgeInsets.symmetric(vertical: 8),
+ padding: const EdgeInsets.symmetric(vertical: 15),
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: [0.3, 1],
+ colors: <Color>[Colors.black, Colors.black12]),
+ ),
+ //color: Color(0xFF0D113F),
+ child: ListTile(
+ contentPadding:
+ const EdgeInsets.symmetric(vertical: 17, horizontal: 24),
+ leading: Icon(
+ Icons.mic_none_outlined,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ title: Text(
+ "Wake Word",
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40),
+ ),
+ trailing: Row(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Text(
+ ref.watch(voiceAssistantStateProvider.select((value) => value.wakeWord)) ?? "Not Set",
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40,
+ ),
+ ),
+ const SizedBox(
+ width: 50,
+ ),
+
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 8,
+ )
+ ],
+ );
+ }
+}
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart
new file mode 100644
index 0000000..ee0365a
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart
@@ -0,0 +1,111 @@
+import 'package:flutter_ics_homescreen/export.dart';
+import 'package:protos/val_api.dart';
+
+class VoiceAssistantSettingsTile extends ConsumerStatefulWidget {
+ final IconData icon;
+ final String title;
+ final bool hasSwich;
+ final VoidCallback voidCallback;
+ const VoiceAssistantSettingsTile({
+ Key? key,
+ required this.icon,
+ required this.title,
+ required this.hasSwich,
+ required this.voidCallback,
+ }) : super(key: key);
+
+ @override
+ VoiceAssistantSettingsTileState createState() => VoiceAssistantSettingsTileState();
+}
+
+class VoiceAssistantSettingsTileState extends ConsumerState<VoiceAssistantSettingsTile> {
+ bool isSwitchOn = true;
+ @override
+ Widget build(BuildContext context) {
+ isSwitchOn = ref.watch(voiceAssistantStateProvider.select((voiceAssistant) => voiceAssistant.isVoiceAssistantEnable));
+ return Column(
+ children: [
+ GestureDetector(
+ onTap: isSwitchOn ? widget.voidCallback : () {},
+ child: Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: isSwitchOn ? [0.3, 1] : [0.8, 1],
+ colors: isSwitchOn
+ ? <Color>[Colors.black, Colors.black12]
+ : <Color>[
+ const Color.fromARGB(50, 0, 0, 0),
+ Colors.transparent
+ ],
+ ),
+ ),
+ child: Card(
+ color: Colors.transparent,
+ elevation: 5,
+ child: Padding(
+ padding:
+ const EdgeInsets.symmetric(vertical: 0, horizontal: 24),
+ child: Row(
+ children: [
+ Icon(
+ widget.icon,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ const SizedBox(width: 24),
+ Expanded(
+ child: Text(
+ widget.title,
+ style: const TextStyle(fontSize: 40),
+ ),
+ ),
+ widget.hasSwich
+ ? Container(
+ width: 126,
+ height: 80,
+ decoration: const ShapeDecoration(
+ color:
+ AGLDemoColors.gradientBackgroundDarkColor,
+ shape: StadiumBorder(
+ side: BorderSide(
+ color: Color(0xFF5477D4),
+ width: 4,
+ )),
+ ),
+ child: FittedBox(
+ fit: BoxFit.fill,
+ child: Switch(
+ value: isSwitchOn,
+ onChanged: (bool value) async {
+ var voiceAgentClient = ref.read(voiceAgentClientProvider);
+ ServiceStatus status = await voiceAgentClient.checkServiceStatus();
+ ref.read(voiceAssistantStateProvider.notifier).toggleVoiceAssistant(status);
+ setState(() {
+ isSwitchOn = value;
+ });
+ // This is called when the user toggles the switch.
+ },
+ inactiveTrackColor: Colors.transparent,
+ activeTrackColor: Colors.transparent,
+ thumbColor:
+ MaterialStateProperty.all<Color>(
+ AGLDemoColors.periwinkleColor)),
+ ),
+ )
+ : const SizedBox(),
+ ],
+ ),
+ ),
+ )
+ ),
+ ),
+ const SizedBox(
+ height: 8,
+ )
+ ],
+ );
+ }
+}
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart
new file mode 100644
index 0000000..d4bdd48
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart
@@ -0,0 +1,102 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+class VoiceAssistantTile extends ConsumerStatefulWidget {
+ final IconData icon;
+ final String title;
+ final bool hasSwitch;
+ final VoidCallback voidCallback;
+ final bool isSwitchOn;
+ const VoiceAssistantTile({super.key, required this.icon, required this.title, required this.hasSwitch, required this.voidCallback,required this.isSwitchOn});
+
+ @override
+ ConsumerState<VoiceAssistantTile> createState() => _VoiceAssistantTileState();
+}
+
+class _VoiceAssistantTileState extends ConsumerState<VoiceAssistantTile> {
+ bool isSwitchOn = true;
+ @override
+ Widget build(BuildContext context) {
+ isSwitchOn = widget.isSwitchOn;
+ return Column(
+ children: [
+ Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: isSwitchOn ? [0.3, 1] : [0.8, 1],
+ colors: isSwitchOn
+ ? <Color>[Colors.black, Colors.black12]
+ : <Color>[
+ const Color.fromARGB(50, 0, 0, 0),
+ Colors.transparent
+ ],
+ ),
+ ),
+ child: Card(
+
+ color: Colors.transparent,
+ elevation: 5,
+ child: Padding(
+ padding:
+ const EdgeInsets.symmetric(vertical: 0, horizontal: 24),
+ child: Row(
+ children: [
+ Icon(
+ widget.icon,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ const SizedBox(width: 24),
+ Expanded(
+ child: Text(
+ widget.title,
+ style: const TextStyle(fontSize: 40),
+ ),
+ ),
+ widget.hasSwitch
+ ? Container(
+ width: 126,
+ height: 80,
+ decoration: const ShapeDecoration(
+ color:
+ AGLDemoColors.gradientBackgroundDarkColor,
+ shape: StadiumBorder(
+ side: BorderSide(
+ color: Color(0xFF5477D4),
+ width: 4,
+ )),
+ ),
+ child: FittedBox(
+ fit: BoxFit.fill,
+ child: Switch(
+ value: isSwitchOn,
+ onChanged: (bool value) {
+ setState(() {
+ isSwitchOn = value;
+ });
+ widget.voidCallback();
+ },
+ inactiveTrackColor: Colors.transparent,
+ activeTrackColor: Colors.transparent,
+ thumbColor:
+ MaterialStateProperty.all<Color>(
+ AGLDemoColors.periwinkleColor)),
+ ),
+ )
+ : const SizedBox(),
+ ],
+ ),
+ ),
+ )
+ ),
+ const SizedBox(
+ height: 14,
+ )
+ ],
+ );
+ }
+}
+
+
diff --git a/lib/presentation/screens/settings/widgets/settings_content.dart b/lib/presentation/screens/settings/widgets/settings_content.dart
index 6d0df50..458677c 100644
--- a/lib/presentation/screens/settings/widgets/settings_content.dart
+++ b/lib/presentation/screens/settings/widgets/settings_content.dart
@@ -1,6 +1,7 @@
import 'package:flutter_ics_homescreen/export.dart';
import '../../../custom_icons/custom_icons.dart';
+import '../settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart';
class Settings extends ConsumerWidget {
const Settings({
@@ -55,6 +56,14 @@ class Settings extends ConsumerWidget {
voidCallback: () {
ref.read(appProvider.notifier).update(AppState.audioSettings);
}),
+ VoiceAssistantSettingsTile(
+ icon: Icons.keyboard_voice_outlined,
+ title: "Voice Assistant",
+ hasSwich: true,
+ voidCallback: (){
+ ref.read(appProvider.notifier).update(AppState.voiceAssistant);
+ }
+ ),
SettingsTile(
icon: Icons.person_2_outlined,
title: 'Profiles',