dynamic chat bubbles implemented along with conversation support. currently only hard-coded dummy data.
This commit is contained in:
@@ -1,7 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Chat {
|
||||
final String content;
|
||||
final bool role;
|
||||
@@ -9,14 +5,5 @@ class Chat {
|
||||
// Most basic chat, text from server or from user.
|
||||
Chat(this.content, this.role);
|
||||
|
||||
String toJson() {
|
||||
Map<String, dynamic> c = {
|
||||
"content": content,
|
||||
"role": role,
|
||||
};
|
||||
|
||||
return json.encode(c);
|
||||
}
|
||||
|
||||
// TODO: Overload constructors to allow for multimedia.
|
||||
}
|
||||
@@ -1,15 +1,40 @@
|
||||
import 'package:app/util/server.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'chat.dart';
|
||||
|
||||
class Conversation {
|
||||
class Conversation with ChangeNotifier {
|
||||
int conversationID;
|
||||
String conversationLabel;
|
||||
String conversationSubLabel;
|
||||
List<Chat> chats = [];
|
||||
|
||||
Conversation(this.conversationID) {
|
||||
chats = Server().getConversationByID(conversationID);
|
||||
Conversation(this.conversationID, this.conversationLabel, this.conversationSubLabel);
|
||||
|
||||
Conversation.completeConversation(this.conversationID, this.conversationLabel, this.conversationSubLabel, this.chats);
|
||||
|
||||
void add(Chat message) {
|
||||
chats.add(message);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void loadChats() {
|
||||
chats = Server.getChatsByConversationID(conversationID);
|
||||
}
|
||||
|
||||
void setLabel() {
|
||||
// TODO: Need to implement
|
||||
}
|
||||
|
||||
void setSubLabel() {
|
||||
// TODO: Need to implement
|
||||
}
|
||||
|
||||
List<Chat> getChats() {
|
||||
return chats;
|
||||
}
|
||||
|
||||
void setLabels(String cLabel, String cSubLabel) {
|
||||
conversationLabel = cLabel;
|
||||
conversationSubLabel = cSubLabel;
|
||||
}
|
||||
}
|
||||
@@ -14,16 +14,15 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
class ConversationThreadModel extends FlutterFlowModel<ConversationThreadWidget> {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
late PromptBoxModel promptBoxModel;
|
||||
late int ?conversationID;
|
||||
late Conversation conversation;
|
||||
|
||||
// If there's a conversation ID, retrieve the conversation history.
|
||||
// Otherwise, start a new conversation with user defaults.
|
||||
ConversationThreadModel(this.conversationID);
|
||||
void updateConversation() {
|
||||
conversation = Server.getLoadedConversation();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState(BuildContext context) {
|
||||
conversation = Conversation(conversationID!);
|
||||
conversation = Server.getLoadedConversation();
|
||||
promptBoxModel = createModel(context, () => PromptBoxModel());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:app/components/conversation_thread/conversation.dart';
|
||||
import 'package:app/util/server.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '/components/conversation_thread/chat_bubble_widget.dart';
|
||||
import 'package:app/components/conversation_thread/conversation_thread_model.dart';
|
||||
@@ -32,7 +34,7 @@ class _ConversationThreadWidgetState extends State<ConversationThreadWidget> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_model = createModel(context, () => ConversationThreadModel(0)); // TODO
|
||||
_model = createModel(context, () => ConversationThreadModel());
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -44,132 +46,140 @@ class _ConversationThreadWidgetState extends State<ConversationThreadWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Conversation c = _model.conversation;
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppTheme.primaryBackground,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: c.chats.length,
|
||||
padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,),
|
||||
reverse: false,
|
||||
scrollDirection: Axis.vertical,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return ChatBubbleWidget(c: c.chats[index]);
|
||||
},
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppTheme.primaryBackground,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListenableBuilder(
|
||||
listenable: _model.conversation,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return ListView.builder(
|
||||
itemCount: Server.getLoadedConversation().chats.length,
|
||||
padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,),
|
||||
reverse: false,
|
||||
scrollDirection: Axis.vertical,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if(Server.getLoadedConversation().chats.last.role) {
|
||||
return ChatBubbleWidget(c: Server.getLoadedConversation().chats[index]);
|
||||
} else {
|
||||
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppTheme.secondaryBackground,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 3,
|
||||
color: Color(0x33000000),
|
||||
offset: Offset(
|
||||
0,
|
||||
-2,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
// TODO
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// FlutterFlowMediaDisplay(
|
||||
// path: '',
|
||||
// imageBuilder: (path) => ClipRRect(
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
// child: CachedNetworkImage(
|
||||
// fadeInDuration: const Duration(milliseconds: 500),
|
||||
// fadeOutDuration:
|
||||
// const Duration(milliseconds: 500),
|
||||
// imageUrl: path,
|
||||
// width: 120,
|
||||
// height: 100,
|
||||
// fit: BoxFit.cover,
|
||||
// ),
|
||||
// ),
|
||||
// TODO
|
||||
// videoPlayerBuilder: (path) =>
|
||||
// FlutterFlowVideoPlayer(
|
||||
// path: path,
|
||||
// width: 300,
|
||||
// autoPlay: false,
|
||||
// looping: true,
|
||||
// showControls: true,
|
||||
// allowFullScreen: true,
|
||||
// allowPlaybackSpeedMenu: false,
|
||||
// ),
|
||||
// ),
|
||||
Align(
|
||||
alignment: const AlignmentDirectional(-1, -1),
|
||||
// TODO: Make the delete button conditional upon media upload.
|
||||
child: FlutterFlowIconButton(
|
||||
borderColor:
|
||||
AppTheme.error,
|
||||
borderRadius: 20,
|
||||
borderWidth: 2,
|
||||
buttonSize: 40,
|
||||
fillColor: AppTheme.primaryBackground,
|
||||
icon: const Icon(
|
||||
Icons.delete_outline_rounded,
|
||||
color: AppTheme.error,
|
||||
size: 24,
|
||||
Container(
|
||||
width: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppTheme.secondaryBackground,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 3,
|
||||
color: Color(0x33000000),
|
||||
offset: Offset(
|
||||
0,
|
||||
-2,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
// TODO
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// FlutterFlowMediaDisplay(
|
||||
// path: '',
|
||||
// imageBuilder: (path) => ClipRRect(
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
// child: CachedNetworkImage(
|
||||
// fadeInDuration: const Duration(milliseconds: 500),
|
||||
// fadeOutDuration:
|
||||
// const Duration(milliseconds: 500),
|
||||
// imageUrl: path,
|
||||
// width: 120,
|
||||
// height: 100,
|
||||
// fit: BoxFit.cover,
|
||||
// ),
|
||||
// ),
|
||||
// TODO
|
||||
// videoPlayerBuilder: (path) =>
|
||||
// FlutterFlowVideoPlayer(
|
||||
// path: path,
|
||||
// width: 300,
|
||||
// autoPlay: false,
|
||||
// looping: true,
|
||||
// showControls: true,
|
||||
// allowFullScreen: true,
|
||||
// allowPlaybackSpeedMenu: false,
|
||||
// ),
|
||||
// ),
|
||||
Align(
|
||||
alignment: const AlignmentDirectional(-1, -1),
|
||||
// TODO: Make the delete button conditional upon media upload.
|
||||
child: FlutterFlowIconButton(
|
||||
borderColor:
|
||||
AppTheme.error,
|
||||
borderRadius: 20,
|
||||
borderWidth: 2,
|
||||
buttonSize: 40,
|
||||
fillColor: AppTheme.primaryBackground,
|
||||
icon: const Icon(
|
||||
Icons.delete_outline_rounded,
|
||||
color: AppTheme.error,
|
||||
size: 24,
|
||||
),
|
||||
onPressed: () {
|
||||
print('IconButton pressed ...');
|
||||
},
|
||||
),
|
||||
onPressed: () {
|
||||
print('IconButton pressed ...');
|
||||
},
|
||||
),
|
||||
),
|
||||
]
|
||||
.divide(const SizedBox(width: 8))
|
||||
.addToStart(const SizedBox(width: 16))
|
||||
.addToEnd(const SizedBox(width: 16)),
|
||||
]
|
||||
.divide(const SizedBox(width: 8))
|
||||
.addToStart(const SizedBox(width: 16))
|
||||
.addToEnd(const SizedBox(width: 16)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Form(
|
||||
key: _model.formKey,
|
||||
autovalidateMode: AutovalidateMode.disabled,
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8),
|
||||
child: wrapWithModel(
|
||||
model: _model.promptBoxModel,
|
||||
updateCallback: () => setState(() {}),
|
||||
child: const PromptBoxWidget(),
|
||||
],
|
||||
),
|
||||
Form(
|
||||
key: _model.formKey,
|
||||
autovalidateMode: AutovalidateMode.disabled,
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8),
|
||||
child: wrapWithModel(
|
||||
model: _model.promptBoxModel,
|
||||
updateCallback: () => setState(() {}),
|
||||
child: const PromptBoxWidget(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user