added send functionality to icon.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:app/theme.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'chat.dart';
|
import 'chat.dart';
|
||||||
|
|
||||||
@@ -26,9 +27,12 @@ class ChatBubbleWidget extends StatelessWidget{
|
|||||||
bottomRight: c.role ? const Radius.circular(12.0) : const Radius
|
bottomRight: c.role ? const Radius.circular(12.0) : const Radius
|
||||||
.circular(0.0),
|
.circular(0.0),
|
||||||
),
|
),
|
||||||
color: c.role ? Colors.blue[200] : Colors.grey[200],
|
color: c.role ? AppTheme.darkest : AppTheme.dark,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
c.content,
|
||||||
|
style: AppTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
child: Text(c.content),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class Conversation with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadChats() {
|
void loadChats() {
|
||||||
chats = Server.getChatsByConversationID(conversationID);
|
chats = Backend.getChatsByConversationID(conversationID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLabel() {
|
void setLabel() {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class ConversationThreadModel extends FlutterFlowModel<ConversationThreadWidget>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState(BuildContext context) {
|
void initState(BuildContext context) {
|
||||||
conversation = Server.getLoadedConversation();
|
conversation = Backend.getLoadedConversation();
|
||||||
promptBoxModel = createModel(context, () => PromptBoxModel());
|
promptBoxModel = createModel(context, () => PromptBoxModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,9 +53,7 @@ class _ConversationThreadWidgetState extends State<ConversationThreadWidget> {
|
|||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: AppTheme.primaryBackground,
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@@ -65,12 +63,12 @@ class _ConversationThreadWidgetState extends State<ConversationThreadWidget> {
|
|||||||
listenable: _model.conversation,
|
listenable: _model.conversation,
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: Server.getLoadedConversation().chats.length,
|
itemCount: Backend.getLoadedConversation().chats.length,
|
||||||
padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,),
|
padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,),
|
||||||
reverse: false,
|
reverse: false,
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return ChatBubbleWidget(c: Server.getLoadedConversation().chats[index]);
|
return ChatBubbleWidget(c: Backend.getLoadedConversation().chats[index]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -79,7 +77,7 @@ class _ConversationThreadWidgetState extends State<ConversationThreadWidget> {
|
|||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: AppTheme.secondaryBackground,
|
color: AppTheme.medium,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 3,
|
blurRadius: 3,
|
||||||
@@ -95,74 +93,74 @@ class _ConversationThreadWidgetState extends State<ConversationThreadWidget> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
// Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
// mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
// children: [
|
||||||
Expanded(
|
// Expanded(
|
||||||
child: Padding(
|
// child: Padding(
|
||||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0),
|
// padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0),
|
||||||
child: SingleChildScrollView(
|
// child: SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
// scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
// child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
// mainAxisSize: MainAxisSize.max,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
// children: [
|
||||||
// FlutterFlowMediaDisplay(
|
// FlutterFlowMediaDisplay(
|
||||||
// path: '',
|
// path: '',
|
||||||
// imageBuilder: (path) => ClipRRect(
|
// imageBuilder: (path) => ClipRRect(
|
||||||
// borderRadius: BorderRadius.circular(8),
|
// borderRadius: BorderRadius.circular(8),
|
||||||
// child: CachedNetworkImage(
|
// child: CachedNetworkImage(
|
||||||
// fadeInDuration: const Duration(milliseconds: 500),
|
// fadeInDuration: const Duration(milliseconds: 500),
|
||||||
// fadeOutDuration:
|
// fadeOutDuration:
|
||||||
// const Duration(milliseconds: 500),
|
// const Duration(milliseconds: 500),
|
||||||
// imageUrl: path,
|
// imageUrl: path,
|
||||||
// width: 120,
|
// width: 120,
|
||||||
// height: 100,
|
// height: 100,
|
||||||
// fit: BoxFit.cover,
|
// fit: BoxFit.cover,
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// TODO
|
// // TODO
|
||||||
// videoPlayerBuilder: (path) =>
|
// videoPlayerBuilder: (path) =>
|
||||||
// FlutterFlowVideoPlayer(
|
// FlutterFlowVideoPlayer(
|
||||||
// path: path,
|
// path: path,
|
||||||
// width: 300,
|
// width: 300,
|
||||||
// autoPlay: false,
|
// autoPlay: false,
|
||||||
// looping: true,
|
// looping: true,
|
||||||
// showControls: true,
|
// showControls: true,
|
||||||
// allowFullScreen: true,
|
// allowFullScreen: true,
|
||||||
// allowPlaybackSpeedMenu: false,
|
// allowPlaybackSpeedMenu: false,
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
Align(
|
// Align(
|
||||||
alignment: const AlignmentDirectional(-1, -1),
|
// alignment: const AlignmentDirectional(-1, -1),
|
||||||
// TODO: Make the delete button conditional upon media upload.
|
// // TODO: Make the delete button conditional upon media upload.
|
||||||
child: FlutterFlowIconButton(
|
// child: FlutterFlowIconButton(
|
||||||
borderColor:
|
// borderColor:
|
||||||
AppTheme.error,
|
// AppTheme.error,
|
||||||
borderRadius: 20,
|
// borderRadius: 20,
|
||||||
borderWidth: 2,
|
// borderWidth: 2,
|
||||||
buttonSize: 40,
|
// buttonSize: 40,
|
||||||
fillColor: AppTheme.primaryBackground,
|
// fillColor: AppTheme.primaryBackground,
|
||||||
icon: const Icon(
|
// icon: const Icon(
|
||||||
Icons.delete_outline_rounded,
|
// Icons.delete_outline_rounded,
|
||||||
color: AppTheme.error,
|
// color: AppTheme.error,
|
||||||
size: 24,
|
// size: 24,
|
||||||
),
|
// ),
|
||||||
onPressed: () {
|
// onPressed: () {
|
||||||
print('IconButton pressed ...');
|
// print('IconButton pressed ...');
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
]
|
// ]
|
||||||
.divide(const SizedBox(width: 8))
|
// .divide(const SizedBox(width: 8))
|
||||||
.addToStart(const SizedBox(width: 16))
|
// .addToStart(const SizedBox(width: 16))
|
||||||
.addToEnd(const SizedBox(width: 16)),
|
// .addToEnd(const SizedBox(width: 16)),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
Form(
|
Form(
|
||||||
key: _model.formKey,
|
key: _model.formKey,
|
||||||
autovalidateMode: AutovalidateMode.disabled,
|
autovalidateMode: AutovalidateMode.disabled,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:app/components/conversation_thread/conversation.dart';
|
||||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
@@ -9,14 +10,18 @@ import 'model_item_model.dart';
|
|||||||
export 'model_item_model.dart';
|
export 'model_item_model.dart';
|
||||||
|
|
||||||
class ModelItemWidget extends StatefulWidget {
|
class ModelItemWidget extends StatefulWidget {
|
||||||
const ModelItemWidget({super.key});
|
final Conversation conversation;
|
||||||
|
const ModelItemWidget({super.key, required this.conversation});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ModelItemWidget> createState() => _ModelItemWidgetState();
|
State<ModelItemWidget> createState() => _ModelItemWidgetState(conversation);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ModelItemWidgetState extends State<ModelItemWidget> {
|
class _ModelItemWidgetState extends State<ModelItemWidget> {
|
||||||
late ModelItemModel _model;
|
late ModelItemModel _model;
|
||||||
|
late Conversation conversation;
|
||||||
|
|
||||||
|
_ModelItemWidgetState(this.conversation);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setState(VoidCallback callback) {
|
void setState(VoidCallback callback) {
|
||||||
@@ -78,22 +83,22 @@ class _ModelItemWidgetState extends State<ModelItemWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0),
|
padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text( // Label
|
||||||
'Randy Peterson',
|
conversation.conversationLabel,
|
||||||
style: AppTheme.bodyMedium,
|
style: AppTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0),
|
padding: const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0),
|
||||||
child: Text(
|
child: Text( // Sub-label
|
||||||
'name@domainname.com',
|
conversation.conversationSubLabel,
|
||||||
style: AppTheme.bodySmall,
|
style: AppTheme.bodySmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class PromptBoxWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
||||||
late PromptBoxModel _model;
|
late PromptBoxModel _model;
|
||||||
|
late String entry_text;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setState(VoidCallback callback) {
|
void setState(VoidCallback callback) {
|
||||||
@@ -69,7 +70,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
|||||||
buttonSize: 40,
|
buttonSize: 40,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.file_upload_outlined,
|
Icons.file_upload_outlined,
|
||||||
color: AppTheme.secondaryText,
|
color: AppTheme.darkest,
|
||||||
size: 30,
|
size: 30,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -88,7 +89,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
|||||||
buttonSize: 40,
|
buttonSize: 40,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.photo_outlined,
|
Icons.photo_outlined,
|
||||||
color: AppTheme.secondaryText,
|
color: AppTheme.darkest,
|
||||||
size: 30,
|
size: 30,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -105,13 +106,28 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
|||||||
borderRadius: 30,
|
borderRadius: 30,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
buttonSize: 40,
|
buttonSize: 40,
|
||||||
icon: const Icon(
|
icon: // Backend.stt.speechToText.isListening ?
|
||||||
Icons.mic_none,
|
// const Icon(
|
||||||
color: AppTheme.secondaryText,
|
// Icons.mic_none,
|
||||||
|
// color: AppTheme.error,
|
||||||
|
// size: 30,
|
||||||
|
// ) :
|
||||||
|
const Icon(
|
||||||
|
Icons.mic_off_outlined,
|
||||||
|
color: AppTheme.darkest,
|
||||||
size: 30,
|
size: 30,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
print('IconButton pressed ...');
|
if(Backend.stt.isSpeechEnabled()) {
|
||||||
|
if(Backend.stt.speechToText.isListening) {
|
||||||
|
print('De-activating STT.');
|
||||||
|
Backend.stt.stopListening();
|
||||||
|
}
|
||||||
|
print('Activating STT.');
|
||||||
|
entry_text = Backend.stt.startListening() as String;
|
||||||
|
} else {
|
||||||
|
print('Microphone is unavailable for STT');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -126,7 +142,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
|||||||
buttonSize: 40,
|
buttonSize: 40,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.videocam_outlined,
|
Icons.videocam_outlined,
|
||||||
color: AppTheme.secondaryText,
|
color: AppTheme.darkest,
|
||||||
size: 30,
|
size: 30,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -145,7 +161,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
|||||||
buttonSize: 40,
|
buttonSize: 40,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.camera_alt_outlined,
|
Icons.camera_alt_outlined,
|
||||||
color: AppTheme.secondaryText,
|
color: AppTheme.darkest,
|
||||||
size: 30,
|
size: 30,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -206,21 +222,28 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
|||||||
),
|
),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: AppTheme.alternate,
|
fillColor: AppTheme.alternate,
|
||||||
suffixIcon: const Icon(
|
suffixIcon: GestureDetector(
|
||||||
Icons.send,
|
onTap: () {
|
||||||
color: AppTheme.primary,
|
// TODO: Connect with multimedia
|
||||||
size: 30,
|
entry_text = _model.textController.text;
|
||||||
|
Backend.respondWhenReady(entry_text);
|
||||||
|
_model.textController?.clear(); // Clear the text field
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
Icons.send,
|
||||||
|
color: AppTheme.primary,
|
||||||
|
size: 30,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: AppTheme.bodyMedium,
|
style: AppTheme.displayMedium,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
validator: _model.textControllerValidator.asValidator(context),
|
validator: _model.textControllerValidator.asValidator(context),
|
||||||
textInputAction: TextInputAction.send, // "Send" on keyboard
|
textInputAction: TextInputAction.send, // "Send" on keyboard
|
||||||
onFieldSubmitted: (text) {
|
onFieldSubmitted: (text) {
|
||||||
// TODO: Clear text box
|
Backend.respondWhenReady(text); // TODO: Connect with multimedia
|
||||||
Server.respondWhenReady(text); // TODO: Connect with multimedia
|
|
||||||
_model.textController?.clear(); // Clear the text field
|
_model.textController?.clear(); // Clear the text field
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ BottomNavigationBarItem _buildNavigationItem(int index) {
|
|||||||
iconData = Icons.settings;
|
iconData = Icons.settings;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
label = 'Home';
|
label = 'Chats';
|
||||||
iconData = Icons.home;
|
iconData = Icons.chat;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return BottomNavigationBarItem(
|
return BottomNavigationBarItem(
|
||||||
icon: Icon(iconData),
|
icon: Icon(iconData),
|
||||||
label: label,
|
label: label,
|
||||||
|
backgroundColor: AppTheme.lightest,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,15 +64,15 @@ class MyApp extends ConsumerWidget {
|
|||||||
final selectedIndex = ref.watch(selectedIndexProvider);
|
final selectedIndex = ref.watch(selectedIndexProvider);
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'App',
|
title: 'One AI',
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
backgroundColor: AppTheme.secondaryBackground,
|
backgroundColor: AppTheme.medium,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: AppTheme.secondaryBackground,
|
backgroundColor: AppTheme.secondaryBackground,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
title: const Text(
|
title: const Text(
|
||||||
'App',
|
'One AI',
|
||||||
style: AppTheme.headlineLarge,
|
style: AppTheme.displayLarge,
|
||||||
),
|
),
|
||||||
actions: const [],
|
actions: const [],
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
@@ -79,6 +80,8 @@ class MyApp extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
body: _pages[selectedIndex],
|
body: _pages[selectedIndex],
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
backgroundColor: AppTheme.darkest,
|
||||||
|
selectedItemColor: AppTheme.lightest,
|
||||||
// Set the number of items to the length of the pages list
|
// Set the number of items to the length of the pages list
|
||||||
items: List.generate(_pages.length, (index) => _buildNavigationItem(index)),
|
items: List.generate(_pages.length, (index) => _buildNavigationItem(index)),
|
||||||
currentIndex: selectedIndex,
|
currentIndex: selectedIndex,
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class _ConversationWidgetState extends State<ConversationWidget> {
|
|||||||
buttonSize: 60,
|
buttonSize: 60,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.arrow_back_rounded,
|
Icons.arrow_back_rounded,
|
||||||
color: AppTheme.primaryTextColor,
|
color: AppTheme.lightest,
|
||||||
size: 30,
|
size: 30,
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -80,21 +80,16 @@ class _ConversationWidgetState extends State<ConversationWidget> {
|
|||||||
title: wrapWithModel(
|
title: wrapWithModel(
|
||||||
model: _model.modelItemModel,
|
model: _model.modelItemModel,
|
||||||
updateCallback: () => setState(() {}),
|
updateCallback: () => setState(() {}),
|
||||||
child: const ModelItemWidget(),
|
child: ModelItemWidget(conversation: conversation,),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8),
|
padding: const EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8),
|
||||||
// ---------- Options Button
|
// ---------- Options Button
|
||||||
child: FlutterFlowIconButton(
|
child: FlutterFlowIconButton(
|
||||||
borderColor: AppTheme.alternate,
|
|
||||||
borderRadius: 12,
|
|
||||||
borderWidth: 2,
|
|
||||||
buttonSize: 40,
|
|
||||||
fillColor: AppTheme.primaryBackground,
|
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.more_vert,
|
Icons.more_vert,
|
||||||
color: AppTheme.primaryTextColor,
|
color: AppTheme.lightest,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import 'package:app/util/server.dart';
|
|||||||
|
|
||||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
|
|
||||||
import 'conversations_list_model.dart';
|
import 'conversations_list_model.dart';
|
||||||
export 'conversations_list_model.dart';
|
export 'conversations_list_model.dart';
|
||||||
@@ -29,10 +28,10 @@ class _ConversationsListWidgetState extends State<ConversationsListWidget> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
Server().init();
|
Backend().init();
|
||||||
|
|
||||||
if(conversations.isEmpty) {
|
if(conversations.isEmpty) {
|
||||||
List<Conversation> convoys = Server.conversations;
|
List<Conversation> convoys = Backend.conversations;
|
||||||
for(Conversation c in convoys) {
|
for(Conversation c in convoys) {
|
||||||
conversations.add(ConversationWidget(conversation: c,));
|
conversations.add(ConversationWidget(conversation: c,));
|
||||||
}
|
}
|
||||||
@@ -55,138 +54,162 @@ class _ConversationsListWidgetState extends State<ConversationsListWidget> {
|
|||||||
: FocusScope.of(context).unfocus(),
|
: FocusScope.of(context).unfocus(),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
top: true,
|
top: true,
|
||||||
child: Column(
|
child: Stack(
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
const Padding(
|
Column(
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0),
|
mainAxisSize: MainAxisSize.max,
|
||||||
child: Text(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
'Conversations',
|
children: [
|
||||||
style: AppTheme.labelMedium,
|
Expanded(
|
||||||
),
|
child: Align(
|
||||||
),
|
alignment: const AlignmentDirectional(0, 0),
|
||||||
Expanded(
|
child: ListView.builder(
|
||||||
child: Align(
|
itemCount: conversations.length,
|
||||||
alignment: const AlignmentDirectional(0, 0),
|
padding: EdgeInsets.zero,
|
||||||
child: ListView.builder(
|
reverse: true,
|
||||||
itemCount: conversations.length,
|
scrollDirection: Axis.vertical,
|
||||||
padding: EdgeInsets.zero,
|
itemBuilder: (context, index) {
|
||||||
reverse: true,
|
return Align(
|
||||||
scrollDirection: Axis.vertical,
|
alignment: const AlignmentDirectional(0, 0),
|
||||||
itemBuilder: (context, index) {
|
child: InkWell(
|
||||||
return Align(
|
splashColor: AppTheme.accent,
|
||||||
alignment: const AlignmentDirectional(0, 0),
|
focusColor: AppTheme.lightest,
|
||||||
child: InkWell(
|
hoverColor: AppTheme.dark,
|
||||||
splashColor: Colors.transparent,
|
highlightColor: AppTheme.darkest,
|
||||||
focusColor: Colors.transparent,
|
// TODO
|
||||||
hoverColor: Colors.transparent,
|
// onTap: () async {
|
||||||
highlightColor: Colors.transparent,
|
// context.pushNamed('Conversation');
|
||||||
// TODO
|
// },
|
||||||
// onTap: () async {
|
child: Container(
|
||||||
// context.pushNamed('Conversation');
|
decoration: const BoxDecoration(
|
||||||
// },
|
//color: AppTheme.darkest,
|
||||||
child: Row(
|
border: Border(
|
||||||
mainAxisSize: MainAxisSize.max,
|
top: BorderSide(width: 2.0, color: AppTheme.darkest),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
decoration: const BoxDecoration(color: AppTheme.secondaryBackground,),
|
|
||||||
child: const Align(
|
|
||||||
alignment: AlignmentDirectional(0, 0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.settings_outlined, // TODO: Dynamically generate or allow users to color code/tag conversations.
|
|
||||||
color: AppTheme.secondaryText,
|
|
||||||
size: 48,
|
|
||||||
),
|
),
|
||||||
),
|
child: Row(
|
||||||
),
|
mainAxisSize: MainAxisSize.max,
|
||||||
Expanded(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
child: Align(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
alignment: const AlignmentDirectional(-1, 0),
|
|
||||||
child: Container(
|
children: [
|
||||||
width: 100,
|
Container(
|
||||||
height: 100,
|
width: 100,
|
||||||
constraints: BoxConstraints(
|
height: 100,
|
||||||
minWidth: MediaQuery.sizeOf(context).width,
|
decoration: const BoxDecoration(color: AppTheme.lightest,),
|
||||||
),
|
child: const Align(
|
||||||
decoration: const BoxDecoration(
|
alignment: AlignmentDirectional(0, 0),
|
||||||
color: AppTheme.secondaryBackground,
|
child: Icon(
|
||||||
shape: BoxShape.rectangle,
|
Icons.settings_outlined, // TODO: Dynamically generate or allow users to color code/tag conversations.
|
||||||
),
|
color: AppTheme.secondaryText,
|
||||||
child: Column(
|
size: 48,
|
||||||
mainAxisSize: MainAxisSize.max,
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
),
|
||||||
children: [
|
Expanded(
|
||||||
Align(
|
child: Align(
|
||||||
alignment: const AlignmentDirectional(-1, 0),
|
alignment: const AlignmentDirectional(-1, 0),
|
||||||
child: Row(
|
child: Container(
|
||||||
mainAxisSize: MainAxisSize.max,
|
width: 100,
|
||||||
children: [
|
height: 100,
|
||||||
Text(
|
constraints: BoxConstraints(
|
||||||
conversations[index].conversation.conversationLabel,
|
minWidth: MediaQuery.sizeOf(context).width,
|
||||||
style: AppTheme.headlineLarge,
|
),
|
||||||
),
|
decoration: const BoxDecoration(
|
||||||
],
|
color: AppTheme.lightest,
|
||||||
),
|
shape: BoxShape.rectangle,
|
||||||
),
|
),
|
||||||
Align(
|
child: Column(
|
||||||
alignment:
|
mainAxisSize: MainAxisSize.max,
|
||||||
const AlignmentDirectional(-1, 0),
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
child: Row(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: [
|
||||||
children: [
|
Align(
|
||||||
Expanded(
|
alignment: const AlignmentDirectional(-1, 0),
|
||||||
child: Text(
|
child: Row(
|
||||||
conversations[index].conversation.conversationSubLabel,
|
mainAxisSize: MainAxisSize.max,
|
||||||
style: AppTheme.bodyMedium,
|
children: [
|
||||||
|
Text(
|
||||||
|
conversations[index].conversation.conversationLabel,
|
||||||
|
style: AppTheme.labelLarge,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Align(
|
||||||
],
|
alignment:
|
||||||
|
const AlignmentDirectional(-1, 0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
conversations[index].conversation.conversationSubLabel,
|
||||||
|
style: AppTheme.labelSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
Container(
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: AppTheme.lightest,
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.chevron_right_rounded,
|
||||||
|
color: AppTheme.secondaryText,
|
||||||
|
size: 48,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
onTap: () => {
|
||||||
|
Backend.loadConversation(index),
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ConversationWidget(conversation: Backend.getLoadedConversation(),),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onLongPress: () => {
|
||||||
|
// TODO: Need to implement.
|
||||||
|
print("opening options")
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Container(
|
);
|
||||||
width: 100,
|
}
|
||||||
height: 100,
|
),
|
||||||
decoration: const BoxDecoration(
|
),
|
||||||
color: AppTheme.secondaryBackground,
|
),
|
||||||
),
|
],
|
||||||
child: const Icon(
|
),
|
||||||
Icons.chevron_right_rounded,
|
Positioned(
|
||||||
color: AppTheme.secondaryText,
|
bottom: 20.0, // Adjust positioning as needed
|
||||||
size: 48,
|
right: 20.0, // Adjust positioning as needed
|
||||||
),
|
child: FloatingActionButton(
|
||||||
),
|
onPressed: () => {
|
||||||
],
|
Backend.loadConversation(Backend.maxConversationID), // TODO: Dynamically get the next ID
|
||||||
),
|
conversations.add(ConversationWidget(conversation: Backend.getLoadedConversation())),
|
||||||
onTap: () => {
|
Navigator.push(
|
||||||
// TODO: Need to implement.
|
context,
|
||||||
// Get the conversation object based on the index
|
MaterialPageRoute(
|
||||||
// Use Navigator to push a new ConversationWidget with conversation data
|
builder: (context) => ConversationWidget(conversation: Backend.getLoadedConversation(),),
|
||||||
Server.loadConversation(index),
|
),
|
||||||
Navigator.push(
|
), // Handle the button press
|
||||||
context,
|
},
|
||||||
MaterialPageRoute(
|
backgroundColor: AppTheme.darkest,
|
||||||
builder: (context) => ConversationWidget(conversation: Server.getLoadedConversation(),),
|
child: const Icon(
|
||||||
),
|
Icons.bubble_chart,
|
||||||
)
|
color: AppTheme.lightest,
|
||||||
},
|
|
||||||
onLongPress: () => {
|
|
||||||
// TODO: Need to implement.
|
|
||||||
print("opening options")
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+30
-22
@@ -1,37 +1,45 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AppTheme {
|
class AppTheme {
|
||||||
static const primary = Colors.deepPurple;
|
static const darkest = Color.fromARGB(255, 45, 45, 145);
|
||||||
static const secondary = Colors.deepPurpleAccent;
|
static const dark = Color.fromARGB(255, 81, 81, 196);
|
||||||
static const tertiary = Colors.white;
|
static const medium = Color.fromARGB(255, 107, 107, 201);
|
||||||
static const alternate = Colors.deepPurple;
|
static const light = Color.fromARGB(255, 155, 155, 255);
|
||||||
|
static const lightest = Color.fromARGB(255, 172, 175, 255);
|
||||||
|
static const accent = Color.fromARGB(255, 255, 113, 0);
|
||||||
|
static const accent2 = Color.fromARGB(255, 255, 164, 79);
|
||||||
|
|
||||||
static const accent1 = Colors.amber;
|
static const primaryBackground = darkest;
|
||||||
static const accent4 = Colors.amber;
|
static const secondaryBackground = medium;
|
||||||
|
static const primaryTextColor = darkest;
|
||||||
|
static const primaryText = TextStyle(color: primaryTextColor, fontSize: 16);
|
||||||
|
static const secondaryText = dark;
|
||||||
|
|
||||||
static const primaryBackground = Colors.deepPurpleAccent;
|
static const accent1 = lightest;
|
||||||
static const secondaryBackground = Colors.deepPurple;
|
static const accent4 = accent2;
|
||||||
|
static const primary = dark;
|
||||||
|
static const secondary = medium;
|
||||||
|
static const tertiary = light;
|
||||||
|
static const alternate = lightest;
|
||||||
|
|
||||||
static const primaryTextColor = Colors.white;
|
static const bodyLarge = TextStyle(color: light, fontSize: 24, fontWeight: FontWeight.bold);
|
||||||
static const primaryText = TextStyle(color: primaryTextColor, fontSize: 12);
|
static const bodyMedium = TextStyle(color: lightest, fontSize: 16);
|
||||||
static const secondaryText = Colors.white;
|
static const bodySmall = TextStyle(color: light, fontSize: 12);
|
||||||
|
|
||||||
static const bodyLarge = TextStyle(color: Colors.white, fontSize: 24);
|
|
||||||
static const bodyMedium = TextStyle(color: Colors.white, fontSize: 12);
|
|
||||||
static const bodySmall = TextStyle(color: Colors.white, fontSize: 10);
|
|
||||||
|
|
||||||
static const error = Colors.red;
|
static const error = Colors.red;
|
||||||
static const info = Colors.grey;
|
static const info = Colors.grey;
|
||||||
|
|
||||||
static const displaySmall = TextStyle(color: Colors.white, fontSize: 12);
|
static const displayLarge = TextStyle(color: darkest, fontSize: 24, fontWeight: FontWeight.bold);
|
||||||
|
static const displayMedium = TextStyle(color: dark, fontSize: 16, fontWeight: FontWeight.bold);
|
||||||
|
static const displaySmall = TextStyle(color: lightest, fontSize: 12, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
static const labelLarge = TextStyle(color: Colors.black, fontSize: 24);
|
static const labelLarge = TextStyle(color: darkest, fontSize: 24, fontWeight: FontWeight.bold);
|
||||||
static const labelMedium = TextStyle(color: Colors.grey, fontSize: 16);
|
static const labelMedium = TextStyle(color: dark, fontSize: 16, fontWeight: FontWeight.bold);
|
||||||
static const labelSmall = TextStyle(color: Colors.grey, fontSize: 12);
|
static const labelSmall = TextStyle(color: medium, fontSize: 12, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
static const headlineLarge = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold);
|
static const headlineLarge = TextStyle(color: medium, fontWeight: FontWeight.bold);
|
||||||
static const headlineMedium = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold);
|
static const headlineMedium = TextStyle(color: light, fontWeight: FontWeight.bold);
|
||||||
static const headlineSmall = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold);
|
static const headlineSmall = TextStyle(color: lightest, fontWeight: FontWeight.bold);
|
||||||
|
|
||||||
static const titleLarge = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 24);
|
static const titleLarge = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 24);
|
||||||
static const titleSmall = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 16);
|
static const titleSmall = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 16);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class Model {
|
||||||
|
late String name;
|
||||||
|
|
||||||
|
Model(this.name);
|
||||||
|
}
|
||||||
@@ -6,11 +6,13 @@ import 'package:app/components/conversation_thread/chat.dart';
|
|||||||
|
|
||||||
import 'package:app/util/speech_to_text.dart';
|
import 'package:app/util/speech_to_text.dart';
|
||||||
|
|
||||||
|
import 'model.dart';
|
||||||
|
|
||||||
// Uses Singleton design pattern to ensure that any server references across the app are using the same instance.
|
// Uses Singleton design pattern to ensure that any server references across the app are using the same instance.
|
||||||
class Server {
|
class Backend {
|
||||||
// SERVER VARIABLES
|
// SERVER VARIABLES
|
||||||
static final Server _single_server = Server._internal();
|
static final Backend _single_backend = Backend._internal();
|
||||||
Server._internal();
|
Backend._internal();
|
||||||
// TODO: One-time process to establish server connection is required for easy setup/maintenance.
|
// TODO: One-time process to establish server connection is required for easy setup/maintenance.
|
||||||
static final url = "http://10.0.2.2:11434/api/"; // For emulated device in testing
|
static final url = "http://10.0.2.2:11434/api/"; // For emulated device in testing
|
||||||
//static final url = "http://192.168.1.68:11434/api"; // For local access to home server
|
//static final url = "http://192.168.1.68:11434/api"; // For local access to home server
|
||||||
@@ -28,30 +30,36 @@ class Server {
|
|||||||
// PRIVACY VARIABLES
|
// PRIVACY VARIABLES
|
||||||
static bool _historyEnabled = true;
|
static bool _historyEnabled = true;
|
||||||
static late int conversationID;
|
static late int conversationID;
|
||||||
|
static late int maxConversationID;
|
||||||
static List<Conversation> conversations = [];
|
static List<Conversation> conversations = [];
|
||||||
static List<Chat> loadedChats = [];
|
static List<Chat> loadedChats = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// RUNTIME & STATUS VARIABLES
|
// RUNTIME & STATUS VARIABLES
|
||||||
|
static late Model model;
|
||||||
static String prompt = "";
|
static String prompt = "";
|
||||||
static String response = "";
|
static String response = "";
|
||||||
static bool initiated = false;
|
static bool initiated = false;
|
||||||
|
|
||||||
// TODO: Verify all different components of STT, TTS, LLM, etc have been initialized.
|
// TODO: Verify all different components of STT, TTS, LLM, etc have been initialized.
|
||||||
// TODO: Handshake w/ server to validate identity
|
// TODO: Handshake w/ server to validate identity
|
||||||
factory Server() {
|
factory Backend() {
|
||||||
// Speech-To-Text engine activation.
|
// Speech-To-Text engine activation.
|
||||||
_speechEnabled = stt.isSpeechEnabled();
|
_speechEnabled = stt.isSpeechEnabled();
|
||||||
|
|
||||||
// loadedChats = conversations[conversationID].chats;
|
// loadedChats = conversations[conversationID].chats;
|
||||||
|
|
||||||
return _single_server;
|
return _single_backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
initiated = true;
|
initiated = true;
|
||||||
|
|
||||||
|
// TODO: Dynamically get available models and models that can be pulled.
|
||||||
|
// TODO: Dynamically get the user's default model
|
||||||
|
model = Model("llama3"); // Primarily used llama3, testing gemma2
|
||||||
|
|
||||||
// TODO: Dynamically obtain conversations.
|
// TODO: Dynamically obtain conversations.
|
||||||
conversations.add(
|
conversations.add(
|
||||||
Conversation.completeConversation(
|
Conversation.completeConversation(
|
||||||
@@ -65,6 +73,8 @@ class Server {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
maxConversationID = conversations.length;
|
||||||
|
|
||||||
// conversations.add(
|
// conversations.add(
|
||||||
// Conversation.completeConversation(
|
// Conversation.completeConversation(
|
||||||
// 1,
|
// 1,
|
||||||
@@ -81,9 +91,12 @@ class Server {
|
|||||||
|
|
||||||
|
|
||||||
// PRIMARY INTERFACE
|
// PRIMARY INTERFACE
|
||||||
|
@Deprecated("Use STT interface directly and use respondWhenReady().")
|
||||||
static Future<String> sttGetResponseWhenReady() async {
|
static Future<String> sttGetResponseWhenReady() async {
|
||||||
// TODO: In case on-device STT is unavailable, use server-side STT service.
|
// TODO: In case on-device STT is unavailable, use server-side STT service.
|
||||||
if (!_speechEnabled) "Unable to process Speech-To-Text on-device.";
|
if (!_speechEnabled) {
|
||||||
|
return "Unable to process Speech-To-Text on-device.";
|
||||||
|
}
|
||||||
|
|
||||||
prompt = stt.getTextWhenReady() as String;
|
prompt = stt.getTextWhenReady() as String;
|
||||||
conversations[conversationID].add(Chat(prompt, true));
|
conversations[conversationID].add(Chat(prompt, true));
|
||||||
@@ -109,6 +122,11 @@ class Server {
|
|||||||
// Load conversation should be called prior to any other server requests.
|
// Load conversation should be called prior to any other server requests.
|
||||||
static void loadConversation(id) {
|
static void loadConversation(id) {
|
||||||
conversationID = id;
|
conversationID = id;
|
||||||
|
if(maxConversationID == id) {
|
||||||
|
conversations.add(Conversation(conversationID, "Conversation $id", "Another AI conversation"));
|
||||||
|
maxConversationID++;
|
||||||
|
}
|
||||||
|
|
||||||
loadedChats = conversations[conversationID].chats;
|
loadedChats = conversations[conversationID].chats;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +162,7 @@ class Server {
|
|||||||
List<Map<String, String>> messageList = convertMessages();
|
List<Map<String, String>> messageList = convertMessages();
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"model": "llama3",
|
"model": model.name,
|
||||||
"messages": messageList,
|
"messages": messageList,
|
||||||
"stream": false,
|
"stream": false,
|
||||||
};
|
};
|
||||||
@@ -152,7 +170,7 @@ class Server {
|
|||||||
else {
|
else {
|
||||||
endpoint = "generate";
|
endpoint = "generate";
|
||||||
data = {
|
data = {
|
||||||
"model": "llama3",
|
"model": model.name,
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
"stream": false,
|
"stream": false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import 'package:speech_to_text/speech_to_text.dart' as stt;
|
|||||||
|
|
||||||
/// Handles standard Speech-To-Text using on-device processing.
|
/// Handles standard Speech-To-Text using on-device processing.
|
||||||
class SpeechToText {
|
class SpeechToText {
|
||||||
final stt.SpeechToText _speechToText = stt.SpeechToText();
|
final stt.SpeechToText speechToText = stt.SpeechToText();
|
||||||
bool _speechEnabled = false;
|
bool speechEnabled = false;
|
||||||
String text = "";
|
String text = "";
|
||||||
|
|
||||||
// Check if speech recognition is enabled.
|
// Check if speech recognition is enabled.
|
||||||
bool isSpeechEnabled() => _speechEnabled;
|
bool isSpeechEnabled() => speechEnabled;
|
||||||
|
|
||||||
Future<String> getTextWhenReady() async {
|
Future<String> getTextWhenReady() async {
|
||||||
await _speechToText.stop();
|
await speechToText.stop();
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,22 +22,22 @@ class SpeechToText {
|
|||||||
void reset() => text = "";
|
void reset() => text = "";
|
||||||
|
|
||||||
/// Start a speech recognition session
|
/// Start a speech recognition session
|
||||||
void _startListening() async => await _speechToText.listen(onResult: _onSpeechResult);
|
void startListening() async => await speechToText.listen(onResult: onSpeechResult);
|
||||||
|
|
||||||
/// Manually stop the active speech recognition session
|
/// Manually stop the active speech recognition session
|
||||||
/// Note that there are also timeouts that each platform enforces
|
/// Note that there are also timeouts that each platform enforces
|
||||||
/// and the SpeechToText plugin supports setting timeouts on the
|
/// and the SpeechToText plugin supports setting timeouts on the
|
||||||
/// listen method.
|
/// listen method.
|
||||||
void _stopListening() async => await _speechToText.stop();
|
void stopListening() async => await speechToText.stop();
|
||||||
|
|
||||||
SpeechToText() {
|
SpeechToText() {
|
||||||
_initSpeech();
|
initSpeech();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This has to happen only once per app
|
/// This has to happen only once per app
|
||||||
void _initSpeech() async => _speechEnabled = await _speechToText.initialize();
|
void initSpeech() async => speechEnabled = await speechToText.initialize();
|
||||||
|
|
||||||
/// This is the callback that the SpeechToText plugin calls when
|
/// This is the callback that the SpeechToText plugin calls when
|
||||||
/// the platform returns recognized words.
|
/// the platform returns recognized words.
|
||||||
void _onSpeechResult(SpeechRecognitionResult result) => text = result.recognizedWords;
|
String onSpeechResult(SpeechRecognitionResult result) => (text = result.recognizedWords);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user