This commit is contained in:
exmKrd
2025-05-13 18:00:30 +02:00
parent e727ac641c
commit ef8d9ec59e
38 changed files with 537 additions and 142 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="nexuschat"
android:name="${applicationName}"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -428,7 +428,7 @@
isa = XCBuildConfiguration;
buildSettings = {
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_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -485,7 +485,7 @@
isa = XCBuildConfiguration;
buildSettings = {
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_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_giphy_picker/giphy_ui.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
@@ -19,7 +20,7 @@ class _ChatScreenState extends State<ChatScreen> {
final ScrollController _scrollController = ScrollController();
final FocusNode _focusNode = FocusNode();
List<Map<String, String>> messages = [];
List<Map<String, dynamic>> messages = [];
String? expediteur;
late String destinataire;
int idConversation = 0;
@@ -133,6 +134,45 @@ class _ChatScreenState extends State<ChatScreen> {
}
}
Future<Map<String, String>> _chiffrerMessage(String message) async {
final uri =
Uri.parse('https://nexuschat.derickexm.be/messages/crypt_message/')
.replace(queryParameters: {'message': message});
final response = await http.post(uri);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
print("Chiffrement → encrypted_message: ${data['encrypted_message']}");
print("Chiffrement → key: ${data['key']}");
return {
'encrypted_message': data['encrypted_message'] ?? message,
'key': data['key'] ?? ''
};
} else {
print("Erreur chiffrement: ${response.body}");
return {'encrypted_message': message, 'key': ''};
}
}
Future<String> _dechiffrerMessage(String encryptedMessage, String key) async {
final uri =
Uri.parse('https://nexuschat.derickexm.be/messages/uncrypt_message/');
final response = await http.post(
uri,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'encrypted_message': encryptedMessage,
'key': key,
}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return data['decrypted_message'] ?? encryptedMessage;
} else {
print("Erreur déchiffrement: ${response.body}");
return encryptedMessage;
}
}
Future<void> _fetchMessages() async {
if (idConversation <= 0) return;
try {
@@ -144,15 +184,24 @@ class _ChatScreenState extends State<ChatScreen> {
final jsonResponse = jsonDecode(response.body);
if (jsonResponse.containsKey('messages')) {
final List<dynamic> messagesList = jsonResponse['messages'];
setState(() {
messages = messagesList.map((msg) {
bool isMe = msg['expediteur'].toString() == expediteur;
final decryptedMessages =
await Future.wait(messagesList.map((msg) async {
final isMe = msg['expediteur'].toString() == expediteur;
final encrypted = msg['messages'].toString();
final cle = msg['key']?.toString() ?? '';
final texte = await _dechiffrerMessage(encrypted, cle);
return {
'sender': msg['expediteur'].toString(),
'text': msg['messages'].toString(),
'text': texte,
'encrypted': msg['messages'].toString(),
'timestamp': msg['sent_at'].toString(),
'key': msg['key']?.toString() ?? '',
'type': msg['type'] ?? 'text'
};
}).toList();
}));
setState(() {
messages = decryptedMessages;
_isInitialLoading = false;
});
_scrollToBottom();
@@ -163,6 +212,31 @@ class _ChatScreenState extends State<ChatScreen> {
}
}
Future<void> _supprimerMessage(Map<String, dynamic> message) async {
try {
final uri = Uri.parse(
'https://nexuschat.derickexm.be/messages/messages/delete_message/');
final response = await http.post(
uri,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'expediteur': expediteur,
'id_conversation': idConversation,
'message': message['encrypted'],
'key': message['key'],
}),
);
if (response.statusCode == 200) {
print("✅ Message supprimé");
_fetchMessages(); // refresh
} else {
print("❌ Erreur suppression: ${response.body}");
}
} catch (e) {
print("❌ Erreur _supprimerMessage: $e");
}
}
Future<void> _loadExpediteur() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? expediteurEmail =
@@ -178,6 +252,7 @@ class _ChatScreenState extends State<ChatScreen> {
setState(() {
expediteur = jsonResponse['username'];
});
print("📦 Chargement expediteur : $expediteur");
}
}
} catch (e) {
@@ -194,21 +269,41 @@ class _ChatScreenState extends State<ChatScreen> {
}
Future<void> sendMessage(String message) async {
print("🧪 Appel à sendMessage()...");
print("🔍 expediteur: $expediteur");
if (expediteur == null || message.trim().isEmpty) return;
final now = DateTime.now();
final cryptoData = await _chiffrerMessage(message.trim());
print("Résultat chiffrement : $cryptoData");
final encryptedMessage = cryptoData['encrypted_message'] ?? '';
final key = cryptoData['key'] ?? '';
// Ajout du texte en clair
final plainText = _controller.text.trim();
if (key == null || key.isEmpty) {
print('❌ Clé de chiffrement manquante. Message non envoyé.');
return;
}
setState(() {
messages.add({
'sender': expediteur!,
'text': message.trim(),
messages = List<Map<String, dynamic>>.from(messages)
..add({
'sender': expediteur ?? '',
'text': plainText,
'encrypted': encryptedMessage,
'timestamp': now.toIso8601String(),
'key': key,
'type': 'text'
});
});
_controller.clear();
_updateButtonState();
_scrollToBottom();
print("✉️ Envoi du message chiffré : $encryptedMessage");
print("🔑 Clé : $key");
try {
final response = await http.post(
Uri.parse('https://nexuschat.derickexm.be/messages/send_message/'),
@@ -216,9 +311,12 @@ class _ChatScreenState extends State<ChatScreen> {
body: jsonEncode({
'expediteur': expediteur,
'destinataire': destinataire,
'message': message.trim(),
'sent_at': now.toIso8601String(),
'message': encryptedMessage,
// Correction : horodatage en heure locale (Belgique)
'timestamp': DateTime.now().toIso8601String(),
'id_conversation': idConversation,
// Correction : forcer 'key' non vide
'key': key.isNotEmpty ? key : 'test'
}),
);
if (response.statusCode == 200) {
@@ -239,6 +337,66 @@ class _ChatScreenState extends State<ChatScreen> {
}
}
Future<void> _sendGif(String gifUrl) async {
if (expediteur == null || gifUrl.isEmpty) return;
print('📤 Préparation à lenvoi du GIF :');
print(' ↪️ URL : $gifUrl');
final nowUtcIso = DateTime.now().toIso8601String();
// Désactivation du chiffrement pour les GIFs
final encryptedMessage = gifUrl;
final key = 'test';
print(' 🔐 Encrypted : $encryptedMessage');
print(' 🔑 Key : $key');
print(' 💬 ID conversation : $idConversation');
setState(() {
messages = List<Map<String, dynamic>>.from(messages)
..add({
'sender': expediteur ?? '',
'text': gifUrl,
'encrypted': encryptedMessage,
'timestamp': nowUtcIso,
'key': key,
'type': 'gif'
});
});
_scrollToBottom();
final body = jsonEncode({
'expediteur': expediteur,
'destinataire': destinataire,
'message': encryptedMessage,
'timestamp': nowUtcIso,
'id_conversation': idConversation,
'key': key,
'type': 'gif'
});
print("📦 Corps envoyé à lAPI :");
print(" expediteur : ${expediteur}");
print(" destinataire : ${destinataire}");
print(" message : $encryptedMessage");
print(" key : $key");
print(" timestamp : $nowUtcIso");
print(" id_conversation: $idConversation");
print(" type : gif");
print("🔒 Longueur message : ${encryptedMessage.length}");
try {
final response = await http.post(
Uri.parse('https://nexuschat.derickexm.be/messages/send_message/'),
headers: {'Content-Type': 'application/json'},
body: body,
);
if (response.statusCode != 200) {
print('Erreur envoi GIF : ${response.body}');
} else {
print('✅ GIF envoyé avec succès.');
}
} catch (e) {
print('Erreur réseau envoi GIF : $e');
}
}
void _scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
@@ -255,8 +413,20 @@ class _ChatScreenState extends State<ChatScreen> {
print('Sélectionner une photo');
}
void _selectGif() {
print('Sélectionner un GIF');
Future<void> _selectGif() async {
GiphyLocale? fr;
fr ??= GiphyLocale.fromContext(context);
final config = GiphyUIConfig(
apiKey: 'qG62ngUKbr66l2jVPcDGulJW1RbZy5xI',
);
final result =
await showGiphyPicker(context, config, locale: GiphyLocale.fr);
if (result != null) {
print("GIF sélectionné : ${result.url}");
_sendGif(result.url);
}
}
@override
@@ -308,20 +478,65 @@ class _ChatScreenState extends State<ChatScreen> {
color: Colors.grey[700],
),
),
Container(
Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: GestureDetector(
onLongPress: isMe
? () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Supprimer le message ?"),
actions: [
TextButton(
child: Text("Annuler"),
onPressed: () =>
Navigator.of(context)
.pop(),
),
TextButton(
child: Text("Supprimer"),
onPressed: () {
Navigator.of(context)
.pop();
_supprimerMessage(
message);
},
),
],
);
},
);
}
: null,
child: Container(
margin: const EdgeInsets.only(top: 2),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: isMe ? Colors.orange : Colors.grey[300],
color: isMe
? Colors.orange
: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
child: Text(
child: message['type'] == 'gif'
? Image.network(message['text'] ?? '')
: Text(
message['text'] ?? '',
style: TextStyle(
color: isMe ? Colors.white : Colors.black,
color: isMe
? Colors.white
: Colors.black,
),
),
),
),
),
],
),
if (formattedTime.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 2),
@@ -339,7 +554,10 @@ class _ChatScreenState extends State<ChatScreen> {
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(
child: TextField(
@@ -371,6 +589,19 @@ class _ChatScreenState extends State<ChatScreen> {
),
),
SizedBox(width: 8),
Container(
decoration: BoxDecoration(
color: Colors.orange.shade100, // plus doux
borderRadius: BorderRadius.circular(12),
),
child: IconButton(
icon: Icon(Icons.gif_box,
color: Colors.deepOrange, size: 28),
onPressed: _selectGif,
tooltip: 'GIF',
),
),
SizedBox(width: 5),
Container(
decoration: BoxDecoration(
color: Colors.orange,
@@ -379,13 +610,26 @@ class _ChatScreenState extends State<ChatScreen> {
child: IconButton(
icon: Icon(Icons.send, color: Colors.black),
onPressed: _isButtonEnabled
? () => sendMessage(_controller.text)
? () {
sendMessage(_controller.text);
_controller.clear();
_updateButtonState();
}
: null,
),
),
SizedBox(width: 8),
],
),
Padding(
padding: const EdgeInsets.only(bottom: 4, top: 2),
child: Text(
"🔒 Messages chiffrés de bout en bout",
style: TextStyle(fontSize: 12, color: Colors.grey),
),
),
],
),
),
],
),

View File

@@ -14,10 +14,12 @@ class Contacts extends StatefulWidget {
class _ContactsState extends State<Contacts>
with SingleTickerProviderStateMixin {
List<dynamic> _contacts = [];
List<dynamic> _filteredContacts = [];
List<dynamic> _demandes = [];
List<dynamic> _users = [];
List<dynamic> _filteredUsers = [];
TextEditingController _searchController = TextEditingController();
TextEditingController _contactSearchController = TextEditingController();
String? _userEmail;
String? currentUser;
@@ -28,6 +30,7 @@ class _ContactsState extends State<Contacts>
super.initState();
_tabController = TabController(length: 3, vsync: this);
_initUser();
_contactSearchController.addListener(_filterContacts);
}
Future<void> _initUser() async {
@@ -66,7 +69,10 @@ class _ContactsState extends State<Contacts>
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
setState(() => _contacts = data);
setState(() {
_contacts = data;
_filteredContacts = data;
});
} else {
print("Erreur chargement contacts: ${response.statusCode}");
}
@@ -119,6 +125,15 @@ class _ContactsState extends State<Contacts>
});
}
void _filterContacts() {
final query = _contactSearchController.text.toLowerCase();
setState(() {
_filteredContacts = _contacts
.where((contact) => contact['contacts'].toLowerCase().contains(query))
.toList();
});
}
Future<void> _envoyerDemandeContact(String destinataire) async {
if (destinataire == currentUser) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -202,6 +217,47 @@ class _ContactsState extends State<Contacts>
}
}
Future<void> _supprimerContact(String destinataire) async {
final confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text("Confirmer la suppression"),
content: Text(
"Voulez-vous vraiment supprimer $destinataire de vos contacts ?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text("Annuler"),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("Supprimer"),
),
],
),
);
if (confirm != true) return;
try {
final response = await http.post(
Uri.parse('https://nexuschat.derickexm.be/contacts/supprimer_contact'),
headers: {"Content-Type": "application/json"},
body: jsonEncode({"owner": currentUser, "contact": destinataire}),
);
if (response.statusCode == 200) {
await _loadContacts();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Contact $destinataire supprimé")),
);
print("Status: ${response.statusCode}");
print("Body: ${response.body}");
}
} catch (e) {
print("Erreur : $e");
}
}
Future<String> _getEtatRelation(String target) async {
final response = await http.get(
Uri.parse(
@@ -234,28 +290,54 @@ class _ContactsState extends State<Contacts>
children: [
_contacts.isEmpty
? Center(child: Text("Aucun contact"))
: ListView.builder(
itemCount: _contacts.length,
: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _contactSearchController,
decoration: InputDecoration(
labelText: "Rechercher dans mes contacts...",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
),
),
),
Expanded(
child: ListView.builder(
itemCount: _filteredContacts.length,
itemBuilder: (context, index) {
final user = _contacts[index]['contacts'];
final user = _filteredContacts[index]['contacts'];
return Card(
child: ListTile(
title: Text(user),
leading: Icon(Icons.person),
trailing: ElevatedButton(
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ChatScreen(usernameExpediteur: user),
builder: (context) => ChatScreen(
usernameExpediteur: user),
),
),
child: Text("Chat"),
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () => _supprimerContact(user),
),
],
),
),
);
},
),
),
],
),
_demandes.isEmpty
? Center(child: Text("Aucune demande"))
: ListView.builder(

View File

@@ -42,6 +42,11 @@ class Inscription extends StatelessWidget {
Future<void> _API_inscription(BuildContext context) async {
const String apiUrl = "https://nexuschat.derickexm.be/users/create_user";
if (username.text.isEmpty || email.text.isEmpty || passwd.text.isEmpty) {
_showSnackBar(context, "Veuillez remplir tous les champs.");
return;
}
try {
final response = await http.post(
Uri.parse(apiUrl),

View File

@@ -271,6 +271,12 @@ class Listechatstate extends State<Listechat> {
style: const TextStyle(
fontWeight: FontWeight.bold),
),
// trailing: IconButton(
// icon: Icon(Icons.delete),
// color: Colors.red,
// onPressed: () =>
// _supprimerConversation(destinataire),
// ),
//
// subtitle: Column(
// crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -78,7 +78,6 @@ class Login extends StatelessWidget {
if (response.statusCode == 200) {
print("Connexion réussie");
// Stocker l'email de l'utilisateur
final prefs = await SharedPreferences.getInstance();
await prefs.setString('user_email', email);
@@ -99,8 +98,7 @@ class Login extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
width:
400, // Définition d'une largeur fixe pour un meilleur affichage
width: 400,
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 2),
@@ -159,6 +157,13 @@ class Login extends StatelessWidget {
child: ElevatedButton(
onPressed: () {
FocusManager.instance.primaryFocus?.unfocus();
if (email.text.isEmpty || passwd.text.isEmpty) {
_showErrorDialog(
context, "Veuillez remplir tous les champs.");
return;
}
print("Email saisi : ${email.text}");
print("Mot de passe : ${passwd.text}");
checkCredentials(email.text, passwd.text);

View File

@@ -93,28 +93,6 @@ class _MenuState extends State<Menu> {
padding: const EdgeInsets.fromLTRB(15, 10, 10, 10),
child: Column(
children: [
if (showEmailWarning)
Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.amber[200],
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: const [
Icon(Icons.warning_amber_rounded, color: Colors.black),
SizedBox(width: 10),
Expanded(
child: Text(
" Les emails de vérification sont momentanément indisponibles. Veuillez nous en excuser !",
style: TextStyle(
fontWeight: FontWeight.w600, color: Colors.red),
),
),
],
),
),
Expanded(
child: Center(
child: _widgetOptions.elementAt(_selectedIndex),

View File

@@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.6.1"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
@@ -41,6 +49,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c"
url: "https://pub.dev"
source: hosted
version: "0.3.5"
clock:
dependency: transitive
description:
@@ -110,14 +134,27 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
flutter_giphy_picker:
dependency: "direct main"
description:
name: flutter_lints
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
name: flutter_giphy_picker
sha256: "249841f086ab5b7200a2316eb2a0a773a813f696d822a856e7803f17df759e35"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "1.0.11"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: ce0e501cfc258907842238e4ca605e74b7fd1cdf04b3b43e86c43f3e40a1592c
url: "https://pub.dev"
source: hosted
version: "0.11.0"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -126,6 +163,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.26"
flutter_staggered_grid_view:
dependency: transitive
description:
name: flutter_staggered_grid_view
sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -136,14 +181,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
focus_detector_v2:
dependency: transitive
description:
name: focus_detector_v2
sha256: d4abc4c755ba894238ab92f42f6eee7ade78aa285199e112f45926c7053f90c6
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
http:
dependency: "direct main"
description:
name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "0.13.6"
version: "1.4.0"
http_parser:
dependency: transitive
description:
@@ -168,6 +221,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.19.0"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker:
dependency: transitive
description:
@@ -192,14 +253,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "5.1.1"
matcher:
dependency: transitive
description:
@@ -469,6 +522,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
visibility_detector:
dependency: transitive
description:
name: visibility_detector
sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420
url: "https://pub.dev"
source: hosted
version: "0.4.0+2"
vm_service:
dependency: transitive
description:
@@ -509,6 +570,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.5.0"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

View File

@@ -31,10 +31,13 @@ dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
http: ^0.13.6
flutter_giphy_picker: ^1.0.5
http: ^1.4.0
adaptive_theme: ^3.6.0
url_launcher: ^6.3.0
intl: ^0.19.0
@@ -47,13 +50,10 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
flutter_launcher_icons: ^0.11.0
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -97,3 +97,8 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
flutter_icons:
android: true
ios: true
image_path: "assets/logo.png"

View File

@@ -19,7 +19,7 @@
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<meta name="description" content="Un chat qui respecte votre intimité.">
<!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes">
@@ -30,7 +30,7 @@
<!-- Favicon -->
<link rel="icon" type="image/png" href="assets/assets/logo.png" />
<title>nexuschat</title>
<title>Nexuschat</title>
<link rel="manifest" href="manifest.json">
</head>