home,signin,login view and ol logik

This commit is contained in:
2026-09-30 20:31:01 +02:00
parent 63d03baac9
commit df71b15ff9
27 changed files with 1654 additions and 127 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Flutter Debug",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"flutterMode": "debug"
}
]
}
+11
View File
@@ -7,6 +7,17 @@
# The following line activates a set of recommended lints for Flutter apps,
# 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
linter:
+1
View File
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="flutter_filament_app"
android:name="${applicationName}"
Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

+6
View File
@@ -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';
}
+45
View File
@@ -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();
}
}
+90
View File
@@ -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': ''});
}
}
+91
View File
@@ -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;
}
}
+16
View File
@@ -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 = '';
+10
View File
@@ -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;
}
}
+120
View File
@@ -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;
}
}
+21
View File
@@ -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());
}
}
+26
View File
@@ -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
View File
@@ -1,122 +1,32 @@
import 'dart:io';
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() {
WidgetsFlutterBinding.ensureInitialized();
HttpOverrides.global = MyHttpOverrides();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// 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'),
return GetMaterialApp(
title: 'Flutter Filament',
debugShowCheckedModeBanner: false,
theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
//initialBinding: SampleBindings(),
initialRoute: LoginPage.namedRoute,
getPages: SampleRouts.samplePages,
);
}
}
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),
),
);
}
}
+56
View File
@@ -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;
}
}
+44
View File
@@ -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;
}
}
+78
View File
@@ -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
},
);
},
),
);
}),
);
}
}
+40
View File
@@ -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,
),
),
),
),
),
);
}
}
+41
View File
@@ -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(),
),
),
),
),
),
);
}
}
+134
View File
@@ -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;
}
}
}
+203
View File
@@ -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),
),
],
),
],
);
}
}
+134
View File
@@ -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!!'),
],
),
),
],
);
}
}
+162
View File
@@ -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!!'),
],
),
),
],
);
}
}
+1
View File
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
+285 -16
View File
@@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
@@ -29,10 +37,18 @@ packages:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
sha256: e51d50bca3217c9a9fa2b41a30e4a38971133f5f9ec7a3d57bae095007f1d28e
url: "https://pub.dev"
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:
dependency: transitive
description:
@@ -41,14 +57,38 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct main"
description:
name: cupertino_icons
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
sha256: b7cc2390aac296c083bea762859413eb1cf5b630c70e0c7e4d831018f762a8b2
url: "https://pub.dev"
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:
dependency: transitive
description:
@@ -57,6 +97,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
ffi:
dependency: transitive
description:
name: ffi
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
flutter:
dependency: "direct main"
description: flutter
@@ -70,11 +118,88 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
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:
dependency: transitive
description:
@@ -107,14 +232,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.0"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
url: "https://pub.dev"
source: hosted
version: "0.12.19"
version: "0.12.20"
material_color_utilities:
dependency: transitive
description:
@@ -123,14 +256,38 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9"
url: "https://pub.dev"
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:
dependency: transitive
description:
@@ -139,6 +296,86 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description: flutter
@@ -156,10 +393,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
sha256: "277654b3034d17ac6f9f1cb5595db011b1d5d41e8806866db28e0abaa101c490"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
version: "1.12.2"
stream_channel:
dependency: transitive
description:
@@ -188,18 +425,26 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
url: "https://pub.dev"
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:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.4.3"
vm_service:
dependency: transitive
description:
@@ -208,6 +453,30 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dart: ">=3.12.2 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
dart: ">=3.13.0 <4.0.0"
flutter: ">=3.47.0"
+9 -4
View File
@@ -33,7 +33,12 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# 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:
flutter_test:
@@ -58,9 +63,9 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- assets/images/filamentTransparent.png
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
+1
View File
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)