add login and input

This commit is contained in:
Josef Seiringer 2025-10-22 21:22:59 +02:00
parent 2f844fcaef
commit ee655328e6
9 changed files with 344 additions and 34 deletions

View File

@ -3,4 +3,5 @@ APPWRITE_PROJECT_NAME=<Your Project Name>
APPWRITE_PROJECT_ID=<Your Project Id>
APPWRITE_DATABASE_ID=<Your Database Id>
APPWRITE_COLLECTION_ID=<Your Collection id>
APPWRITE_SELF_SIGNED=<true>
PTVE_API_KEY=<Your PTV API key Geolocation Address>

25
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,25 @@
{
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "web_flutter_tank_appwrite_app",
"request": "launch",
"type": "dart"
},
{
"name": "web_flutter_tank_appwrite_app (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "web_flutter_tank_appwrite_app (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

View File

@ -0,0 +1,18 @@
import 'package:get/get.dart';
import '../controllers/input_controller.dart';
class InputBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<InputController>(() => InputController());
}
}
/// Alternative: Permanent Binding für App-weite Nutzung
class InputPermanentBinding extends Bindings {
@override
void dependencies() {
// Permanent - Controller bleibt im Speicher
Get.put<InputController>(InputController(), permanent: true);
}
}

View File

@ -0,0 +1,17 @@
import 'package:get/get.dart';
class InputController extends GetxController {
@override
void onInit() {
super.onInit();
}
@override
void onReady() {}
@override
void onClose() {}
}

View File

@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:appwrite/appwrite.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/foundation.dart';
class LoginController extends GetxController {
final _account = Rxn<account_models.Account>();
@ -12,48 +13,129 @@ class LoginController extends GetxController {
final nameController = TextEditingController();
final _endpoint = dotenv.env['APPWRITE_ENDPOINT_URL'] ?? '';
final _projectId = dotenv.env['APPWRITE_PROJECT_ID'] ?? '';
final bool _selfSigned =
(dotenv.env['APPWRITE_SELF_SIGNED'] ?? 'false').toLowerCase() == 'true';
final _logedInUser = Rxn<user_models.User>();
final formKey = GlobalKey<FormState>();
final isLogIn = false.obs;
account_models.Account? get account => _account.value;
user_models.User? get logedInUser => _logedInUser.value;
@override
@override
void onInit() {
Client client = Client()
.setEndpoint(_endpoint)
.setProject(_projectId);
Client client = Client().setEndpoint(_endpoint).setProject(_projectId);
// Optional: Unsichere/self-signed Zertifikate im Dev zulassen (nicht im Web wirksam)
if (!kIsWeb && _selfSigned) {
client.setSelfSigned(status: true);
}
_account.value = Account(client);
super.onInit();
}
Future<void> login(String email, String password) async {
await _account.value!.createEmailPasswordSession(
email: email,
password: password,
);
final user = await _account.value!.get();
_logedInUser.value = user;
emailValidator(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter your email';
}
final emailRegex = RegExp(r'^[^@]+@[^@]+\.[^@]+');
if (!emailRegex.hasMatch(value)) {
return 'Please enter a valid email address';
}
return null;
}
Future<void> register(String email, String password, String name) async {
await _account.value!.create(
userId: ID.unique(),
email: email,
password: password,
name: name,
);
await login(email, password);
passwordValidator(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
if (value.length < 6) {
return 'Password must be at least 6 characters long';
}
return null;
}
nameValidator(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter your name';
}
return null;
}
Future<void> login() async {
try {
var result = await _account.value!.createEmailPasswordSession(
email: mailController.text,
password: passwordController.text,
);
// ignore: avoid_print
print('Login result: $result');
final user = await _account.value!.get();
_logedInUser.value = user;
clearFields();
Get.snackbar(
'Erfolg',
'Anmeldung erfolgreich',
snackPosition: SnackPosition.BOTTOM,
);
} on AppwriteException catch (e) {
final msg = (e.message == null || e.message!.isEmpty)
? 'Verbindungsfehler. Prüfe Zertifikat/Endpoint.'
: e.message!;
Get.snackbar(
'Login fehlgeschlagen',
msg,
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 5),
);
} catch (e) {
Get.snackbar(
'Login fehlgeschlagen',
e.toString(),
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 5),
);
}
}
clearFields(){
mailController.clear();
passwordController.clear();
nameController.clear();
}
Future<void> register() async {
try {
await _account.value!.create(
userId: ID.unique(),
email: mailController.text,
password: passwordController.text,
name: nameController.text,
);
await login();
} on AppwriteException catch (e) {
final msg = (e.message == null || e.message!.isEmpty)
? 'Registrierung fehlgeschlagen. Prüfe Verbindung.'
: e.message!;
Get.snackbar(
'Registrierung fehlgeschlagen',
msg,
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 5),
);
} catch (e) {
Get.snackbar(
'Registrierung fehlgeschlagen',
e.toString(),
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 5),
);
}
}
Future<void> logout() async {
await _account.value!.deleteSession(sessionId: 'current');
_logedInUser.value = null;
}
await _account.value!.deleteSession(sessionId: 'current');
_logedInUser.value = null;
}
@override
void onClose() {
@ -63,3 +145,4 @@ class LoginController extends GetxController {
super.onClose();
}
}

View File

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controllers/input_controller.dart';
class InputPage extends GetView<InputController> {
const InputPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(),
);
}
}

View File

@ -1,14 +1,129 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controllers/login_controller.dart';
import '../../widgets/my_form_field.dart';
class LoginPage extends GetView<LoginController> {
const LoginPage({super.key});
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(color: Colors.red.shade300, child: Center(child: Text('Login Page')),),
@override
Widget build(BuildContext context) {
var logCtrl = controller;
var displaySize = MediaQuery.of(context).size;
return PopScope(
canPop: false,
child: SafeArea(
child: Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/gasolineGuru.jpg'),
fit: BoxFit.cover,
),
),
// Optional: Für besseren Kontrast Inhalt leicht abdunkeln
child: Container(
color: Colors.black.withValues(alpha: 0.25),
child: Center(
child: SizedBox(
width: displaySize.width * 2 / 3,
child: Form(
key: logCtrl.formKey,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.local_gas_station,
size: 100,
color: Colors.grey.shade300,
),
const SizedBox(height: 20),
_eMailForm(logCtrl),
const SizedBox(height: 20),
_passwdForm(logCtrl),
SizedBox(height: 20),
Obx(
() => !logCtrl.isLogIn.value
? SizedBox.shrink()
: _nameForm(logCtrl),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
if (logCtrl.formKey.currentState!.validate()) {
if (!logCtrl.isLogIn.value) {
await logCtrl.login();
} else {
await logCtrl.register();
}
}
},
child: Obx(
() => Text(
!logCtrl.isLogIn.value ? 'Login' : 'Register',
),
),
),
const SizedBox(height: 20),
TextButton(
onPressed: () {
logCtrl.isLogIn.value = !logCtrl.isLogIn.value;
},
child: Obx(
() => Text(
!logCtrl.isLogIn.value
? 'No account? Register'
: 'Already have an account? Login',
style: TextStyle(color: Colors.grey.shade300),
),
),
),
// Hier können Sie Ihre Login-Felder hinzufügen
],
),
),
),
),
),
),
),
),
),
);
}
}
MyFormField _passwdForm(LoginController logCtrl) {
return MyFormField(
userController: logCtrl.passwordController,
validator: (String? value) => logCtrl.passwordValidator(value) as String?,
labelText: 'Password',
filled: true,
fillColor: Colors.grey.shade300,
keyboardType: TextInputType.visiblePassword,
);
}
MyFormField _eMailForm(LoginController logCtrl) {
return MyFormField(
userController: logCtrl.mailController,
validator: (String? value) => logCtrl.emailValidator(value) as String?,
labelText: 'E-Mail',
filled: true,
fillColor: Colors.grey.shade300,
keyboardType: TextInputType.emailAddress,
);
}
_nameForm(LoginController logCtrl) {
return MyFormField(
userController: logCtrl.nameController,
validator: (String? value) => logCtrl.nameValidator(value) as String?,
labelText: 'Name',
filled: true,
fillColor: Colors.grey.shade300,
keyboardType: TextInputType.name,
);
}
}

View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
class MyFormField extends StatelessWidget {
const MyFormField({
super.key,
this.validator,
required this.labelText,
required this.filled,
required this.fillColor,
required this.keyboardType,
required this.userController,
});
final TextEditingController userController;
final String labelText;
final bool filled;
final Color fillColor;
final TextInputType keyboardType;
final String? Function(String?)? validator;
@override
Widget build(BuildContext context) {
return TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
style: const TextStyle(color: Colors.black),
controller: userController,
decoration: InputDecoration(
labelText: labelText,
filled: filled,
fillColor: fillColor,
),
keyboardType: keyboardType,
validator: validator,
obscureText: labelText == 'Password' ? true : false,
);
}
}