first commit
45
.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.build/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
.swiftpm/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins-dependencies
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
/coverage/
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
30
.metadata
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "3b62efc2a3da49882f43c372e0bc53daef7295a6"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
||||
base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
||||
- platform: web
|
||||
create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
||||
base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
16
README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# flutter_tank_web_app
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
31
analysis_options.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# 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
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
BIN
assets/images/guru.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/images/guru01.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
7
lib/config/environment.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
class Environment {
|
||||
static const String appwritePublicEndpoint = 'https://appwrite.joshihomeserver.ipv64.net/v1';
|
||||
static const String appwriteProjectId = '6894f2b0001f127bab72';
|
||||
static const String appwriteProjectName = 'Flutter Projects';
|
||||
static const String appwriteRealtimeCollectionId = '68a22f520035a95d6666';
|
||||
static const String appwriteDatabaseId = '68a22ef90021b90f0f43';
|
||||
}
|
||||
85
lib/controller/home_controller.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../models/tank_model.dart';
|
||||
import '../services/appwrite_service.dart';
|
||||
|
||||
class HomeController extends GetxController {
|
||||
final isLoading = false.obs;
|
||||
final listTankModel = <TankModel>[].obs;
|
||||
final appwriteService = AppwriteService();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
_loadListDocument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {}
|
||||
|
||||
@override
|
||||
void onClose() {}
|
||||
|
||||
Future<void> _loadListDocument() async {
|
||||
isLoading.value = true;
|
||||
if(listTankModel.isNotEmpty){
|
||||
listTankModel.clear();
|
||||
}
|
||||
var dateYear = DateTime.now().year;
|
||||
var userId = await appwriteService.getCurrentUserId();
|
||||
if (userId == null) {
|
||||
//User nicht eingeloggt, evtl. zur Login-Seite navigieren
|
||||
update();
|
||||
return;
|
||||
}
|
||||
var resultList = await appwriteService.getDocumentsFromCollection(userId);
|
||||
if (resultList.isEmpty) {
|
||||
//Dokumente erfolgreich geladen, hier können Sie die Liste verarbeiten
|
||||
print('Dokumente wurden nicht geladen: ${resultList.length}');
|
||||
} else {
|
||||
for (var doc in resultList) {
|
||||
var tankModel = TankModel.fromMap(doc.data);
|
||||
listTankModel.add(tankModel);
|
||||
}
|
||||
if (listTankModel.isNotEmpty) {
|
||||
var sortList = listTankModel.where((tank) {
|
||||
var year = (DateTime.tryParse(tank.szDate)!).year;
|
||||
return year == dateYear;
|
||||
}).toList();
|
||||
listTankModel.clear();
|
||||
listTankModel.addAll(sortList);
|
||||
listTankModel.sort((a, b) {
|
||||
var dateA = DateTime.tryParse(a.szDate)!;
|
||||
var dateB = DateTime.tryParse(b.szDate)!;
|
||||
return dateB.compareTo(dateA);
|
||||
});
|
||||
for (var tank in listTankModel) {
|
||||
print(
|
||||
'SortTankModel: ${tank.szDate} - ${tank.szLiters}L - ${tank.szPricePerLiter}€/L - Total: ${tank.szPriceTotal}€',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
var logoutSuccess = await appwriteService.logout();
|
||||
if (logoutSuccess) {
|
||||
Get.snackbar(
|
||||
'Logout erfolgreich',
|
||||
'Sie wurden abgemeldet.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
Get.offAllNamed('/login-page');
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'Logout fehlgeschlagen',
|
||||
'Bitte versuchen Sie es erneut.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
lib/controller/login_controller.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../pages/home_view.dart';
|
||||
import '../pages/signin_view.dart';
|
||||
import '../services/appwrite_service.dart';
|
||||
|
||||
class LoginController extends GetxController {
|
||||
final emailController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
final appwriteService = AppwriteService();
|
||||
|
||||
@override
|
||||
void onReady() {}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
emailController.dispose();
|
||||
passwordController.dispose();
|
||||
}
|
||||
|
||||
void login() async {
|
||||
var email = emailController.text;
|
||||
var password = passwordController.text;
|
||||
// Hier können Sie die Login-Logik implementieren
|
||||
print('Login mit E-Mail: $email, Passwort: $password');
|
||||
var loginSuccess = await appwriteService.login(email, password);
|
||||
if (loginSuccess) {
|
||||
Get.snackbar(
|
||||
'Login erfolgreich',
|
||||
'Willkommen zurück!',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
Get.offAndToNamed(HomePage.namedRoute);
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'Login fehlgeschlagen',
|
||||
'Bitte überprüfen Sie Ihre E-Mail und Ihr Passwort und versuchen Sie es erneut.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void goToSignInPage() {
|
||||
Get.offAndToNamed(SigninPage.namedRoute);
|
||||
}
|
||||
}
|
||||
87
lib/controller/signin_controller.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../pages/home_view.dart';
|
||||
import '../pages/login_view.dart';
|
||||
import '../services/appwrite_service.dart';
|
||||
|
||||
class SigninController extends GetxController {
|
||||
final userNameController = TextEditingController();
|
||||
final emailController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
final appwriteService = AppwriteService();
|
||||
|
||||
@override
|
||||
void onReady() {}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
userNameController.dispose();
|
||||
emailController.dispose();
|
||||
passwordController.dispose();
|
||||
}
|
||||
|
||||
void register() async {
|
||||
//Eingaben überprüfen
|
||||
if (!_checkInputs()) return;
|
||||
//Registrierungslogik
|
||||
var userName = userNameController.text;
|
||||
var email = emailController.text;
|
||||
var password = passwordController.text;
|
||||
// Testausgabe
|
||||
print(
|
||||
'Registrieren mit Benutzername: $userName, E-Mail: $email, Passwort: $password',
|
||||
);
|
||||
//appwrite Registrierung und login Logik hier einfügen
|
||||
var registrationSuccess = await appwriteService.register(
|
||||
userName,
|
||||
email,
|
||||
password,
|
||||
);
|
||||
//Nach erfolgreicher Registrierung Meldung und zur Startseite navigieren
|
||||
if (registrationSuccess) {
|
||||
Get.snackbar(
|
||||
'Registrierung erfolgreich, sie werden weitergeleitet',
|
||||
'Willkommen, $userName!',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
Get.offAndToNamed(HomePage.namedRoute);
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'Registrierung fehlgeschlagen',
|
||||
'Bitte überprüfen Sie Ihre Eingaben und versuchen Sie es erneut.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void goToLoginPage() {
|
||||
Get.offAndToNamed(LoginPage.namedRoute);
|
||||
}
|
||||
|
||||
bool _checkInputs() {
|
||||
var isOk = false;
|
||||
if (userNameController.text.isEmpty ||
|
||||
emailController.text.isEmpty ||
|
||||
passwordController.text.isEmpty) {
|
||||
Get.snackbar(
|
||||
'Fehler',
|
||||
'Bitte alle Felder ausfüllen',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
isOk = false;
|
||||
} else {
|
||||
isOk = true;
|
||||
}
|
||||
if (passwordController.text.length < 6) {
|
||||
Get.snackbar(
|
||||
'Fehler',
|
||||
'Passwort muss mindestens 6 Zeichen lang sein',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
isOk = false;
|
||||
} else {
|
||||
isOk = true;
|
||||
}
|
||||
return isOk;
|
||||
}
|
||||
}
|
||||
10
lib/helper/helper.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
19
lib/helper/sample_bindings.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
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());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
27
lib/helper/sample_routes.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'sample_bindings.dart';
|
||||
import '../pages/home_view.dart';
|
||||
import '../pages/signin_view.dart';
|
||||
import '../pages/login_view.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,
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
28
lib/main.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'helper/sample_bindings.dart';
|
||||
import 'helper/sample_routes.dart';
|
||||
import 'pages/login_view.dart';
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetMaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
|
||||
initialBinding: SampleBindings(),
|
||||
initialRoute: LoginPage.namedRoute,
|
||||
getPages: SampleRouts.samplePages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
82
lib/models/tank_model.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class TankModel {
|
||||
final String szDocumentId;
|
||||
final String szUserId;
|
||||
final String szDate;
|
||||
final String szOdometer;
|
||||
final String szLiters;
|
||||
final String szPricePerLiter;
|
||||
final String szLocation;
|
||||
final String szPriceTotal;
|
||||
|
||||
TankModel({
|
||||
required this.szDocumentId,
|
||||
required this.szUserId,
|
||||
required this.szDate,
|
||||
required this.szOdometer,
|
||||
required this.szLiters,
|
||||
required this.szPricePerLiter,
|
||||
required this.szLocation,
|
||||
required this.szPriceTotal,
|
||||
});
|
||||
|
||||
TankModel copyWith({
|
||||
String? szDocumentId,
|
||||
String? szUserId,
|
||||
String? szDate,
|
||||
String? szOdometer,
|
||||
String? szLiters,
|
||||
String? szPricePerLiter,
|
||||
String? szLocation,
|
||||
String? szPriceTotal,
|
||||
}) {
|
||||
return TankModel(
|
||||
szDocumentId: szDocumentId ?? this.szDocumentId,
|
||||
szUserId: szUserId ?? this.szUserId,
|
||||
szDate: szDate ?? this.szDate,
|
||||
szOdometer: szOdometer ?? this.szOdometer,
|
||||
szLiters: szLiters ?? this.szLiters,
|
||||
szPricePerLiter: szPricePerLiter ?? this.szPricePerLiter,
|
||||
szLocation: szLocation ?? this.szLocation,
|
||||
szPriceTotal: szPriceTotal ?? this.szPriceTotal,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
final result = <String, dynamic>{};
|
||||
|
||||
result.addAll({'\$id': szDocumentId});
|
||||
result.addAll({'userId': szUserId});
|
||||
result.addAll({'date': szDate});
|
||||
result.addAll({'odometer': szOdometer});
|
||||
result.addAll({'liters': szLiters});
|
||||
result.addAll({'pricePerLiter': szPricePerLiter});
|
||||
result.addAll({'location': szLocation});
|
||||
result.addAll({'priceTotal': szPriceTotal});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
factory TankModel.fromMap(Map<String, dynamic> map) {
|
||||
return TankModel(
|
||||
szDocumentId: map['\$id'] ?? '',
|
||||
szUserId: map['userId'] ?? '',
|
||||
szDate: map['date'] ?? '',
|
||||
szOdometer: map['odometer'] ?? '',
|
||||
szLiters: map['liters'] ?? '',
|
||||
szPricePerLiter: map['pricePerLiter'] ?? '',
|
||||
szLocation: map['location'] ?? '',
|
||||
szPriceTotal: (double.parse(map['liters']?.toString() ?? '0') * double.parse(map['pricePerLiter']?.toString() ?? '0')).toStringAsFixed(2),
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory TankModel.fromJson(String source) => TankModel.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TankModel(szDocumentId: $szDocumentId, szUserId: $szUserId, szDate: $szDate, szOdometer: $szOdometer, szLiters: $szLiters, szPricePerLiter: $szPricePerLiter, szLocation: $szLocation, szPriceTotal: $szPriceTotal)';
|
||||
}
|
||||
}
|
||||
56
lib/pages/home_view.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../controller/home_controller.dart';
|
||||
|
||||
class HomePage extends GetView<HomeController> {
|
||||
static const String namedRoute = '/home-page';
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var homCtrl = controller;
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.blueGrey,
|
||||
foregroundColor: Colors.white,
|
||||
title: const Text('Tank List'),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
controller.onInit();
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => homCtrl.logout(),
|
||||
icon: Icon(Icons.logout),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Obx(
|
||||
() => homCtrl.isLoading.value == false
|
||||
? ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
var tank = homCtrl.listTankModel[index];
|
||||
return ListTile(
|
||||
title: Text(
|
||||
'${tank.szDate} - ${tank.szLiters}L - ${tank.szPricePerLiter}€/L',
|
||||
),
|
||||
subtitle: Text(
|
||||
'Total: ${tank.szPriceTotal}€ - Odometer: ${tank.szOdometer}km',
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: homCtrl.listTankModel.length,
|
||||
)
|
||||
: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
36
lib/pages/login_view.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
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 SafeArea(
|
||||
child: Scaffold(
|
||||
body: Container(
|
||||
height: displayHeight,
|
||||
width: displayWidth,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 130.0,
|
||||
),
|
||||
color: Colors.blue.shade100,
|
||||
child: SingleChildScrollView(
|
||||
child: MyLoginWidget(
|
||||
onButtonPressed: () => logCtrl.login(),
|
||||
signInOnTab: () => logCtrl.goToSignInPage(),
|
||||
logCtrl: logCtrl,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
36
lib/pages/signin_view.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
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: Scaffold(
|
||||
body: Container(
|
||||
height: displayHeight,
|
||||
width: displayWidth,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 100.0,
|
||||
),
|
||||
color: Colors.blue.shade100,
|
||||
child: SingleChildScrollView(
|
||||
child: MySigninWidget(
|
||||
signCtrl: sigCtrl,
|
||||
onButtonPressed: () => sigCtrl.register(),
|
||||
logInOnTab: () => sigCtrl.goToLoginPage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
168
lib/services/appwrite_service.dart
Normal file
@@ -0,0 +1,168 @@
|
||||
import 'package:appwrite/models.dart';
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
import '../config/environment.dart';
|
||||
|
||||
class AppwriteService {
|
||||
static final String endpoint = Environment.appwritePublicEndpoint;
|
||||
static final String projectId = Environment.appwriteProjectId;
|
||||
static final String projectName = Environment.appwriteProjectName;
|
||||
static final String realtimeCollectionId =
|
||||
Environment.appwriteRealtimeCollectionId;
|
||||
static final String databaseId = Environment.appwriteDatabaseId;
|
||||
|
||||
final Client _client = Client().setProject(projectId).setEndpoint(endpoint);
|
||||
|
||||
// ignore: unused_field
|
||||
late final Account _account;
|
||||
// ignore: unused_field
|
||||
late final Databases _databases;
|
||||
|
||||
AppwriteService._internal() {
|
||||
_account = Account(_client);
|
||||
_databases = Databases(_client);
|
||||
}
|
||||
|
||||
static final AppwriteService _instance = AppwriteService._internal();
|
||||
|
||||
/// Singleton instance getter
|
||||
factory AppwriteService() => _instance;
|
||||
|
||||
// register new user with userName, e-Mail and password
|
||||
Future<bool> register(String userName, String email, String password) async {
|
||||
try {
|
||||
final user = await _account.create(
|
||||
userId: ID.unique(),
|
||||
email: email,
|
||||
password: password,
|
||||
name: userName,
|
||||
);
|
||||
print('Registrierung erfolgreich: ${user.$id}');
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Registrierung fehlgeschlagen: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// login with e-Mail and password
|
||||
Future<bool> login(String email, String password) async {
|
||||
await logout();
|
||||
try {
|
||||
final session = await _account.createEmailPasswordSession(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
print('Login erfolgreich: ${session.$id}');
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Login fehlgeschlagen: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// logout current user
|
||||
Future<bool> logout() async {
|
||||
try {
|
||||
await _account.deleteSession(sessionId: 'current');
|
||||
print('Logout erfolgreich');
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Logout fehlgeschlagen: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Get current user ID
|
||||
Future<String?> getCurrentUserId() async {
|
||||
try {
|
||||
final user = await _account.get();
|
||||
return user.$id;
|
||||
} catch (e) {
|
||||
print('Fehler beim Abrufen der Benutzer-ID: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get List<Document> from Realtime Collection
|
||||
Future<List<Document>> getDocumentsFromCollection(String userId) async {
|
||||
try {
|
||||
final documents = await _databases.listDocuments(
|
||||
databaseId: databaseId,
|
||||
collectionId: realtimeCollectionId,
|
||||
queries: [Query.equal('userId', userId), Query.orderDesc('date')],
|
||||
);
|
||||
return documents.documents;
|
||||
} catch (e) {
|
||||
print('Fehler beim Abrufen der Dokumente: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get Document per Id from Realtime Collection
|
||||
Future<Document?> getDocumentById(String documentId) async {
|
||||
try {
|
||||
final document = await _databases.getDocument(
|
||||
databaseId: databaseId,
|
||||
collectionId: realtimeCollectionId,
|
||||
documentId: documentId,
|
||||
);
|
||||
return document;
|
||||
} catch (e) {
|
||||
print('Fehler beim Abrufen des Dokuments: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Save a new document to Realtime Collection
|
||||
Future<bool> createDocumentInCollection(Map<String, dynamic> data) async {
|
||||
try {
|
||||
await _databases.createDocument(
|
||||
databaseId: databaseId,
|
||||
collectionId: realtimeCollectionId,
|
||||
documentId: ID.unique(),
|
||||
data: data,
|
||||
);
|
||||
print('Dokument erfolgreich erstellt');
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Fehler beim Erstellen des Dokuments: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update an existing document in Realtime Collection
|
||||
Future<bool> updateDocumentInCollection(
|
||||
String documentId,
|
||||
Map<String, dynamic> data,
|
||||
) async {
|
||||
try {
|
||||
await _databases.updateDocument(
|
||||
databaseId: databaseId,
|
||||
collectionId: realtimeCollectionId,
|
||||
documentId: documentId,
|
||||
data: data,
|
||||
);
|
||||
print('Dokument erfolgreich aktualisiert');
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Fehler beim Aktualisieren des Dokuments: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete a document from Realtime Collection
|
||||
Future<bool> deleteDocumentFromCollection(String documentId) async {
|
||||
try {
|
||||
await _databases.deleteDocument(
|
||||
databaseId: databaseId,
|
||||
collectionId: realtimeCollectionId,
|
||||
documentId: documentId,
|
||||
);
|
||||
print('Dokument erfolgreich gelöscht');
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Fehler beim Löschen des Dokuments: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
134
lib/widgets/my_login_widget.dart
Normal 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/guru.png',
|
||||
width: 400,
|
||||
height: 400,
|
||||
),
|
||||
),
|
||||
),
|
||||
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!!'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
146
lib/widgets/my_signin_widget.dart
Normal file
@@ -0,0 +1,146 @@
|
||||
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),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: Image.asset(
|
||||
'assets/images/guru01.png',
|
||||
width: 400,
|
||||
height: 400,
|
||||
),
|
||||
),
|
||||
),
|
||||
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: 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!!'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
610
pubspec.lock
Normal file
@@ -0,0 +1,610 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
appwrite:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: appwrite
|
||||
sha256: "3e1f618c8f75bafa49ef7b1b445f64c53cf4620a195443f4d119bbc95a666d0a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.0.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
cookie_jar:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cookie_jar
|
||||
sha256: a6ac027d3ed6ed756bfce8f3ff60cb479e266f3b0fdabd6242b804b6765e52de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.8"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
device_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.1.2"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_platform_interface
|
||||
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.3"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_auth_2:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_web_auth_2
|
||||
sha256: "4d3d2fd3d26bf1a26b3beafd4b4b899c0ffe10dc99af25abc58ffe24e991133c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
flutter_web_auth_2_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_web_auth_2_platform_interface
|
||||
sha256: e8669e262005a8354389ba2971f0fc1c36188481234ff50d013aaf993f30f739
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
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"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_fonts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "6996212014b996eaa17074e02b1b925b212f5e053832d9048970dc27255a8fb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
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: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.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: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "9922a1ad59ac5afb154cc948aa6ded01987a75003651d0a2866afc23f4da624e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.3"
|
||||
package_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.3.1"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
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: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
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: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
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: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_io
|
||||
sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
url_launcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.2"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.28"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.6"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.5"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.15.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32_registry
|
||||
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.5"
|
||||
window_to_front:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: window_to_front
|
||||
sha256: "7aef379752b7190c10479e12b5fd7c0b9d92adc96817d9e96c59937929512aee"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.3"
|
||||
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: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.7 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
29
pubspec.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: flutter_tank_web_app
|
||||
description: "A new Flutter project."
|
||||
|
||||
publish_to: "none"
|
||||
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ^3.10.7
|
||||
|
||||
dependencies:
|
||||
cupertino_icons: ^1.0.8
|
||||
flutter:
|
||||
sdk: flutter
|
||||
get: ^4.7.3
|
||||
google_fonts: ^7.1.0
|
||||
intl: ^0.20.2
|
||||
appwrite: ^14.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^6.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
assets:
|
||||
- assets/images/
|
||||
30
test/widget_test.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:flutter_tank_web_app/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const MyApp());
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
BIN
web/favicon.png
Normal file
|
After Width: | Height: | Size: 917 B |
BIN
web/icons/Icon-192.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
web/icons/Icon-512.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
web/icons/Icon-maskable-192.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
web/icons/Icon-maskable-512.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
38
web/index.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
|
||||
The path provided below has to start and end with a slash "/" in order for
|
||||
it to work correctly.
|
||||
|
||||
For more details:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
|
||||
This is a placeholder for base href that will be replaced by the value of
|
||||
the `--base-href` argument provided to `flutter build`.
|
||||
-->
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="flutter_tank_web_app">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>flutter_tank_web_app</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
</body>
|
||||
</html>
|
||||
35
web/manifest.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "flutter_tank_web_app",
|
||||
"short_name": "flutter_tank_web_app",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#0175C2",
|
||||
"theme_color": "#0175C2",
|
||||
"description": "A new Flutter project.",
|
||||
"orientation": "portrait-primary",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/Icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||