Mudança nos icones de ios.

This commit is contained in:
2026-03-16 22:24:32 +00:00
parent c5db0717c5
commit 4bd018e8f8
37 changed files with 560 additions and 140 deletions

View File

@@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application <application
android:label="app" android:label="EPVChat"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
assets/icon/icon_logos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -431,7 +431,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@@ -488,7 +488,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:async'; import 'dart:async';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import 'database_helper.dart';
class ChatScreen extends StatefulWidget { class ChatScreen extends StatefulWidget {
const ChatScreen({super.key}); const ChatScreen({super.key});
@@ -12,17 +13,15 @@ class ChatScreen extends StatefulWidget {
State<ChatScreen> createState() => _ChatScreenState(); State<ChatScreen> createState() => _ChatScreenState();
} }
class ChatMessage {
final String text;
final bool isAssistant;
ChatMessage({required this.text, this.isAssistant = false});
}
class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin { class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
final List<ChatMessage> _messages = []; final List<ChatMessage> _messages = [];
final List<ChatSession> _sessions = [];
ChatSession? _currentSession;
final TextEditingController _textController = TextEditingController(); final TextEditingController _textController = TextEditingController();
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool _isTyping = false; bool _isTyping = false;
late AnimationController _typingController; late AnimationController _typingController;
@@ -34,26 +33,62 @@ class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
duration: const Duration(milliseconds: 1200), duration: const Duration(milliseconds: 1200),
)..repeat(); )..repeat();
Timer(const Duration(seconds: 2), () => _checkAvailableModels()); _loadInitialData();
} }
Future<void> _checkAvailableModels() async { Future<void> _loadInitialData() async {
try { await _loadSessions();
final response = await http.get( if (_sessions.isNotEmpty) {
Uri.parse('http://89.114.196.110:11434/api/tags'), await _selectSession(_sessions.first);
).timeout(const Duration(seconds: 15)); } else {
await _createNewSession();
}
}
if (response.statusCode == 200) { Future<void> _loadSessions() async {
final data = jsonDecode(response.body); final sessions = await DatabaseHelper.instance.getSessions();
print("--- MODELOS DISPONÍVEIS ---"); if (mounted) {
if (data['models'] != null) { setState(() {
for (var m in data['models']) { _sessions.clear();
print("- ${m['name']}"); _sessions.addAll(sessions);
} });
} }
}
Future<void> _selectSession(ChatSession session) async {
final messages = await DatabaseHelper.instance.getMessages(session.id!);
if (mounted) {
setState(() {
_currentSession = session;
_messages.clear();
_messages.addAll(messages.reversed);
_isTyping = false;
});
}
if (_scaffoldKey.currentState?.isDrawerOpen ?? false) {
Navigator.pop(context);
}
}
Future<void> _createNewSession() async {
final title = "Chat ${DateTime.now().hour}:${DateTime.now().minute}";
final id = await DatabaseHelper.instance.createSession(title);
await _loadSessions();
final newSession = _sessions.firstWhere((s) => s.id == id);
await _selectSession(newSession);
}
Future<void> _deleteSession(int id) async {
await DatabaseHelper.instance.deleteSession(id);
await _loadSessions();
if (_currentSession?.id == id) {
if (_sessions.isNotEmpty) {
await _selectSession(_sessions.first);
} else {
await _createNewSession();
} }
} catch (e) { } else {
print("Erro ao listar modelos: $e"); setState(() {});
} }
} }
@@ -65,61 +100,76 @@ class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
super.dispose(); super.dispose();
} }
void _clearChat() {
setState(() {
_messages.clear();
_isTyping = false;
});
}
Future<void> _handleSubmitted(String text) async { Future<void> _handleSubmitted(String text) async {
if (text.trim().isEmpty || _isTyping) return; if (text.trim().isEmpty || _isTyping) return;
// Se por algum motivo não houver sessão, cria uma antes de enviar
if (_currentSession == null) {
await _createNewSession();
}
final userMsgText = text.trim();
_textController.clear(); _textController.clear();
final userMsg = ChatMessage(
sessionId: _currentSession!.id!,
text: userMsgText,
isAssistant: false,
timestamp: DateTime.now().toIso8601String(),
);
await DatabaseHelper.instance.insertMessage(userMsg);
setState(() { setState(() {
_messages.insert(0, ChatMessage(text: text)); _messages.insert(0, userMsg);
_isTyping = true; _isTyping = true;
}); });
try { try {
final url = Uri.parse('http://89.114.196.110:11434/api/chat'); final url = Uri.parse('http://89.114.196.110:11434/api/chat');
final response = await http final response = await http.post(
.post(
url, url,
headers: { headers: {'Content-Type': 'application/json'},
'Content-Type': 'application/json',
},
body: jsonEncode({ body: jsonEncode({
'model': 'qwen3:4b', 'model': 'qwen3:4b',
'messages': [{'role': 'user', 'content': text}], 'messages': [{'role': 'user', 'content': userMsgText}],
'stream': false, 'stream': false,
}), }),
) ).timeout(const Duration(seconds: 60));
.timeout(const Duration(seconds: 60));
if (response.statusCode == 200) { if (response.statusCode == 200) {
final data = jsonDecode(response.body); final data = jsonDecode(response.body);
final reply = data['message']?['content'] ?? 'Sem resposta.'; final replyText = data['message']?['content'] ?? 'Sem resposta.';
final assistantMsg = ChatMessage(
sessionId: _currentSession!.id!,
text: replyText,
isAssistant: true,
timestamp: DateTime.now().toIso8601String(),
);
await DatabaseHelper.instance.insertMessage(assistantMsg);
if (mounted) { if (mounted) {
setState(() { setState(() {
_isTyping = false; _isTyping = false;
_messages.insert(0, ChatMessage(text: reply, isAssistant: true)); _messages.insert(0, assistantMsg);
}); });
} }
} else { } else {
throw Exception('Erro HTTP ${response.statusCode}: ${response.body}'); throw Exception('Erro HTTP ${response.statusCode}');
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
setState(() { setState(() {
_isTyping = false; _isTyping = false;
_messages.insert( _messages.insert(0, ChatMessage(
0, sessionId: _currentSession!.id!,
ChatMessage(text: "Erro: $e", isAssistant: true), text: "Erro de ligação. Verifique se o servidor está online.",
); isAssistant: true,
timestamp: DateTime.now().toIso8601String(),
));
}); });
} }
} }
@@ -147,20 +197,19 @@ class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
bottomLeft: Radius.circular(isAssistant ? 4 : 20), bottomLeft: Radius.circular(isAssistant ? 4 : 20),
bottomRight: Radius.circular(isAssistant ? 20 : 4), bottomRight: Radius.circular(isAssistant ? 20 : 4),
), ),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 5, offset: const Offset(0, 2)),
],
), ),
child: isAssistant child: isAssistant
? MarkdownBody( ? MarkdownBody(
data: message.text, data: message.text,
styleSheet: MarkdownStyleSheet( styleSheet: MarkdownStyleSheet(
p: const TextStyle(color: Colors.black87, fontSize: 15, height: 1.4), p: const TextStyle(color: Colors.black87, fontSize: 15, height: 1.4),
strong: const TextStyle(color: Colors.black, fontWeight: FontWeight.bold), strong: const TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
listBullet: const TextStyle(color: Colors.black87),
), ),
) )
: Text( : Text(message.text, style: const TextStyle(color: Colors.white, fontSize: 15)),
message.text,
style: const TextStyle(color: Colors.white, fontSize: 15, height: 1.4),
),
), ),
), ),
if (!isAssistant) _buildAvatar(Icons.person), if (!isAssistant) _buildAvatar(Icons.person),
@@ -218,61 +267,47 @@ class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
} }
Widget _buildTextComposer() { Widget _buildTextComposer() {
return ClipRRect( return Container(
child: BackdropFilter( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), decoration: BoxDecoration(
child: Container( color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), border: Border(top: BorderSide(color: Colors.black.withOpacity(0.05))),
decoration: BoxDecoration( ),
color: Colors.white.withOpacity(0.8), child: SafeArea(
border: Border(top: BorderSide(color: Colors.black.withOpacity(0.05), width: 0.5)), child: Row(
), children: [
child: SafeArea( Expanded(
child: Row( child: Container(
children: [ decoration: BoxDecoration(
IconButton( color: const Color(0xFFF1F5F9),
icon: const Icon(Icons.delete_sweep_rounded, color: Colors.redAccent), borderRadius: BorderRadius.circular(30.0),
onPressed: _isTyping ? null : _clearChat,
), ),
Expanded( child: TextField(
child: Container( controller: _textController,
decoration: BoxDecoration( enabled: !_isTyping,
color: const Color(0xFFF1F5F9), onSubmitted: _handleSubmitted,
borderRadius: BorderRadius.circular(30.0), decoration: const InputDecoration(
border: Border.all(color: Colors.black.withOpacity(0.05)), hintText: "Mensagem EPVChat...",
), border: InputBorder.none,
child: TextField( contentPadding: EdgeInsets.symmetric(horizontal: 24.0, vertical: 14.0),
controller: _textController,
enabled: !_isTyping,
onSubmitted: _handleSubmitted,
decoration: InputDecoration(
hintText: _isTyping ? "Aguarde a resposta..." : "Mensagem EPVChat...",
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 14.0),
),
),
), ),
), ),
const SizedBox(width: 8), ),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: _isTyping
? [Colors.grey, Colors.grey]
: [const Color(0xFF8ad5c9), const Color(0xFF57a7ed)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
shape: BoxShape.circle,
),
child: IconButton(
icon: const Icon(Icons.send_rounded, color: Colors.white),
onPressed: _isTyping ? null : () => _handleSubmitted(_textController.text),
),
),
],
), ),
), const SizedBox(width: 12),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: _isTyping ? [Colors.grey, Colors.grey] : [const Color(0xFF8ad5c9), const Color(0xFF57a7ed)],
),
shape: BoxShape.circle,
),
child: IconButton(
icon: const Icon(Icons.send_rounded, color: Colors.white),
onPressed: _isTyping ? null : () => _handleSubmitted(_textController.text),
),
),
],
), ),
), ),
); );
@@ -283,45 +318,123 @@ class _ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
final screenWidth = MediaQuery.of(context).size.width; final screenWidth = MediaQuery.of(context).size.width;
return Scaffold( return Scaffold(
key: _scaffoldKey,
drawer: _buildSidebar(),
backgroundColor: Colors.white, backgroundColor: Colors.white,
body: Column( body: Stack(
children: [ children: [
Expanded( // Sombreado Mint
child: ListView( Positioned(
controller: _scrollController, top: -screenWidth * 0.45,
reverse: true, left: -screenWidth * 0.2,
children: [ right: -screenWidth * 0.2,
...(_isTyping ? [_buildTypingIndicator()] : []), child: Container(
..._messages.map((msg) => _buildMessage(msg)), height: screenWidth * 1.1,
Padding( decoration: BoxDecoration(
padding: const EdgeInsets.only(bottom: 50, top: 20), shape: BoxShape.circle,
child: Stack( gradient: RadialGradient(
alignment: Alignment.center, center: Alignment.center,
radius: 0.5,
colors: [const Color(0xFF8ad5c9).withOpacity(0.6), const Color(0xFF8ad5c9).withOpacity(0.0)],
stops: const [0.2, 1.0],
),
),
),
),
Column(
children: [
// Área do Topo (Menu e Logo)
SafeArea(
child: Container(
height: 100,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Container( IconButton(
height: screenWidth * 0.8, icon: const Icon(Icons.menu_rounded, color: Color(0xFF57a7ed), size: 32),
decoration: BoxDecoration( onPressed: () => _scaffoldKey.currentState?.openDrawer(),
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
const Color(0xFF8ad5c9).withOpacity(0.4),
const Color(0xFF8ad5c9).withOpacity(0.0),
],
),
),
),
Image.asset(
'assets/logo.png',
height: 170,
fit: BoxFit.contain,
), ),
Image.asset('assets/logo.png', height: 100, errorBuilder: (c,e,s) => const SizedBox(width: 100)),
const SizedBox(width: 48), // Equilíbrio para o ícone do menu
], ],
), ),
), ),
], ),
Expanded(
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.symmetric(vertical: 20),
reverse: true,
itemCount: _messages.length + (_isTyping ? 1 : 0),
itemBuilder: (context, index) {
if (_isTyping && index == 0) return _buildTypingIndicator();
int msgIndex = _isTyping ? index - 1 : index;
return _buildMessage(_messages[msgIndex]);
},
),
),
_buildTextComposer(),
],
),
],
),
);
}
Widget _buildSidebar() {
return Drawer(
width: MediaQuery.of(context).size.width * 0.75,
child: Column(
children: [
DrawerHeader(
decoration: const BoxDecoration(
gradient: LinearGradient(colors: [Color(0xFF8ad5c9), Color(0xFF57a7ed)]),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.auto_awesome, color: Colors.white, size: 40),
const SizedBox(height: 10),
const Text('Histórico de Chats', style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)),
],
),
),
),
ListTile(
leading: const Icon(Icons.add_comment_rounded, color: Color(0xFF57a7ed)),
title: const Text('Nova Conversa', style: TextStyle(fontWeight: FontWeight.bold)),
onTap: _createNewSession,
),
const Divider(),
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: _sessions.length,
itemBuilder: (context, index) {
final session = _sessions[index];
final isSelected = _currentSession?.id == session.id;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF8ad5c9).withOpacity(0.1) : Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
title: Text(session.title, maxLines: 1, overflow: TextOverflow.ellipsis),
trailing: IconButton(
icon: const Icon(Icons.delete_outline_rounded, color: Colors.redAccent),
onPressed: () => _deleteSession(session.id!),
),
onTap: () => _selectSession(session),
),
);
},
), ),
), ),
_buildTextComposer(),
], ],
), ),
); );

144
lib/database_helper.dart Normal file
View File

@@ -0,0 +1,144 @@
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class ChatSession {
final int? id;
final String title;
final String timestamp;
ChatSession({this.id, required this.title, required this.timestamp});
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'timestamp': timestamp,
};
}
factory ChatSession.fromMap(Map<String, dynamic> map) {
return ChatSession(
id: map['id'],
title: map['title'],
timestamp: map['timestamp'],
);
}
}
class ChatMessage {
final int? id;
final int sessionId;
final String text;
final bool isAssistant;
final String timestamp;
ChatMessage({
this.id,
required this.sessionId,
required this.text,
required this.isAssistant,
required this.timestamp,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'sessionId': sessionId,
'text': text,
'isAssistant': isAssistant ? 1 : 0,
'timestamp': timestamp,
};
}
factory ChatMessage.fromMap(Map<String, dynamic> map) {
return ChatMessage(
id: map['id'],
sessionId: map['sessionId'],
text: map['text'],
isAssistant: map['isAssistant'] == 1,
timestamp: map['timestamp'],
);
}
}
class DatabaseHelper {
static final DatabaseHelper instance = DatabaseHelper._init();
static Database? _database;
DatabaseHelper._init();
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB('chat.db');
return _database!;
}
Future<Database> _initDB(String filePath) async {
final dbPath = await getDatabasesPath();
final path = join(dbPath, filePath);
return await openDatabase(
path,
version: 1,
onCreate: _createDB,
);
}
Future _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
timestamp TEXT NOT NULL
)
''');
await db.execute('''
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sessionId INTEGER NOT NULL,
text TEXT NOT NULL,
isAssistant INTEGER NOT NULL,
timestamp TEXT NOT NULL,
FOREIGN KEY (sessionId) REFERENCES sessions (id) ON DELETE CASCADE
)
''');
}
// Session operations
Future<int> createSession(String title) async {
final db = await instance.database;
return await db.insert('sessions', {
'title': title,
'timestamp': DateTime.now().toIso8601String(),
});
}
Future<List<ChatSession>> getSessions() async {
final db = await instance.database;
final result = await db.query('sessions', orderBy: 'timestamp DESC');
return result.map((json) => ChatSession.fromMap(json)).toList();
}
Future<void> deleteSession(int id) async {
final db = await instance.database;
await db.delete('sessions', where: 'id = ?', whereArgs: [id]);
}
// Message operations
Future<int> insertMessage(ChatMessage message) async {
final db = await instance.database;
return await db.insert('messages', message.toMap());
}
Future<List<ChatMessage>> getMessages(int sessionId) async {
final db = await instance.database;
final result = await db.query(
'messages',
where: 'sessionId = ?',
whereArgs: [sessionId],
orderBy: 'timestamp ASC',
);
return result.map((json) => ChatMessage.fromMap(json)).toList();
}
}

View File

@@ -11,7 +11,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'EPVChat! Clone', title: 'EPVChat!',
theme: ThemeData( theme: ThemeData(
brightness: Brightness.light, brightness: Brightness.light,
scaffoldBackgroundColor: Colors.white, scaffoldBackgroundColor: Colors.white,

View File

@@ -5,6 +5,8 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import sqflite_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
} }

View File

@@ -1,6 +1,14 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
archive:
dependency: transitive
description:
name: archive
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
url: "https://pub.dev"
source: hosted
version: "4.0.9"
args: args:
dependency: transitive dependency: transitive
description: description:
@@ -33,6 +41,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.4.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
url: "https://pub.dev"
source: hosted
version: "0.4.2"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@@ -65,11 +89,27 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.3" version: "1.3.3"
ffi:
dependency: transitive
description:
name: ffi
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -107,6 +147,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.2" version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
url: "https://pub.dev"
source: hosted
version: "4.8.0"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
url: "https://pub.dev"
source: hosted
version: "4.11.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@@ -172,13 +228,45 @@ packages:
source: hosted source: hosted
version: "1.17.0" version: "1.17.0"
path: path:
dependency: transitive dependency: "direct main"
description: description:
name: path name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
posix:
dependency: transitive
description:
name: posix
sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
url: "https://pub.dev"
source: hosted
version: "6.5.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@@ -192,6 +280,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.2" version: "1.10.2"
sqflite:
dependency: "direct main"
description:
name: sqflite
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40"
url: "https://pub.dev"
source: hosted
version: "2.4.2+3"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@@ -216,6 +344,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.dev"
source: hosted
version: "3.4.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@@ -264,6 +400,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks: sdks:
dart: ">=3.11.1 <4.0.0" dart: ">=3.11.1 <4.0.0"
flutter: ">=3.22.0" flutter: ">=3.38.0"

View File

@@ -13,13 +13,22 @@ dependencies:
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
http: ^1.6.0 http: ^1.6.0
flutter_markdown: ^0.7.3 flutter_markdown: ^0.7.3
sqflite: ^2.3.0
path: ^1.9.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^6.0.0 flutter_lints: ^6.0.0
flutter_launcher_icons: ^0.13.1
flutter_icons:
android: true
ios: true
image_path: "assets/icon/icon_logos.png"
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/logo.png - assets/logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 33 KiB