aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMalik Talha <talhamalik727x@gmail.com>2023-11-16 22:04:56 +0500
committerMalik Talha <talhamalik727x@gmail.com>2023-11-16 22:04:56 +0500
commitecd34435c1a74b39bf41d59ad479fdc85d0afb7b (patch)
tree1ed954b6c53321f854355bf82d13b736364e595b /lib
parentd433980265de4eccd343dcbfc92c3e7416057842 (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.dart55
-rw-r--r--lib/screens/error_screen.dart2
-rw-r--r--lib/screens/home_screen.dart193
-rw-r--r--lib/utils/app_config.dart5
-rw-r--r--lib/widgets/assistant_mode_choice.dart15
-rw-r--r--lib/widgets/chat_section.dart90
-rw-r--r--lib/widgets/nlu_engine_choice.dart14
-rw-r--r--lib/widgets/try_commands.dart105
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) {