Skip to content

Instantly share code, notes, and snippets.

@suesitran
Created March 7, 2025 19:10
Show Gist options
  • Save suesitran/ff9fd0091738badf00759c19bd489d8a to your computer and use it in GitHub Desktop.
Save suesitran/ff9fd0091738badf00759c19bd489d8a to your computer and use it in GitHub Desktop.
ChatService - VertexAI in Firebase for Flutter Project
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:file_picker/file_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:firebase_vertexai/firebase_vertexai.dart';
import 'package:flutter/foundation.dart';
import 'package:mime/mime.dart';
class ChatService {
late final ChatSession _chatSession;
ChatService() {
final GenerativeModel model =
FirebaseVertexAI.instance.generativeModel(model: 'gemini-2.0-flash');
_chatSession = model.startChat();
}
void sendMessage(String prompt, [PlatformFile? file]) async {
// get mimetype of file if available
final String? mimeType =
file != null ? lookupMimeType(file.xFile.name) : null;
// create a new document for this chat message
final messagesRef = FirebaseFirestore.instance.collection('messages').doc();
Attachment? attachment;
if (file != null) {
// file is available, upload it to Cloud Storage
final Uint8List bytes = await file.xFile.readAsBytes();
await FirebaseStorage.instance.ref(messagesRef.path).putData(
bytes,
SettableMetadata(
contentType: mimeType ?? 'application/octet-stream'));
attachment = Attachment(
file: file.xFile.path,
mimeType: mimeType ?? 'application/octet-stream',
data: bytes);
}
// save Message to Firestore
messagesRef.set(Message(
time: DateTime.now(),
sender: Sender.user,
message: prompt,
attachment: attachment)
.toMap());
// send to Gemini
final GenerateContentResponse response =
await _chatSession.sendMessage(Content('user', [
TextPart(prompt),
if (attachment != null)
InlineDataPart(attachment.mimeType, attachment.data)
]));
// add response to Firestore
FirebaseFirestore.instance.collection('messages').add(Message(
time: DateTime.now(),
sender: Sender.model,
message: response.text ?? 'Unable to generate response',
).toMap());
}
}
/// data classes
enum Sender {
// when user sends message to Gemini
user,
// when Gemini sends message to user
model;
static Sender fromName(String? name) {
if (name == 'user') {
return Sender.user;
} else {
return Sender.model;
}
}
}
class Message {
final String id;
final DateTime time;
final Sender sender;
final String message;
final Attachment? attachment;
Message({
required this.time,
required this.sender,
required this.message,
this.attachment,
}) : id = '';
Message.fromMap(this.id, Map<String, dynamic> map)
: time = (map['time'] as Timestamp).toDate(),
sender = Sender.fromName(map['from']),
message = map['message'],
attachment = map['attachment'] != null
? Attachment.fromMap(map['attachment'])
: null;
Map<String, dynamic> toMap() => {
'time': Timestamp.fromDate(time),
'from': sender.name,
'message': message,
'attachment': attachment?.toMap()
};
}
class Attachment {
final String file;
final String mimeType;
final Uint8List data;
Attachment({required this.file, required this.mimeType, required this.data});
Map<String, dynamic> toMap() =>
{'file': file, 'mimeType': mimeType, 'data': data.toList()};
Attachment.fromMap(Map<String, dynamic> map)
: file = map['file'],
mimeType = map['mimeType'],
data = Uint8List.fromList(map['data'] ?? []);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment