add service for appwrite

This commit is contained in:
2026-02-24 22:38:01 +01:00
parent 487379ed0a
commit 9c8cdb3845
9 changed files with 649 additions and 67 deletions

View File

@@ -0,0 +1,12 @@
class Environment {
static const String appwritePublicEndpoint =
'https://appwrite.joshihomeserver.ipv64.net/v1';
static const String appwriteProjectId = '6894f2b0001f127bab72';
static const String appwriteProjectName = 'Flutter Projects';
static const String appwriteRealtimeCollectionId = '699e0b3d0006563a668b';
static const String appwriteDatabaseId = '68a22ef90021b90f0f43';
static const String appwriteUserEMail = 'wei@a1.net';
static const String appwritePasswd = '123456789';
//static const String locationIQKey = 'pk.dea65023dc6fed25c96902bb97fb231d';
//static const String locationIQBaseUrl = 'https://eu1.locationiq.com/v1/reverse?key=$locationIQKey&';
}

View File

@@ -1,11 +1,14 @@
import 'package:get/get.dart';
import '../models/home_model.dart';
import '../services/appwrite_service.dart';
import '../widgets/add_weight_dialog.dart';
class HomeController extends GetxController {
final isLoggedIn = false.obs;
final isloading = false.obs;
final List<WeightModel> weights = <WeightModel>[].obs;
final appwriteService = AppwriteService();
@override
void onInit() {
@@ -19,26 +22,48 @@ class HomeController extends GetxController {
@override
void onClose() {}
void _loadDataList() {
void _loadDataList() async {
isloading.value = true;
if (weights.isNotEmpty) {
weights.clear();
}
// Simulate loading data from a database or API
weights.assignAll(WeightModel.sampleData);
isLoggedIn.value = await appwriteService.login();
if (isLoggedIn.value) {
final documents = await appwriteService.getDocumentsFromCollection();
if (documents.isEmpty) {
print(
'Keine Dokumente gefunden. Stelle sicher, dass die Collection Einträge enthält.',
);
} else {
print(
'Dokumente erfolgreich geladen: ${documents.length} Einträge gefunden.',
);
weights.assignAll(
documents.map((doc) => WeightModel.fromJson(doc.data)),
);
}
} else {
print('Fehler beim Einloggen. Keine Daten geladen.');
}
isloading.value = false;
update();
}
void addWeightEntry(WeightModel entry) {
weights.add(entry);
var map = WeightModel.toMapForAppwrite(entry);
appwriteService.createDocumentInCollection(map);
update();
}
void editWeightEntry(WeightModel updated) {
final idx = weights.indexWhere((w) => w.id == updated.id);
final idx = weights.indexWhere(
(w) => w.documentId == updated.documentId && w.date == updated.date,
);
if (idx != -1) {
weights[idx] = updated;
var map = WeightModel.toMapForAppwrite(updated);
appwriteService.updateDocumentInCollection(updated.documentId, map);
update();
}
}
@@ -67,8 +92,9 @@ class HomeController extends GetxController {
// ── Dialog-Logik ─────────────────────────────────────────────────────────
Future<void> openAddDialog(String personName) async {
Future<void> openAddDialog(String personName, String userId) async {
final entry = await AddWeightDialog.show(
userId: userId,
personName: personName,
lastWeight: getLastWeight(personName),
);

View File

@@ -1,33 +1,35 @@
class WeightModel {
final String documentId;
final String name;
final double weight;
final DateTime date;
final String id;
final double weightChange;
WeightModel({
required this.weight,
required this.date,
required this.id,
required this.weightChange, required this.name,
required this.weightChange,
required this.name,
required this.documentId,
});
static WeightModel fromJson(Map<String, dynamic> json) {
return WeightModel(
weight: json['weight'],
date: DateTime.parse(json['date']),
id: json['id'],
weightChange: json['weightChange'], name: json['name'],
weightChange: json['weightChange'],
name: json['name'],
documentId: json['\$id'],
);
}
static Map<String, dynamic> toJson(WeightModel weightModel) {
static Map<String, dynamic> toMapForAppwrite(WeightModel weightModel) {
return {
'weight': weightModel.weight,
'date': weightModel.date.toIso8601String(),
'id': weightModel.id,
'weightChange': weightModel.weightChange,
'name': weightModel.name,
//'\$id': weightModel.documentId,
};
}
@@ -35,47 +37,54 @@ class WeightModel {
static final empty = WeightModel(
weight: 0.0,
date: DateTime.now(),
id: '00',
weightChange: 0.0, name: 'Joe',
weightChange: 0.0,
name: 'Joe',
documentId: '00',
);
// Sample data for testing and demonstration purposes
static List<WeightModel> sampleData = [
WeightModel(
weight: 70.0,
date: DateTime(2024, 1, 1),
id: '01',
weightChange: 0.0, name: 'Joe',
),
WeightModel(
weight: 69.5,
date: DateTime(2024, 1, 15),
id: '02',
weightChange: -0.5, name: 'Joe',
),
WeightModel(
weight: 68.0,
date: DateTime(2024, 2, 1),
id: '03',
weightChange: -1.5, name: 'Joe',
),
WeightModel(
weight: 100.0,
date: DateTime(2024, 1, 1),
id: '04',
weightChange: 0.0, name: 'Karl',
),
WeightModel(
weight: 95.5,
date: DateTime(2024, 1, 15),
id: '05',
weightChange: -4.5, name: 'Karl',
),
WeightModel(
weight: 90.0,
date: DateTime(2024, 2, 1),
id: '06',
weightChange: -5.5, name: 'Karl',
),
];
// static List<WeightModel> sampleData = [
// WeightModel(
// weight: 70.0,
// date: DateTime(2024, 1, 1),
// weightChange: 0.0,
// name: 'Joe',
// documentId: '699e0c79003da44797d9',
// ),
// WeightModel(
// weight: 69.2,
// date: DateTime(2024, 1, 15),
// weightChange: -0.5,
// name: 'Joe',
// documentId: '699e0ca5002697256161',
// ),
// WeightModel(
// weight: 68.0,
// date: DateTime(2024, 2, 1),
// weightChange: -1.5,
// name: 'Joe',
// documentId: '699e0cdc00352f911abe',
// ),
// WeightModel(
// weight: 100.0,
// date: DateTime(2024, 1, 1),
// weightChange: 0.0,
// name: 'Karl',
// documentId: '699e0cfd0014079d20b7',
// ),
// WeightModel(
// weight: 95.5,
// date: DateTime(2024, 1, 15),
// weightChange: -4.5,
// name: 'Karl',
// documentId: '699e0d2100090bef253b',
// ),
// WeightModel(
// weight: 90.0,
// date: DateTime(2024, 2, 1),
// weightChange: -5.5,
// name: 'Karl',
// documentId: '699e0d40001f669c6a15',
// ),
// ];
}

View File

@@ -79,17 +79,19 @@ class HomePage extends GetView<HomeController> {
? SliverGrid(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 600,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 0.95,
),
maxCrossAxisExtent: 600,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 0.95,
),
delegate: SliverChildBuilderDelegate(
(context, i) => PersonWeightCard(
personName: names[i],
entries: grouped[names[i]]!,
onAddWeight: () =>
homeCtrl.openAddDialog(names[i]),
onAddWeight: () => homeCtrl.openAddDialog(
names[i],
grouped[names[i]]!.first.documentId,
),
onEditEntry: (entry) =>
homeCtrl.openEditDialog(entry),
),
@@ -103,8 +105,10 @@ class HomePage extends GetView<HomeController> {
child: PersonWeightCard(
personName: names[i],
entries: grouped[names[i]]!,
onAddWeight: () =>
homeCtrl.openAddDialog(names[i]),
onAddWeight: () => homeCtrl.openAddDialog(
names[i],
grouped[names[i]]!.first.documentId,
),
onEditEntry: (entry) =>
homeCtrl.openEditDialog(entry),
),
@@ -122,5 +126,3 @@ class HomePage extends GetView<HomeController> {
);
}
}

View File

@@ -0,0 +1,155 @@
import 'package:appwrite/models.dart';
import 'package:appwrite/appwrite.dart';
import '../configs/environment.dart';
class AppwriteService {
static final String endpoint = Environment.appwritePublicEndpoint;
static final String projectId = Environment.appwriteProjectId;
static final String projectName = Environment.appwriteProjectName;
static final String realtimeCollectionId =
Environment.appwriteRealtimeCollectionId;
static final String databaseId = Environment.appwriteDatabaseId;
static final String userEMail = Environment.appwriteUserEMail;
static final String passwd = Environment.appwritePasswd;
final Client _client = Client().setProject(projectId).setEndpoint(endpoint);
// ignore: unused_field
late final Account _account;
// ignore: unused_field
late final Databases _databases;
AppwriteService._internal() {
_account = Account(_client);
_databases = Databases(_client);
}
static final AppwriteService _instance = AppwriteService._internal();
/// Singleton instance getter
factory AppwriteService() => _instance;
// login with e-Mail and password
Future<bool> login() async {
await logout();
try {
final session = await _account.createEmailPasswordSession(
email: userEMail,
password: passwd,
);
print('Login erfolgreich: ${session.$id}');
return true;
} catch (e) {
print('Login fehlgeschlagen: $e');
return false;
}
}
// logout current user
Future<bool> logout() async {
try {
await _account.deleteSession(sessionId: 'current');
print('Logout erfolgreich');
return true;
} catch (e) {
print('Logout fehlgeschlagen: $e');
return false;
}
}
//Get current user ID
Future<String?> getCurrentUserId() async {
try {
final user = await _account.get();
return user.$id;
} catch (e) {
print('Fehler beim Abrufen der Benutzer-ID: $e');
return null;
}
}
// Get List<Document> from Realtime Collection
Future<List<Document>> getDocumentsFromCollection() async {
try {
final documents = await _databases.listDocuments(
databaseId: databaseId,
collectionId: realtimeCollectionId,
queries: [Query.orderAsc('name'), Query.orderDesc('date')],
);
return documents.documents;
} catch (e) {
print('Fehler beim Abrufen der Dokumente: $e');
return [];
}
}
// Get Document per Id from Realtime Collection
Future<Document?> getDocumentById(String documentId) async {
try {
final document = await _databases.getDocument(
databaseId: databaseId,
collectionId: realtimeCollectionId,
documentId: documentId,
);
return document;
} catch (e) {
print('Fehler beim Abrufen des Dokuments: $e');
return null;
}
}
// Save a new document to Realtime Collection
Future<bool> createDocumentInCollection(Map<String, dynamic> data) async {
try {
await _databases.createDocument(
databaseId: databaseId,
collectionId: realtimeCollectionId,
documentId: ID.unique(),
data: data,
);
print('Dokument erfolgreich erstellt');
return true;
} catch (e) {
print('Fehler beim Erstellen des Dokuments: $e');
return false;
}
}
// Update an existing document in Realtime Collection
Future<bool> updateDocumentInCollection(
String documentId,
Map<String, dynamic> data,
) async {
try {
await _databases.updateDocument(
databaseId: databaseId,
collectionId: realtimeCollectionId,
documentId: documentId,
data: data,
);
print('Dokument erfolgreich aktualisiert');
return true;
} catch (e) {
print('Fehler beim Aktualisieren des Dokuments: $e');
return false;
}
}
// Delete a document from Realtime Collection
Future<bool> deleteDocumentFromCollection(String documentId) async {
try {
await _databases.deleteDocument(
databaseId: databaseId,
collectionId: realtimeCollectionId,
documentId: documentId,
);
print('Dokument erfolgreich gelöscht');
return true;
} catch (e) {
print('Fehler beim Löschen des Dokuments: $e');
return false;
}
}
}

View File

@@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
import '../models/home_model.dart';
class AddWeightDialog extends StatefulWidget {
final String userId;
final String personName;
/// Letztes bekanntes Gewicht dieser Person wird für weightChange benötigt.
@@ -16,6 +17,7 @@ class AddWeightDialog extends StatefulWidget {
const AddWeightDialog({
super.key,
required this.userId,
required this.personName,
required this.lastWeight,
this.existingEntry,
@@ -23,11 +25,16 @@ class AddWeightDialog extends StatefulWidget {
/// Öffnet den Dialog zum Hinzufügen eines neuen Eintrags.
static Future<WeightModel?> show({
required String userId,
required String personName,
required double lastWeight,
}) {
return Get.dialog<WeightModel>(
AddWeightDialog(personName: personName, lastWeight: lastWeight),
AddWeightDialog(
userId: userId,
personName: personName,
lastWeight: lastWeight,
),
barrierColor: Colors.black.withValues(alpha: 0.55),
);
}
@@ -40,6 +47,7 @@ class AddWeightDialog extends StatefulWidget {
}) {
return Get.dialog<WeightModel>(
AddWeightDialog(
userId: entry.documentId,
personName: entry.name,
lastWeight: previousWeight,
existingEntry: entry,
@@ -135,9 +143,10 @@ class _AddWeightDialogState extends State<AddWeightDialog>
final name = _nameController.text.trim();
final entry = WeightModel(
// Im Edit-Modus dieselbe ID behalten
id:
widget.existingEntry?.id ??
documentId:
widget.existingEntry?.documentId ??
DateTime.now().millisecondsSinceEpoch.toString(),
name: name,
weight: _parsedWeight!,
date: _selectedDate,