home,signin,login view and ol logik
This commit is contained in:
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Flutter Debug",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main.dart",
|
||||||
|
"flutterMode": "debug"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -7,6 +7,17 @@
|
|||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
analyzer:
|
||||||
|
errors:
|
||||||
|
avoid_print: ignore
|
||||||
|
exclude:
|
||||||
|
- build/**
|
||||||
|
- android/**
|
||||||
|
- ios/**
|
||||||
|
- web/**
|
||||||
|
- windows/**
|
||||||
|
- macos/**
|
||||||
|
- linux/**
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
android:label="flutter_filament_app"
|
android:label="flutter_filament_app"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 197 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
class EnvDirectus {
|
||||||
|
static const String directusPublicEndpoint = 'https://directus.joshihomeserver.ipv64.net';
|
||||||
|
static const String directusApiTokenBearer = '6mqGYbOCQAg7UAhIubb87wXcJVHTAsv9';
|
||||||
|
static const String directusTankUsersRest = 'TankUsers';
|
||||||
|
static const String directusFilamentRest = 'Filament';
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../models/filament_model.dart';
|
||||||
|
import '../pages/login_view.dart';
|
||||||
|
import '../services/directus_service.dart';
|
||||||
|
|
||||||
|
class HomeController extends GetxController {
|
||||||
|
final identId = Get.arguments['userIdentId'] as String? ?? '';
|
||||||
|
final directusService = DirectusService();
|
||||||
|
final isLoading = false.obs;
|
||||||
|
final filamentModelList = <FilamentModel>[].obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
loadFilamentListPerUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {}
|
||||||
|
|
||||||
|
void logout() {
|
||||||
|
Get.offAllNamed(LoginPage.namedRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFilament() {}
|
||||||
|
|
||||||
|
Future<void> loadFilamentListPerUser() async {
|
||||||
|
isLoading.value = true;
|
||||||
|
if (filamentModelList.isNotEmpty) {
|
||||||
|
filamentModelList.clear();
|
||||||
|
}
|
||||||
|
if (identId != '') {
|
||||||
|
var resultList = await directusService.getListByUserId(identId);
|
||||||
|
if (resultList.isEmpty) {
|
||||||
|
print('Dokumente wurden nicht geladen: ${resultList.length}');
|
||||||
|
} else {
|
||||||
|
print('Filament List: $resultList');
|
||||||
|
filamentModelList.addAll(
|
||||||
|
resultList.map((json) => FilamentModel.fromJson(json)).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isLoading.value = false;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../helper/helper.dart' as helper;
|
||||||
|
import '../models/tank_user_model.dart';
|
||||||
|
import '../pages/home_view.dart';
|
||||||
|
import '../pages/signin_view.dart';
|
||||||
|
import '../services/directus_service.dart';
|
||||||
|
import '../helper/password_helper.dart';
|
||||||
|
|
||||||
|
class LoginController extends GetxController {
|
||||||
|
final emailController = TextEditingController();
|
||||||
|
final passwordController = TextEditingController();
|
||||||
|
final directusService = DirectusService();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
emailController.dispose();
|
||||||
|
passwordController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void newLoginDirectus() async {
|
||||||
|
var email = emailController.text.trim().toLowerCase();
|
||||||
|
var password = passwordController.text;
|
||||||
|
var result = await directusService.getTankUserList();
|
||||||
|
if (result.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Login fehlgeschlagen',
|
||||||
|
'Fehler beim Abrufen der Benutzerdaten. Bitte versuchen Sie es später erneut.',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tankUserList = result
|
||||||
|
.map((userJson) => TankUser.fromJson(userJson))
|
||||||
|
.toList();
|
||||||
|
var identId =
|
||||||
|
tankUserList
|
||||||
|
.firstWhereOrNull((user) => user.email == email)
|
||||||
|
?.userIdentId ??
|
||||||
|
'';
|
||||||
|
if (identId.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Login fehlgeschlagen',
|
||||||
|
'Benutzer mit dieser E-Mail-Adresse wurde nicht gefunden.',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var storedHash =
|
||||||
|
tankUserList
|
||||||
|
.firstWhereOrNull((user) => user.email == email)
|
||||||
|
?.passwordHash ??
|
||||||
|
'';
|
||||||
|
if (storedHash.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Login fehlgeschlagen',
|
||||||
|
'Kein gespeicherter Passwort-Hash für diesen Benutzer gefunden.',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var isPassCorrect = await PasswordHelper.verifyPassword(
|
||||||
|
password,
|
||||||
|
storedHash,
|
||||||
|
);
|
||||||
|
if (isPassCorrect && identId.isNotEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Login erfolgreich',
|
||||||
|
'Willkommen zurück!',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
print('Login erfolgreich für Benutzer mit IdentId: $identId');
|
||||||
|
helper.kCurrentUserIdentId = identId; // Set the current user identId
|
||||||
|
Get.offAllNamed(HomePage.namedRoute, arguments: {'userIdentId': identId});
|
||||||
|
} else {
|
||||||
|
Get.snackbar(
|
||||||
|
'Login fehlgeschlagen',
|
||||||
|
'Bitte überprüfen Sie Ihre E-Mail und Ihr Passwort und versuchen Sie es erneut\noder registrieren Sie sich, wenn Sie noch kein Konto haben.',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToSignInPage() {
|
||||||
|
Get.offAllNamed(SigninPage.namedRoute, arguments: {'userIdentId': ''});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../pages/home_view.dart';
|
||||||
|
import '../pages/login_view.dart';
|
||||||
|
import '../services/directus_service.dart';
|
||||||
|
|
||||||
|
class SigninController extends GetxController {
|
||||||
|
final userNameController = TextEditingController();
|
||||||
|
final emailController = TextEditingController();
|
||||||
|
final passwordController = TextEditingController();
|
||||||
|
final cityController = TextEditingController();
|
||||||
|
final countryController = TextEditingController();
|
||||||
|
final directusService = DirectusService();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
userNameController.dispose();
|
||||||
|
emailController.dispose();
|
||||||
|
passwordController.dispose();
|
||||||
|
cityController.dispose();
|
||||||
|
countryController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void register() async {
|
||||||
|
// Eingaben überprüfen
|
||||||
|
if (!_checkInputs()) return;
|
||||||
|
|
||||||
|
// Registrierungslogik
|
||||||
|
var userName = userNameController.text.trim();
|
||||||
|
var email = emailController.text.trim().toLowerCase();
|
||||||
|
var password = passwordController.text;
|
||||||
|
var city = cityController.text.trim();
|
||||||
|
var country = countryController.text.trim();
|
||||||
|
|
||||||
|
print(
|
||||||
|
'Registrieren mit Benutzername: $userName, E-Mail: $email, Stadt: $city, Land: $country',
|
||||||
|
);
|
||||||
|
|
||||||
|
bool registrationSuccess = await directusService.register(userName, email, password, city, country);
|
||||||
|
|
||||||
|
if (registrationSuccess) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Registrierung erfolgreich',
|
||||||
|
'Willkommen, $userName!',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
var userIdentId = base64Encode(utf8.encode('$email:$password'));
|
||||||
|
Get.offAllNamed(HomePage.namedRoute, arguments: {'userIdentId': userIdentId});
|
||||||
|
} else {
|
||||||
|
Get.snackbar(
|
||||||
|
'Registrierung fehlgeschlagen',
|
||||||
|
'Bitte überprüfen Sie Ihre Eingaben und versuchen Sie es erneut.',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToLoginPage() {
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
Get.offAllNamed(LoginPage.namedRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _checkInputs() {
|
||||||
|
if (userNameController.text.trim().isEmpty ||
|
||||||
|
emailController.text.trim().isEmpty ||
|
||||||
|
passwordController.text.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Fehler',
|
||||||
|
'Bitte alle erforderlichen Felder ausfüllen',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwordController.text.length < 6) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Fehler',
|
||||||
|
'Passwort muss mindestens 6 Zeichen lang sein',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
/// Retrieves the current date in the format "yyyy-MM-dd".
|
||||||
|
///
|
||||||
|
/// @return [String] A formatted date.
|
||||||
|
String getCurrentDate(DateTime date) {
|
||||||
|
return DateFormat("yyyy-MM-dd").format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
var kListDropDownYear = <int>[
|
||||||
|
for (var year = DateTime.now().year; year >= 2020; year--) year
|
||||||
|
];
|
||||||
|
|
||||||
|
var kCurrentUserIdentId = '';
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class MyHttpOverrides extends HttpOverrides {
|
||||||
|
@override
|
||||||
|
HttpClient createHttpClient(SecurityContext? context) {
|
||||||
|
return super.createHttpClient(context)
|
||||||
|
..badCertificateCallback =
|
||||||
|
(X509Certificate cert, String host, int port) => true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
|
||||||
|
class PasswordHelper {
|
||||||
|
static const int _saltLength = 16;
|
||||||
|
static const int _hashLength = 32;
|
||||||
|
|
||||||
|
// 19 MiB, laut aktueller cryptography-Doku ein sinnvoller Startwert.
|
||||||
|
static const int _memory = 19 * 1024;
|
||||||
|
static const int _iterations = 2;
|
||||||
|
static const int _parallelism = 1;
|
||||||
|
|
||||||
|
static final Argon2id _algorithm = Argon2id(
|
||||||
|
memory: _memory,
|
||||||
|
iterations: _iterations,
|
||||||
|
parallelism: _parallelism,
|
||||||
|
hashLength: _hashLength,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Erstellt einen neuen Passwort-Hash inklusive zufälligem Salt.
|
||||||
|
///
|
||||||
|
/// Rückgabeformat:
|
||||||
|
/// argon2id$memory$iterations$parallelism$saltBase64$hashBase64
|
||||||
|
static Future<String> hashPassword(String password) async {
|
||||||
|
final salt = _generateSalt(_saltLength);
|
||||||
|
|
||||||
|
final secretKey = await _algorithm.deriveKeyFromPassword(
|
||||||
|
password: password,
|
||||||
|
nonce: salt,
|
||||||
|
);
|
||||||
|
|
||||||
|
final hashBytes = await secretKey.extractBytes();
|
||||||
|
|
||||||
|
final saltBase64 = base64UrlEncode(salt);
|
||||||
|
final hashBase64 = base64UrlEncode(hashBytes);
|
||||||
|
|
||||||
|
return 'argon2id'
|
||||||
|
'\$$_memory'
|
||||||
|
'\$$_iterations'
|
||||||
|
'\$$_parallelism'
|
||||||
|
'\$$saltBase64'
|
||||||
|
'\$$hashBase64';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prüft ein Klartext-Passwort gegen den gespeicherten Hash.
|
||||||
|
static Future<bool> verifyPassword(
|
||||||
|
String password,
|
||||||
|
String storedHash,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final parts = storedHash.split('\$');
|
||||||
|
|
||||||
|
if (parts.length != 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts[0] != 'argon2id') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final memory = int.parse(parts[1]);
|
||||||
|
final iterations = int.parse(parts[2]);
|
||||||
|
final parallelism = int.parse(parts[3]);
|
||||||
|
|
||||||
|
final salt = base64Url.decode(parts[4]);
|
||||||
|
final expectedHash = base64Url.decode(parts[5]);
|
||||||
|
|
||||||
|
final algorithm = Argon2id(
|
||||||
|
memory: memory,
|
||||||
|
iterations: iterations,
|
||||||
|
parallelism: parallelism,
|
||||||
|
hashLength: expectedHash.length,
|
||||||
|
);
|
||||||
|
|
||||||
|
final secretKey = await algorithm.deriveKeyFromPassword(
|
||||||
|
password: password,
|
||||||
|
nonce: salt,
|
||||||
|
);
|
||||||
|
|
||||||
|
final calculatedHash = await secretKey.extractBytes();
|
||||||
|
|
||||||
|
return _constantTimeEquals(
|
||||||
|
calculatedHash,
|
||||||
|
expectedHash,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Erzeugt kryptographisch zufälligen Salt.
|
||||||
|
static List<int> _generateSalt(int length) {
|
||||||
|
final random = Random.secure();
|
||||||
|
|
||||||
|
return List<int>.generate(
|
||||||
|
length,
|
||||||
|
(_) => random.nextInt(256),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vergleich ohne vorzeitigen Abbruch.
|
||||||
|
static bool _constantTimeEquals(
|
||||||
|
List<int> a,
|
||||||
|
List<int> b,
|
||||||
|
) {
|
||||||
|
if (a.length != b.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var difference = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
difference |= a[i] ^ b[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return difference == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../controller/home_controller.dart';
|
||||||
|
import '../controller/login_controller.dart';
|
||||||
|
import '../controller/signin_controller.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SampleBindings extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
// Define your dependencies here no permanent Binding
|
||||||
|
Get.lazyPut<LoginController>(() => LoginController());
|
||||||
|
Get.lazyPut<SigninController>(() => SigninController());
|
||||||
|
Get.lazyPut<HomeController>(() => HomeController());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../pages/home_view.dart';
|
||||||
|
import '../pages/login_view.dart';
|
||||||
|
import '../pages/signin_view.dart';
|
||||||
|
import 'sample_bindings.dart';
|
||||||
|
|
||||||
|
class SampleRouts {
|
||||||
|
static final sampleBindings = SampleBindings();
|
||||||
|
static List<GetPage<dynamic>> samplePages = [
|
||||||
|
GetPage(
|
||||||
|
name: LoginPage.namedRoute,
|
||||||
|
page: () => const LoginPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: SigninPage.namedRoute,
|
||||||
|
page: () => const SigninPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: HomePage.namedRoute,
|
||||||
|
page: () => const HomePage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
+17
-107
@@ -1,122 +1,32 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'helper/http_override.dart';
|
||||||
|
//import 'helper/sample_bindings.dart';
|
||||||
|
import 'helper/sample_routes.dart';
|
||||||
|
import 'pages/login_view.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
HttpOverrides.global = MyHttpOverrides();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return GetMaterialApp(
|
||||||
title: 'Flutter Demo',
|
title: 'Flutter Filament',
|
||||||
theme: ThemeData(
|
debugShowCheckedModeBanner: false,
|
||||||
// This is the theme of your application.
|
theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
|
||||||
//
|
//initialBinding: SampleBindings(),
|
||||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
initialRoute: LoginPage.namedRoute,
|
||||||
// the application has a purple toolbar. Then, without quitting the app,
|
getPages: SampleRouts.samplePages,
|
||||||
// try changing the seedColor in the colorScheme below to Colors.green
|
|
||||||
// and then invoke "hot reload" (save your changes or press the "hot
|
|
||||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
|
||||||
// the command line to start the app).
|
|
||||||
//
|
|
||||||
// Notice that the counter didn't reset back to zero; the application
|
|
||||||
// state is not lost during the reload. To reset the state, use hot
|
|
||||||
// restart instead.
|
|
||||||
//
|
|
||||||
// This works for code too, not just values: Most code changes can be
|
|
||||||
// tested with just a hot reload.
|
|
||||||
colorScheme: .fromSeed(seedColor: Colors.deepPurple),
|
|
||||||
),
|
|
||||||
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
|
||||||
const MyHomePage({super.key, required this.title});
|
|
||||||
|
|
||||||
// This widget is the home page of your application. It is stateful, meaning
|
|
||||||
// that it has a State object (defined below) that contains fields that affect
|
|
||||||
// how it looks.
|
|
||||||
|
|
||||||
// This class is the configuration for the state. It holds the values (in this
|
|
||||||
// case the title) provided by the parent (in this case the App widget) and
|
|
||||||
// used by the build method of the State. Fields in a Widget subclass are
|
|
||||||
// always marked "final".
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
|
||||||
int _counter = 0;
|
|
||||||
|
|
||||||
void _incrementCounter() {
|
|
||||||
setState(() {
|
|
||||||
// This call to setState tells the Flutter framework that something has
|
|
||||||
// changed in this State, which causes it to rerun the build method below
|
|
||||||
// so that the display can reflect the updated values. If we changed
|
|
||||||
// _counter without calling setState(), then the build method would not be
|
|
||||||
// called again, and so nothing would appear to happen.
|
|
||||||
_counter++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// This method is rerun every time setState is called, for instance as done
|
|
||||||
// by the _incrementCounter method above.
|
|
||||||
//
|
|
||||||
// The Flutter framework has been optimized to make rerunning build methods
|
|
||||||
// fast, so that you can just rebuild anything that needs updating rather
|
|
||||||
// than having to individually change instances of widgets.
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
// TRY THIS: Try changing the color here to a specific color (to
|
|
||||||
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
|
|
||||||
// change color while the other colors stay the same.
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
||||||
// Here we take the value from the MyHomePage object that was created by
|
|
||||||
// the App.build method, and use it to set our appbar title.
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
// Center is a layout widget. It takes a single child and positions it
|
|
||||||
// in the middle of the parent.
|
|
||||||
child: Column(
|
|
||||||
// Column is also a layout widget. It takes a list of children and
|
|
||||||
// arranges them vertically. By default, it sizes itself to fit its
|
|
||||||
// children horizontally, and tries to be as tall as its parent.
|
|
||||||
//
|
|
||||||
// Column has various properties to control how it sizes itself and
|
|
||||||
// how it positions its children. Here we use mainAxisAlignment to
|
|
||||||
// center the children vertically; the main axis here is the vertical
|
|
||||||
// axis because Columns are vertical (the cross axis would be
|
|
||||||
// horizontal).
|
|
||||||
//
|
|
||||||
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
|
|
||||||
// action in the IDE, or press "p" in the console), to see the
|
|
||||||
// wireframe for each widget.
|
|
||||||
mainAxisAlignment: .center,
|
|
||||||
children: [
|
|
||||||
const Text('You have pushed the button this many times:'),
|
|
||||||
Text(
|
|
||||||
'$_counter',
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: _incrementCounter,
|
|
||||||
tooltip: 'Increment',
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
class FilamentModel {
|
||||||
|
int? iD;
|
||||||
|
String? userIdentId;
|
||||||
|
String? art;
|
||||||
|
String? color;
|
||||||
|
String? duckTemperatur;
|
||||||
|
String? menge;
|
||||||
|
String? nozzelTemperatur;
|
||||||
|
String? bettTemperatur;
|
||||||
|
String? restMengeAktuelleRolleGramm;
|
||||||
|
String? updatedAt;
|
||||||
|
String? createdAt;
|
||||||
|
|
||||||
|
FilamentModel(
|
||||||
|
{this.iD,
|
||||||
|
this.userIdentId,
|
||||||
|
this.art,
|
||||||
|
this.color,
|
||||||
|
this.duckTemperatur,
|
||||||
|
this.menge,
|
||||||
|
this.nozzelTemperatur,
|
||||||
|
this.bettTemperatur,
|
||||||
|
this.restMengeAktuelleRolleGramm,
|
||||||
|
this.updatedAt,
|
||||||
|
this.createdAt});
|
||||||
|
|
||||||
|
FilamentModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
iD = json['ID'];
|
||||||
|
userIdentId = json['UserIdentId'];
|
||||||
|
art = json['Art'];
|
||||||
|
color = json['Color'];
|
||||||
|
duckTemperatur = json['DuckTemperatur'];
|
||||||
|
menge = json['Menge'];
|
||||||
|
nozzelTemperatur = json['NozzelTemperatur'];
|
||||||
|
bettTemperatur = json['BettTemperatur'];
|
||||||
|
restMengeAktuelleRolleGramm = json['RestMengeAktuelleRolleGramm'];
|
||||||
|
updatedAt = json['UpdatedAt'];
|
||||||
|
createdAt = json['CreatedAt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['ID'] = iD;
|
||||||
|
data['UserIdentId'] = userIdentId;
|
||||||
|
data['Art'] = art;
|
||||||
|
data['Color'] = color;
|
||||||
|
data['DuckTemperatur'] = duckTemperatur;
|
||||||
|
data['Menge'] = menge;
|
||||||
|
data['NozzelTemperatur'] = nozzelTemperatur;
|
||||||
|
data['BettTemperatur'] = bettTemperatur;
|
||||||
|
data['RestMengeAktuelleRolleGramm'] = restMengeAktuelleRolleGramm;
|
||||||
|
data['UpdatedAt'] = updatedAt;
|
||||||
|
data['CreatedAt'] = createdAt;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
class TankUser {
|
||||||
|
int? iD;
|
||||||
|
String? userIdentId;
|
||||||
|
String? userName;
|
||||||
|
String? city;
|
||||||
|
String? country;
|
||||||
|
String? email;
|
||||||
|
String? passwordHash;
|
||||||
|
String? createdAt;
|
||||||
|
|
||||||
|
TankUser(
|
||||||
|
{this.iD,
|
||||||
|
this.userIdentId,
|
||||||
|
this.userName,
|
||||||
|
this.city,
|
||||||
|
this.country,
|
||||||
|
this.email,
|
||||||
|
this.passwordHash,
|
||||||
|
this.createdAt});
|
||||||
|
|
||||||
|
TankUser.fromJson(Map<String, dynamic> json) {
|
||||||
|
iD = json['ID'];
|
||||||
|
userIdentId = json['UserIdentId'];
|
||||||
|
userName = json['UserName'];
|
||||||
|
city = json['City'];
|
||||||
|
country = json['Country'];
|
||||||
|
email = json['Email'];
|
||||||
|
passwordHash = json['PasswordHash'];
|
||||||
|
createdAt = json['CreatedAt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['ID'] = iD;
|
||||||
|
data['UserIdentId'] = userIdentId;
|
||||||
|
data['UserName'] = userName;
|
||||||
|
data['City'] = city;
|
||||||
|
data['Country'] = country;
|
||||||
|
data['Email'] = email;
|
||||||
|
data['PasswordHash'] = passwordHash;
|
||||||
|
data['CreatedAt'] = createdAt;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../controller/home_controller.dart';
|
||||||
|
import '../widgets/filament_item_widget.dart';
|
||||||
|
|
||||||
|
class HomePage extends GetView<HomeController> {
|
||||||
|
static const String namedRoute = '/home-page';
|
||||||
|
const HomePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var hCtrl = controller;
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade50,
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.teal.shade400,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
title: const Text(
|
||||||
|
'Filament Lager',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.logout),
|
||||||
|
onPressed: hCtrl.logout,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
backgroundColor: Colors.teal.shade400.withValues(alpha: 0.4),
|
||||||
|
foregroundColor: Colors.grey.shade200,
|
||||||
|
onPressed: hCtrl.addFilament,
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: const Text('Filament'),
|
||||||
|
),
|
||||||
|
body: Obx(() {
|
||||||
|
if (hCtrl.isLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hCtrl.filamentModelList.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.inventory_2_outlined, size: 64, color: Colors.grey.shade400),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Keine Filamente vorhanden',
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.grey.shade600),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: hCtrl.loadFilamentListPerUser,
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.only(top: 8, bottom: 80),
|
||||||
|
itemCount: hCtrl.filamentModelList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final filament = hCtrl.filamentModelList[index];
|
||||||
|
return FilamentItemWidget(
|
||||||
|
filament: filament,
|
||||||
|
onTap: () {
|
||||||
|
// Optionale Detailansicht oder Bearbeitung
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../controller/login_controller.dart';
|
||||||
|
import '../widgets/my_login_widget.dart';
|
||||||
|
|
||||||
|
class LoginPage extends GetView<LoginController> {
|
||||||
|
static const String namedRoute = '/login-page';
|
||||||
|
const LoginPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var logCtrl = controller;
|
||||||
|
var displayWidth = MediaQuery.of(context).size.width;
|
||||||
|
var displayHeight = MediaQuery.of(context).size.height;
|
||||||
|
return PopScope(
|
||||||
|
canPop: false, // Verhindert das Zurücknavigieren über die System-Geste / den Back-Button
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
body: Container(
|
||||||
|
height: displayHeight,
|
||||||
|
width: displayWidth,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 50.0,
|
||||||
|
),
|
||||||
|
color: Colors.blue.shade100,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: MyLoginWidget(
|
||||||
|
onButtonPressed: () => logCtrl.newLoginDirectus(),
|
||||||
|
signInOnTab: () => logCtrl.goToSignInPage(),
|
||||||
|
logCtrl: logCtrl,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../controller/signin_controller.dart';
|
||||||
|
import '../widgets/my_signin_widget.dart';
|
||||||
|
|
||||||
|
class SigninPage extends GetView<SigninController> {
|
||||||
|
static const String namedRoute = '/signin-page';
|
||||||
|
const SigninPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var sigCtrl = controller;
|
||||||
|
var displayWidth = MediaQuery.of(context).size.width;
|
||||||
|
var displayHeight = MediaQuery.of(context).size.height;
|
||||||
|
return SafeArea(
|
||||||
|
child: PopScope(
|
||||||
|
canPop: false, // Verhindert das Zurücknavigieren über die System-Geste / den Back-Button
|
||||||
|
child: Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
body: Container(
|
||||||
|
height: displayHeight,
|
||||||
|
width: displayWidth,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 90.0,
|
||||||
|
),
|
||||||
|
color: Colors.blue.shade100,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: MySigninWidget(
|
||||||
|
signCtrl: sigCtrl,
|
||||||
|
onButtonPressed: () => sigCtrl.register(),
|
||||||
|
logInOnTab: () => sigCtrl.goToLoginPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../config/env_directus.dart';
|
||||||
|
import '../helper/password_helper.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class DirectusService {
|
||||||
|
static final String endpoint = EnvDirectus.directusPublicEndpoint;
|
||||||
|
static final String apiTokenBearer = EnvDirectus.directusApiTokenBearer;
|
||||||
|
static final String tankUsersRest = EnvDirectus.directusTankUsersRest;
|
||||||
|
static final String filamentRest = EnvDirectus.directusFilamentRest;
|
||||||
|
|
||||||
|
static Map<String, String> get headers => {
|
||||||
|
'Authorization': 'Bearer $apiTokenBearer',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
Future<List<dynamic>> getTankUserList() async {
|
||||||
|
var client = http.Client();
|
||||||
|
String apiUrl = '$endpoint/items/$tankUsersRest';
|
||||||
|
var response = await client.get(Uri.parse(apiUrl), headers: headers);
|
||||||
|
if(response.statusCode != 200) {
|
||||||
|
print('Fehler beim Abrufen der TankUser-Liste: ${response.statusCode}');
|
||||||
|
client.close();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
final json = jsonDecode(
|
||||||
|
utf8.decode(response.bodyBytes),
|
||||||
|
) as Map<String, dynamic>;
|
||||||
|
//print('$json}');
|
||||||
|
client.close();
|
||||||
|
return json['data'] as List<dynamic>;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<dynamic>> getListByUserId(String identId) async {
|
||||||
|
var client = http.Client();
|
||||||
|
//String apiUrl = '$endpoint/items/$filamentRest?filter[UserIdentId][_eq]=$userIdentId';
|
||||||
|
String apiUrl = '$endpoint/items/$filamentRest?filter[UserIdentId][_eq]=$identId';
|
||||||
|
var response = await client.get(Uri.parse(apiUrl), headers: headers);
|
||||||
|
if(response.statusCode != 200) {
|
||||||
|
print('Fehler beim Abrufen der Dokumente aus der Collection: ${response.statusCode}');
|
||||||
|
client.close();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
final json = jsonDecode(
|
||||||
|
utf8.decode(response.bodyBytes),
|
||||||
|
) as Map<String, dynamic>;
|
||||||
|
//print('$json}');
|
||||||
|
client.close();
|
||||||
|
return json['data'] as List<dynamic>;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteDocumentFromTable(int iD) async {
|
||||||
|
var client = http.Client();
|
||||||
|
String apiUrl = '$endpoint/items/$filamentRest/$iD';
|
||||||
|
var response = await client.delete(Uri.parse(apiUrl), headers: headers);
|
||||||
|
if(response.statusCode < 200 || response.statusCode >= 300) {
|
||||||
|
print('Fehler beim Löschen des Dokuments aus der Collection: ${response.statusCode}');
|
||||||
|
client.close();
|
||||||
|
throw Exception('Fehler beim Löschen des Dokuments aus der Collection: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> createDocumentInTable(Map<String, String> data) async {
|
||||||
|
return await http.post(
|
||||||
|
Uri.parse('$endpoint/items/$filamentRest'),
|
||||||
|
headers: headers,
|
||||||
|
body: jsonEncode(data),
|
||||||
|
).then((response) {
|
||||||
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||||
|
print('Dokument erfolgreich erstellt: ${response.body}');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print('Fehler beim Erstellen des Dokuments: ${response.statusCode} - ${response.body}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}).catchError((error) {
|
||||||
|
print('Fehler beim Erstellen des Dokuments: $error');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> updateDocumentInTable(int? iD, Map<String, String> data) async {
|
||||||
|
return iD != null
|
||||||
|
? http.patch(
|
||||||
|
Uri.parse('$endpoint/items/$filamentRest/$iD'),
|
||||||
|
headers: headers,
|
||||||
|
body: jsonEncode(data),
|
||||||
|
).then((response) {
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('Dokument erfolgreich aktualisiert: ${response.body}');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print('Fehler beim Aktualisieren des Dokuments: ${response.statusCode} - ${response.body}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}).catchError((error) {
|
||||||
|
print('Fehler beim Aktualisieren des Dokuments: $error');
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
: Future.value(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> register(String userName, String email, String password, String city, String country) async {
|
||||||
|
var client = http.Client();
|
||||||
|
String apiUrl = '$endpoint/items/$tankUsersRest';
|
||||||
|
Map<String, String> data = {
|
||||||
|
'UserIdentId': base64Encode(utf8.encode('$email:$password')),
|
||||||
|
'UserName': userName,
|
||||||
|
'City': city,
|
||||||
|
'Country': country,
|
||||||
|
'Email': email,
|
||||||
|
'PasswordHash': await PasswordHelper.hashPassword(password),
|
||||||
|
};
|
||||||
|
var response = await client.post(
|
||||||
|
Uri.parse(apiUrl),
|
||||||
|
headers: headers,
|
||||||
|
body: jsonEncode(data),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||||
|
print('Benutzer erfolgreich registriert: ${response.body}');
|
||||||
|
client.close();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print('Fehler bei der Registrierung des Benutzers: ${response.statusCode} - ${response.body}');
|
||||||
|
client.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../models/filament_model.dart';
|
||||||
|
|
||||||
|
class FilamentItemWidget extends StatelessWidget {
|
||||||
|
final FilamentModel filament;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
const FilamentItemWidget({
|
||||||
|
super.key,
|
||||||
|
required this.filament,
|
||||||
|
this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hilfsmethode zur Bestimmung der Farbe basierend auf dem String "Color"
|
||||||
|
Color _parseColor(String colorName) {
|
||||||
|
switch (colorName.toLowerCase().trim()) {
|
||||||
|
case 'weiß':
|
||||||
|
return Colors.white;
|
||||||
|
case 'schwarz':
|
||||||
|
return Colors.black;
|
||||||
|
case 'hell braun':
|
||||||
|
case 'braun':
|
||||||
|
return Colors.brown.shade400;
|
||||||
|
case 'knochenfarbe':
|
||||||
|
return const Color(0xFFE3DAC9); // Bone / Beige
|
||||||
|
default:
|
||||||
|
return Colors.grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsmethode zur Umwandlung von "1kg", "7kg" etc. in Gramm
|
||||||
|
double _parseTotalWeightInGramm(String mengeStr) {
|
||||||
|
final cleaned = mengeStr.toLowerCase().replaceAll('kg', '').trim();
|
||||||
|
final kg = double.tryParse(cleaned) ?? 1.0;
|
||||||
|
return kg * 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final parsedColor = _parseColor(filament.color!);
|
||||||
|
final restGramm = double.tryParse(filament.restMengeAktuelleRolleGramm!) ?? 0.0;
|
||||||
|
final totalGramm = _parseTotalWeightInGramm(filament.menge!);
|
||||||
|
final progress = (restGramm / totalGramm).clamp(0.0, 1.0);
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: BorderSide(color: Colors.grey.shade200),
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
onTap: onTap,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Header: Typ, Farbindikator, Spule/Menge
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
// Farbpunkte mit Schatten für weiße/helle Farben
|
||||||
|
Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: parsedColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: Colors.grey.shade300, width: 1),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: parsedColor.withValues(alpha: 0.4),
|
||||||
|
blurRadius: 6,
|
||||||
|
spreadRadius: 1,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
// Material & Farbe
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
filament.art!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
filament.color!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Spulengröße Badge
|
||||||
|
Chip(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
labelStyle: const TextStyle(fontSize: 12),
|
||||||
|
label: Text(filament.menge!),
|
||||||
|
backgroundColor: Colors.teal.shade50,
|
||||||
|
side: BorderSide.none,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Temperaturen (Nozzle & Bed)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
_buildIconText(
|
||||||
|
icon: Icons.thermostat,
|
||||||
|
label: 'Nozzle',
|
||||||
|
value: filament.nozzelTemperatur!,
|
||||||
|
color: Colors.orange.shade700,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 24),
|
||||||
|
_buildIconText(
|
||||||
|
icon: Icons.hot_tub,
|
||||||
|
label: 'Bett',
|
||||||
|
value: filament.bettTemperatur!,
|
||||||
|
color: Colors.blue.shade700,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Restmengen Progress Indicator
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Restmenge',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${restGramm.toInt()}g / ${totalGramm.toInt()}g',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value: progress,
|
||||||
|
minHeight: 6,
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
color: progress < 0.2 ? Colors.red : Colors.teal.shade400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIconText({
|
||||||
|
required IconData icon,
|
||||||
|
required String label,
|
||||||
|
required String value,
|
||||||
|
required Color color,
|
||||||
|
}) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Icon(icon, size: 18, color: color),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(fontSize: 10, color: Colors.grey.shade600),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart' show GoogleFonts;
|
||||||
|
import '../controller/login_controller.dart';
|
||||||
|
|
||||||
|
class MyLoginWidget extends StatelessWidget {
|
||||||
|
final void Function()? onButtonPressed;
|
||||||
|
final void Function()? signInOnTab;
|
||||||
|
final LoginController logCtrl;
|
||||||
|
|
||||||
|
const MyLoginWidget({
|
||||||
|
super.key,
|
||||||
|
this.onButtonPressed,
|
||||||
|
this.signInOnTab,
|
||||||
|
required this.logCtrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final googleFont = GoogleFonts.righteous().fontFamily;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
// Umriss (Outline)
|
||||||
|
Text(
|
||||||
|
"Login Page",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 44,
|
||||||
|
fontFamily: googleFont,
|
||||||
|
fontStyle: FontStyle.normal,
|
||||||
|
letterSpacing: 5.0,
|
||||||
|
foreground: Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 5
|
||||||
|
..color = Colors.blue.shade900,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Füllung
|
||||||
|
Text(
|
||||||
|
"Login Page",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 44,
|
||||||
|
fontFamily: googleFont,
|
||||||
|
fontStyle: FontStyle.normal,
|
||||||
|
color: Colors.orange.shade300,
|
||||||
|
letterSpacing: 5.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.orange.shade400, width: 8),
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(18),
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/filamentTransparent.png',
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: TextField(
|
||||||
|
controller: logCtrl.emailController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'E-Mail',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: TextField(
|
||||||
|
obscureText: true,
|
||||||
|
controller: logCtrl.passwordController,
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Password',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 30),
|
||||||
|
ElevatedButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStatePropertyAll<Color>(
|
||||||
|
Colors.orange.shade500,
|
||||||
|
),
|
||||||
|
foregroundColor: WidgetStatePropertyAll<Color>(
|
||||||
|
Colors.blue.shade900,
|
||||||
|
),
|
||||||
|
minimumSize: WidgetStatePropertyAll<Size>(Size(430, 60)),
|
||||||
|
),
|
||||||
|
onPressed: onButtonPressed,
|
||||||
|
child: Text(
|
||||||
|
'Login',
|
||||||
|
style: TextStyle(fontSize: 34, fontFamily: googleFont),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.black),
|
||||||
|
children: [
|
||||||
|
TextSpan(text: 'Zum registrieren '),
|
||||||
|
WidgetSpan(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: signInOnTab,
|
||||||
|
child: Text(
|
||||||
|
'Sign in',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.blue.shade700,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: ' clicken!!'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart' show GoogleFonts;
|
||||||
|
import '../controller/signin_controller.dart';
|
||||||
|
|
||||||
|
class MySigninWidget extends StatelessWidget {
|
||||||
|
final void Function()? onButtonPressed;
|
||||||
|
final void Function()? logInOnTab;
|
||||||
|
final SigninController signCtrl;
|
||||||
|
|
||||||
|
const MySigninWidget({
|
||||||
|
super.key,
|
||||||
|
this.onButtonPressed,
|
||||||
|
this.logInOnTab,
|
||||||
|
required this.signCtrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final googleFont = GoogleFonts.righteous().fontFamily;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
// Umriss (Outline)
|
||||||
|
Text(
|
||||||
|
"Signin Page",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 44,
|
||||||
|
fontFamily: googleFont,
|
||||||
|
fontStyle: FontStyle.normal,
|
||||||
|
letterSpacing: 5.0,
|
||||||
|
foreground: Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 5
|
||||||
|
..color = Colors.blue.shade900,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Füllung
|
||||||
|
Text(
|
||||||
|
"Signin Page",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 44,
|
||||||
|
fontFamily: googleFont,
|
||||||
|
fontStyle: FontStyle.normal,
|
||||||
|
color: Colors.orange.shade300,
|
||||||
|
letterSpacing: 5.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.orange.shade400, width: 8),
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: TextField(
|
||||||
|
controller: signCtrl.userNameController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Username',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: TextField(
|
||||||
|
controller: signCtrl.emailController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Email',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: TextField(
|
||||||
|
obscureText: true,
|
||||||
|
controller: signCtrl.passwordController,
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Password',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: TextField(
|
||||||
|
controller: signCtrl.cityController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'City',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: TextField(
|
||||||
|
controller: signCtrl.countryController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
labelText: 'Country',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 30),
|
||||||
|
ElevatedButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStatePropertyAll<Color>(
|
||||||
|
Colors.orange.shade500,
|
||||||
|
),
|
||||||
|
foregroundColor: WidgetStatePropertyAll<Color>(
|
||||||
|
Colors.blue.shade900,
|
||||||
|
),
|
||||||
|
minimumSize: WidgetStatePropertyAll<Size>(Size(430, 60)),
|
||||||
|
),
|
||||||
|
onPressed: onButtonPressed,
|
||||||
|
child: Text(
|
||||||
|
'Register',
|
||||||
|
style: TextStyle(fontSize: 34, fontFamily: googleFont),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.black),
|
||||||
|
children: [
|
||||||
|
TextSpan(text: 'Zum login '),
|
||||||
|
WidgetSpan(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: logInOnTab,
|
||||||
|
child: Text(
|
||||||
|
'Log in',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.blue.shade700,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: ' clicken!!'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|||||||
+285
-16
@@ -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:
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -29,10 +37,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: clock
|
name: clock
|
||||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
sha256: e51d50bca3217c9a9fa2b41a30e4a38971133f5f9ec7a3d57bae095007f1d28e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.1.3"
|
||||||
|
code_assets:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: code_assets
|
||||||
|
sha256: "828110d598123b5ea96c00c9f3c72105bf79f8ee36c20a39b26209ade421ec57"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -41,14 +57,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.1"
|
version: "1.19.1"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.7"
|
||||||
|
cryptography:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cryptography
|
||||||
|
sha256: "3eda3029d34ec9095a27a198ac9785630fe525c0eb6a49f3d575272f8e792ef0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.9.0"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
sha256: b7cc2390aac296c083bea762859413eb1cf5b630c70e0c7e4d831018f762a8b2
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.9"
|
version: "2.0.0"
|
||||||
|
cupertino_ui:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cupertino_ui
|
||||||
|
sha256: c23816577a1280cff409e989541767887011f8f1d8c1863a794ed308af46f2a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -57,6 +97,14 @@ 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
|
||||||
@@ -70,11 +118,88 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.0.0"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
get:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: get
|
||||||
|
sha256: "5ed34a7925b85336e15d472cc4cfe7d9ebf4ab8e8b9f688585bf6b50f4c3d79a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.7.3"
|
||||||
|
google_fonts:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: google_fonts
|
||||||
|
sha256: fdbbfbbb1a62f376ab29fa9627561d389751fb984da0b630ce3326e272911c8a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.0"
|
||||||
|
hooks:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: hooks
|
||||||
|
sha256: eaac480a35ec0814146c2c48d96aaa829e0e44a7662c88ae84c9edf4bc35651f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
http:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.2"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.20.3"
|
||||||
|
jni:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni
|
||||||
|
sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
jni_flutter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni_flutter
|
||||||
|
sha256: b2310cdd4c18c65c081ab141a41efa94aa26c65431803703ece51996f174f351
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
jni_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni_util
|
||||||
|
sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -107,14 +232,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "6.1.0"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.19"
|
version: "0.12.20"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -123,14 +256,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.0"
|
version: "0.13.0"
|
||||||
|
material_ui:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_ui
|
||||||
|
sha256: "32d98f11cf5942a095a8c3e01b03e4de3b17941437713dcf36b1088fb62e23d2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
|
sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.19.0"
|
||||||
|
objective_c:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: objective_c
|
||||||
|
sha256: ad56fd53a78ff6b1472fa59ff2a4e8b8ccabafc586fc263a1dfad0b99b5553e3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.6.0"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
sha256: ffcf4cf3d6c0b74ac43708d9f56625506e8a68aa935abe9d267a7330f320eb5d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -139,6 +296,86 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.6"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.0"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: a36d119c13416516a7b5913fbe8af8531e11633d784c550b2125f76c758524ec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.0"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.8"
|
||||||
|
pub_semver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
sha256: "261236774e8b1d69cfc6b9eabbc96c40f25e7a2d6b171f3385d4f65d5734fb24"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
record_use:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_use
|
||||||
|
sha256: "1cb8564af8d43b464294411db9217f5ec04891c6f22ee2c32d73ae05e88a6bd2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -156,10 +393,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
sha256: "277654b3034d17ac6f9f1cb5595db011b1d5d41e8806866db28e0abaa101c490"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.12.1"
|
version: "1.12.2"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -188,18 +425,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
|
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.11"
|
version: "0.7.12"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.4.3"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -208,6 +453,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.3.0"
|
version: "15.3.0"
|
||||||
|
web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web
|
||||||
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
sha256: f67cdd8e07d3c6329146aaef1ba043542b3134c12489f553ca9a7435d1068aea
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.12.2 <4.0.0"
|
dart: ">=3.13.0 <4.0.0"
|
||||||
flutter: ">=3.18.0-18.0.pre.54"
|
flutter: ">=3.47.0"
|
||||||
|
|||||||
+9
-4
@@ -33,7 +33,12 @@ dependencies:
|
|||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^2.0.0
|
||||||
|
cryptography: ^2.9.0
|
||||||
|
get: ^4.7.3
|
||||||
|
google_fonts: ^9.0.0
|
||||||
|
http: ^1.6.0
|
||||||
|
intl: ^0.20.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -58,9 +63,9 @@ flutter:
|
|||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
assets:
|
||||||
# - images/a_dot_burr.jpeg
|
- assets/images/filamentTransparent.png
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/to/resolution-aware-images
|
# https://flutter.dev/to/resolution-aware-images
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|||||||
Reference in New Issue
Block a user