diff options
author | 2023-11-16 22:04:56 +0500 | |
---|---|---|
committer | 2023-11-16 22:04:56 +0500 | |
commit | ecd34435c1a74b39bf41d59ad479fdc85d0afb7b (patch) | |
tree | 1ed954b6c53321f854355bf82d13b736364e595b /lib | |
parent | d433980265de4eccd343dcbfc92c3e7416057842 (diff) |
Add themes to voice assistant app
Add four themes: "light", "dark", "textured-light", and
"textured-dark" to the voice assistant app. Themes can be
changed by modifying assets/config.json file.
Bug-AGL: SPEC-4906
Signed-off-by: Malik Talha <talhamalik727x@gmail.com>
Change-Id: I3e257da70543c7918e4f0cf96a62907390af8480
Diffstat (limited to 'lib')
-rw-r--r-- | lib/main.dart | 55 | ||||
-rw-r--r-- | lib/screens/error_screen.dart | 2 | ||||
-rw-r--r-- | lib/screens/home_screen.dart | 193 | ||||
-rw-r--r-- | lib/utils/app_config.dart | 5 | ||||
-rw-r--r-- | lib/widgets/assistant_mode_choice.dart | 15 | ||||
-rw-r--r-- | lib/widgets/chat_section.dart | 90 | ||||
-rw-r--r-- | lib/widgets/nlu_engine_choice.dart | 14 | ||||
-rw-r--r-- | lib/widgets/try_commands.dart | 105 |
8 files changed, 307 insertions, 172 deletions
diff --git a/lib/main.dart b/lib/main.dart index b271b79..9dfbd54 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'models/app_state.dart'; import 'screens/home_screen.dart'; import 'screens/error_screen.dart'; @@ -62,19 +63,53 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'AGL Voice Assistant', + title: 'Voice Assistant', theme: ThemeData( useMaterial3: true, - colorScheme: ColorScheme.fromSeed(seedColor: Colors.green), + colorScheme: config.theme == "dark" || config.theme == "textured-dark" + ? ColorScheme.dark( + primary: Colors.green, + onPrimary: Colors.black, // Text color on the primary background + secondary: Colors.greenAccent, + onSecondary: + Colors.black, // Text color on the secondary background + ) + : ColorScheme.light( + primary: Colors.green, + onPrimary: Colors.white, // Text color on the primary background + secondary: Colors.greenAccent, + onSecondary: + Colors.white, // Text color on the secondary background + ), ), - home: Consumer<ServiceStatusProvider>( - builder: (context, provider, child) { - return provider.isServiceOnline - ? HomePage(config: config, wakeWord: provider.wakeWord) - : ErrorScreen( - onRetry: onRetry, // Pass the callback to the ErrorScreen - ); // Conditionally render HomePage or ErrorScreen - }, + home: Stack( + children: [ + Container( + color: config.theme == "dark" || config.theme == "textured-dark" + ? Colors.black + : Colors + .white, // Set the background color based on dark or light mode + width: double.infinity, + height: double.infinity, + ), + if (config.theme == 'textured-dark' || + config.theme == 'textured-light') + SvgPicture.asset( + 'assets/background_texture.svg', + width: double.infinity, + height: double.infinity, + fit: BoxFit.contain, + ), + Consumer<ServiceStatusProvider>( + builder: (context, provider, child) { + return provider.isServiceOnline + ? HomePage(config: config, wakeWord: provider.wakeWord) + : ErrorScreen( + onRetry: onRetry, + ); + }, + ), + ], ), ); } diff --git a/lib/screens/error_screen.dart b/lib/screens/error_screen.dart index 04a5d30..21d6fd2 100644 --- a/lib/screens/error_screen.dart +++ b/lib/screens/error_screen.dart @@ -34,7 +34,7 @@ class ErrorScreen extends StatelessWidget { textAlign: TextAlign.center, style: TextStyle( fontSize: 18, - color: Colors.grey[700], + color: Colors.grey[600], ), ), SizedBox(height: 20), diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index bf1d020..a9d567b 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:flutter_voiceassistant/widgets/try_commands.dart'; import 'package:provider/provider.dart'; @@ -46,17 +48,29 @@ class HomePageState extends State<HomePage> { appState.isCommandProcessing = false; if (newMode == AssistantMode.wakeWord) { - appState.isWakeWordMode = true; addChatMessage( 'Switched to Wake Word mode. I\'ll listen for the wake word "$_wakeWord" before responding.'); - toggleWakeWordDetection(context, true); + + // Close old ongoing wake word detection loop if any + if (appState.isWakeWordMode) { + appState.isWakeWordMode = false; + toggleWakeWordDetection(context, false); + } + // Start a new wake word detection loop + if (!appState.isWakeWordMode) { + appState.isWakeWordMode = true; + toggleWakeWordDetection(context, true); + } } else if (newMode == AssistantMode.manual) { - appState.isWakeWordMode = false; addChatMessage( 'Switched to Manual mode. You can send commands directly by pressing record button.'); - toggleWakeWordDetection(context, false); + + // Close old ongoing wake word detection loop if any + if (appState.isWakeWordMode) { + appState.isWakeWordMode = false; + toggleWakeWordDetection(context, false); + } } - print(appState.isWakeWordMode); setState(() {}); // Trigger a rebuild } @@ -334,6 +348,7 @@ class HomePageState extends State<HomePage> { final appState = context.watch<AppState>(); return Scaffold( + backgroundColor: Colors.transparent, body: SingleChildScrollView( child: Center( child: SizedBox( @@ -342,13 +357,21 @@ class HomePageState extends State<HomePage> { child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ - Image.asset( - 'assets/agl_logo.png', // Replace with your logo image path - width: 120, // Adjust the width as needed - height: 120, // Adjust the height as needed + Container( + margin: EdgeInsets.only( + top: 25, + bottom: 25), // Adjust the top and bottom margin as needed + child: Image.asset( + _config.theme == "dark" || _config.theme == "textured-dark" + ? 'assets/agl_logo_darkmode.png' + : 'assets/agl_logo_lightmode.png', + width: 300, + fit: BoxFit + .contain, // Ensure the image fits within the specified dimensions + ), ), Text( - "AGL Voice Assistant", + "Voice Assistant", style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold), ), SizedBox(height: 15), @@ -357,37 +380,52 @@ class HomePageState extends State<HomePage> { children: [ Flexible( flex: 1, - child: Card( - elevation: 4, // Add elevation for shadow - 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); + 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, + ); }, - ); - }, - ), + ), + ), + ], ), - ], + ), ), ), ), @@ -397,37 +435,52 @@ class HomePageState extends State<HomePage> { Flexible( flex: 1, - child: Card( - elevation: 4, // Add elevation for shadow - 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); + 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, + ); }, - ); - }, - ), + ), + ), + ], ), - ], + ), ), ), ), @@ -439,10 +492,12 @@ class HomePageState extends State<HomePage> { scrollController: _scrollController, chatMessages: chatMessages, addChatMessage: addChatMessage, + theme: _config.theme, ), SizedBox(height: 10), if (!appState.isWakeWordMode || appState.isWakeWordDetected) - TryCommandsSection(onCommandTap: handleCommandTap), + TryCommandsSection( + onCommandTap: handleCommandTap, theme: _config.theme), SizedBox(height: 30), if (!appState.isWakeWordMode || appState.isWakeWordDetected) if (!appState.isCommandProcessing) diff --git a/lib/utils/app_config.dart b/lib/utils/app_config.dart index 8f5c566..eacb10f 100644 --- a/lib/utils/app_config.dart +++ b/lib/utils/app_config.dart @@ -5,8 +5,10 @@ import 'package:flutter/services.dart'; class AppConfig { late String grpcHost; late int grpcPort; + late String theme; - AppConfig({required this.grpcHost, required this.grpcPort}); + AppConfig( + {required this.grpcHost, required this.grpcPort, required this.theme}); factory AppConfig.fromAsset() { return AppConfig._(); @@ -21,6 +23,7 @@ class AppConfig { return AppConfig( grpcHost: jsonMap['grpc_host'], grpcPort: jsonMap['grpc_port'], + theme: jsonMap['theme'], ); } } diff --git a/lib/widgets/assistant_mode_choice.dart b/lib/widgets/assistant_mode_choice.dart index d0c1953..c2afe5b 100644 --- a/lib/widgets/assistant_mode_choice.dart +++ b/lib/widgets/assistant_mode_choice.dart @@ -75,8 +75,10 @@ enum AssistantMode { wakeWord, manual } class AssistantModeChoice extends StatefulWidget { final Function(AssistantMode) onModeChanged; + final String theme; - const AssistantModeChoice({Key? key, required this.onModeChanged}) + const AssistantModeChoice( + {Key? key, required this.onModeChanged, required this.theme}) : super(key: key); @override @@ -85,11 +87,14 @@ class AssistantModeChoice extends StatefulWidget { class AssistantModeChoiceState extends State<AssistantModeChoice> { late AssistantMode _selectedMode; + late String _theme; @override void initState() { super.initState(); _selectedMode = AssistantMode.manual; // Initialize the selection + _theme = widget.theme; + print(widget.theme); } @override @@ -112,7 +117,9 @@ class AssistantModeChoiceState extends State<AssistantModeChoice> { ), color: _selectedMode == AssistantMode.wakeWord ? Colors.green - : Colors.white, + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, border: Border.all( color: Colors.transparent, ), @@ -157,7 +164,9 @@ class AssistantModeChoiceState extends State<AssistantModeChoice> { ), color: _selectedMode == AssistantMode.manual ? Colors.green - : Colors.white, + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, border: Border.all( color: Colors.transparent, ), diff --git a/lib/widgets/chat_section.dart b/lib/widgets/chat_section.dart index ca48cab..0a6a053 100644 --- a/lib/widgets/chat_section.dart +++ b/lib/widgets/chat_section.dart @@ -1,56 +1,71 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; class ChatSection extends StatelessWidget { final ScrollController scrollController; final List<ChatMessage> chatMessages; final Function(String text, {bool isUserMessage}) addChatMessage; + final String theme; ChatSection({ required this.scrollController, required this.chatMessages, required this.addChatMessage, + required this.theme, }); @override @override Widget build(BuildContext context) { - return Card( - elevation: 4, // Add a subtle shadow - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Chat heading - Container( - padding: EdgeInsets.fromLTRB(12, 12, 0, 0), - // alignment: Alignment.l, - child: Text( - 'Conversation Logs', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - ), - textAlign: TextAlign.left, - ), + return ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: theme == "textured-dark" || theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add a subtle shadow + shadowColor: theme == "textured-dark" || theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), ), - // Chat messages with fixed height - Container( - padding: EdgeInsets.all(10), - height: 180, // Adjust the height as needed - child: ListView.builder( - controller: scrollController, - itemCount: chatMessages.length, - itemBuilder: (context, index) { - final message = chatMessages[index]; - return ChatMessageTile(message: message); - }, - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Chat heading + Container( + padding: EdgeInsets.fromLTRB(12, 12, 0, 0), + // alignment: Alignment.l, + child: Text( + 'Conversation Logs', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + textAlign: TextAlign.left, + ), + ), + // Chat messages with fixed height + Container( + padding: EdgeInsets.all(10), + height: 180, // Adjust the height as needed + child: ListView.builder( + controller: scrollController, + itemCount: chatMessages.length, + itemBuilder: (context, index) { + final message = chatMessages[index]; + return ChatMessageTile(message: message); + }, + ), + ), + // User input field (if needed) + // ... + ], ), - // User input field (if needed) - // ... - ], + ), ), ); } @@ -82,7 +97,7 @@ class ChatMessageTile extends StatelessWidget { children: [ if (!message.isUserMessage) CircleAvatar( - backgroundColor: Colors.green[400], + backgroundColor: Colors.green, child: Icon( Icons.smart_toy_outlined, color: Colors.white, @@ -103,8 +118,7 @@ class ChatMessageTile extends StatelessWidget { ? Radius.circular(0) : Radius.circular(16), ), - color: - message.isUserMessage ? Colors.blue : Colors.green[400], + color: message.isUserMessage ? Colors.blue : Colors.green, ), child: Text( message.text, diff --git a/lib/widgets/nlu_engine_choice.dart b/lib/widgets/nlu_engine_choice.dart index 22b7074..32db7dd 100644 --- a/lib/widgets/nlu_engine_choice.dart +++ b/lib/widgets/nlu_engine_choice.dart @@ -73,8 +73,10 @@ enum NLUEngine { snips, rasa } class NLUEngineChoice extends StatefulWidget { final Function(NLUEngine) onEngineChanged; + final String theme; - const NLUEngineChoice({Key? key, required this.onEngineChanged}) + const NLUEngineChoice( + {Key? key, required this.onEngineChanged, required this.theme}) : super(key: key); @override @@ -83,11 +85,13 @@ class NLUEngineChoice extends StatefulWidget { class _NLUEngineChoiceState extends State<NLUEngineChoice> { late NLUEngine _selectedEngine; + late String _theme; @override void initState() { super.initState(); _selectedEngine = NLUEngine.snips; // Initialize the selection + _theme = widget.theme; } @override @@ -110,7 +114,9 @@ class _NLUEngineChoiceState extends State<NLUEngineChoice> { ), color: _selectedEngine == NLUEngine.snips ? Colors.green - : Colors.white, + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, border: Border.all( color: Colors.transparent, ), @@ -155,7 +161,9 @@ class _NLUEngineChoiceState extends State<NLUEngineChoice> { ), color: _selectedEngine == NLUEngine.rasa ? Colors.green - : Colors.white, + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, border: Border.all( color: Colors.transparent, ), diff --git a/lib/widgets/try_commands.dart b/lib/widgets/try_commands.dart index 0670bd2..3e47e9c 100644 --- a/lib/widgets/try_commands.dart +++ b/lib/widgets/try_commands.dart @@ -1,64 +1,75 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; class TryCommandsSection extends StatelessWidget { final Future<void> Function(String) onCommandTap; // Define a callback + final String theme; - TryCommandsSection({required this.onCommandTap}); + TryCommandsSection({required this.onCommandTap, required this.theme}); @override Widget build(BuildContext context) { return SizedBox( - width: double.infinity, - child: Card( - // padding: EdgeInsets.all(16), - // decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(12), - // ), - elevation: 4, // Add a subtle shadow - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: EdgeInsets.fromLTRB(12, 12, 0, 0), - child: Text( - "Try Commands", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, + width: double.infinity, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: theme == "textured-dark" || theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add a subtle shadow + shadowColor: theme == "textured-dark" || theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.fromLTRB(12, 12, 0, 0), + child: Text( + "Try Commands", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.left, ), - textAlign: TextAlign.left, ), - ), - Container( - padding: EdgeInsets.fromLTRB(12, 0, 12, 0), - child: Text( - "(Tap on any of the following commands to try them out)", - style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic), - textAlign: TextAlign.left, + Container( + padding: EdgeInsets.fromLTRB(12, 0, 12, 0), + child: Text( + "(Tap on any of the following commands to try them out)", + style: TextStyle(fontSize: 14, fontStyle: FontStyle.italic), + textAlign: TextAlign.left, + ), ), - ), - SizedBox(height: 6), - Container( - padding: EdgeInsets.fromLTRB(12, 0, 12, 12), - child: Wrap( - spacing: 10, - children: [ - buildCommandButton("Set the volume to fifty percent"), - buildCommandButton("Set the fan speed to max"), - buildCommandButton( - "Increase the temperature by three degrees"), - buildCommandButton( - "Decrease the fan speed by five percent"), - buildCommandButton("Can you reduce the volume"), - ], + SizedBox(height: 6), + Container( + padding: EdgeInsets.fromLTRB(12, 0, 12, 12), + child: Wrap( + spacing: 10, + children: [ + buildCommandButton("Set the volume to fifty percent"), + buildCommandButton("Set the fan speed to max"), + buildCommandButton( + "Increase the temperature by three degrees"), + buildCommandButton( + "Decrease the fan speed by five percent"), + buildCommandButton("Can you reduce the volume"), + ], + ), ), - ), - ], + ], + ), ), - )); + ), + ), + ); } Widget buildCommandButton(String commandText) { |