perbarui login fix nama aplikasi serta icon aplikasi

This commit is contained in:
rhanarmt 2026-05-19 18:40:20 +07:00
parent 634edd078a
commit 7502d80d4b
56 changed files with 351 additions and 61 deletions

View File

@ -2,7 +2,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="finalproject"
android:label="Tobaku Sulastri"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -15,6 +15,11 @@ subprojects {
subprojects {
project.evaluationDependsOn(":app")
}
subprojects {
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("-Xlint:-options")
}
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)

View File

@ -1,3 +1,5 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
org.gradle.java.home=C:/Program Files/Android/Android Studio1/jbr
android.useAndroidX=true
android.enableJetifier=true
android.javaCompile.suppressSourceTargetDeprecationWarning=true

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Finalproject</string>
<string>Tobaku Sulastri</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>finalproject</string>
<string>Tobaku Sulastri</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@ -20,7 +20,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Prediksi Stok Bahan Kue',
title: 'Tobaku Sulastri',
theme: AppTheme.lightTheme(),
debugShowCheckedModeBanner: false,
navigatorObservers: [routeObserver],

View File

@ -1,11 +1,14 @@
import 'package:flutter/material.dart';
import 'package:finalproject/services/auth_service.dart';
import 'package:finalproject/services/ml_service.dart';
class LoginController extends ChangeNotifier {
final usernameController = TextEditingController(text: 'admin@sulastri.com');
final passwordController = TextEditingController(text: 'password');
final usernameController = TextEditingController();
final passwordController = TextEditingController();
bool isPasswordVisible = false;
bool isLoading = false;
String? errorMessage;
void togglePasswordVisibility() {
isPasswordVisible = !isPasswordVisible;
@ -24,13 +27,27 @@ class LoginController extends ChangeNotifier {
if (error != null) return false;
isLoading = true;
errorMessage = null;
notifyListeners();
await Future.delayed(const Duration(seconds: 1));
final result = await MLService.login(
usernameOrEmail: usernameController.text.trim(),
password: passwordController.text,
);
isLoading = false;
final success = result['status'] == 'success';
if (success) {
final data = result['data'];
await AuthService.saveLoginSession(
name: data is Map ? data['name']?.toString() : null,
email: data is Map ? data['email']?.toString() : null,
);
} else {
errorMessage = result['message']?.toString() ?? 'Login gagal';
}
notifyListeners();
return true;
return success;
}
@override

View File

@ -37,9 +37,27 @@ class _LoginScreenState extends State<LoginScreen> {
if (success) {
Navigator.of(context).pushReplacementNamed('/dashboard');
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(_controller.errorMessage ?? 'Login gagal'),
backgroundColor: AppColors.statusError,
),
);
}
}
void _onForgotPasswordPressed() {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text(
'Fitur lupa password dengan OTP akan dikembangkan pada tahap berikutnya.',
),
backgroundColor: AppColors.primaryBrown,
),
);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
@ -193,7 +211,9 @@ class _LoginScreenState extends State<LoginScreen> {
alignment: Alignment.centerRight,
child: TextButton(
onPressed:
_controller.isLoading ? null : () {},
_controller.isLoading
? null
: _onForgotPasswordPressed,
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
),
@ -247,35 +267,13 @@ class _LoginScreenState extends State<LoginScreen> {
),
),
),
const SizedBox(height: 24),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.bgLight,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColors.grey300),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Demo: Klik "Masuk" untuk melanjutkan',
style: AppTextStyles.bodySmall.copyWith(
color: AppColors.statusError,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
),
),
const SizedBox(height: 48),
Text(
'© 2025 Toko Bahan Kue Sulastri',
'(c) 2025 Tobaku Sulastri',
style: AppTextStyles.bodySmall.copyWith(
color: AppColors.textTertiary,
),

View File

@ -1,6 +1,7 @@
import 'package:finalproject/theme/colors.dart';
import 'package:finalproject/theme/text_styles.dart';
import 'package:flutter/material.dart';
import 'package:finalproject/services/auth_service.dart';
import 'settings_controller.dart';
@ -51,8 +52,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
),
ElevatedButton(
onPressed: () {
onPressed: () async {
Navigator.pop(context);
await AuthService.logout();
if (!context.mounted) return;
Navigator.of(
context,
).pushNamedAndRemoveUntil('/login', (route) => false);
@ -341,7 +344,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
const SizedBox(height: 12),
Text(
'© 2025 Toko Bahan Kue Sulastri',
'© 2025 Tobaku Sulastri',
style: AppTextStyles.bodySmall.copyWith(
color: AppColors.textTertiary,
),

View File

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:finalproject/services/auth_service.dart';
import 'package:finalproject/theme/colors.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
@ -63,10 +64,12 @@ class _SplashScreenState extends State<SplashScreen>
// Tunggu sebentar sebelum navigate
await Future.delayed(const Duration(seconds: 1));
if (mounted) {
// Navigate ke Login
Navigator.of(context).pushReplacementNamed('/login');
}
final isLoggedIn = await AuthService.isLoggedIn();
if (!mounted) return;
Navigator.of(
context,
).pushReplacementNamed(isLoggedIn ? '/dashboard' : '/login');
} catch (e) {
setState(() => _statusMessage = 'Error: ${e.toString()}');
await Future.delayed(const Duration(seconds: 2));
@ -148,16 +151,17 @@ class _SplashScreenState extends State<SplashScreen>
borderRadius: BorderRadius.circular(28),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
color: Colors.black.withValues(alpha: 0.15),
blurRadius: 25,
offset: const Offset(0, 10),
),
],
),
child: Center(
child: Text(
'🍰',
style: TextStyle(fontSize: 60),
child: Padding(
padding: const EdgeInsets.all(18),
child: Image.asset(
'assets/branding/app_icon.png',
fit: BoxFit.contain,
),
),
),
@ -179,7 +183,7 @@ class _SplashScreenState extends State<SplashScreen>
children: [
// Main title
Text(
'Sulastri',
'Tobaku Sulastri',
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.w700,
@ -301,6 +305,16 @@ class _SplashScreenState extends State<SplashScreen>
),
const SizedBox(height: 40),
Text(
_statusMessage,
style: TextStyle(
fontSize: 13,
color: AppColors.textTertiary,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 16),
// Version text
Text(
'Version 1.0.0',

View File

@ -0,0 +1,32 @@
import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
static const String _isLoggedInKey = 'is_logged_in';
static const String _userNameKey = 'user_name';
static const String _userEmailKey = 'user_email';
static Future<void> saveLoginSession({String? name, String? email}) async {
final preferences = await SharedPreferences.getInstance();
await preferences.setBool(_isLoggedInKey, true);
if (name != null && name.isNotEmpty) {
await preferences.setString(_userNameKey, name);
}
if (email != null && email.isNotEmpty) {
await preferences.setString(_userEmailKey, email);
}
}
static Future<bool> isLoggedIn() async {
final preferences = await SharedPreferences.getInstance();
return preferences.getBool(_isLoggedInKey) ?? false;
}
static Future<void> logout() async {
final preferences = await SharedPreferences.getInstance();
await preferences.remove(_isLoggedInKey);
await preferences.remove(_userNameKey);
await preferences.remove(_userEmailKey);
}
}

View File

@ -5,10 +5,42 @@ class MLService {
// API URL - Change based on environment
// Untuk emulator Android: 10.0.2.2
// Untuk device fisik: 192.168.x.x atau 127.0.0.1 kalau local
static const String baseUrl = 'http://192.168.1.72:5000';
static const String baseUrl = 'http://192.168.18.30:5000';
static const int timeoutSeconds = 30;
/// Login using account data from MySQL login table.
static Future<Map<String, dynamic>> login({
required String usernameOrEmail,
required String password,
}) async {
try {
final response = await http
.post(
Uri.parse('$baseUrl/api/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': usernameOrEmail,
'username': usernameOrEmail,
'password': password,
}),
)
.timeout(Duration(seconds: timeoutSeconds));
final body = jsonDecode(response.body) as Map<String, dynamic>;
if (response.statusCode == 200) {
return body;
}
return {'status': 'error', 'message': body['message'] ?? 'Login gagal'};
} catch (e) {
return {
'status': 'error',
'message': 'Tidak dapat terhubung ke server: $e',
};
}
}
/// Convert grams to kilograms with rounding rules:
/// - <= 500g -> 0.5kg
/// - 500g < g <= 1000g -> 1kg

View File

@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "finalproject");
gtk_header_bar_set_title(header_bar, "Tobaku Sulastri");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "finalproject");
gtk_window_set_title(window, "Tobaku Sulastri");
}
gtk_window_set_default_size(window, 1280, 720);

View File

@ -7,8 +7,10 @@ import Foundation
import path_provider_foundation
import share_plus
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 851 B

View File

@ -5,10 +5,10 @@
// 'flutter create' template.
// The application's name. By default this is also the title of the Flutter window.
PRODUCT_NAME = finalproject
PRODUCT_NAME = Tobaku Sulastri
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.example.finalproject
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved.
PRODUCT_COPYRIGHT = Copyright (c) 2026 Tobaku Sulastri. All rights reserved.

View File

@ -10,6 +10,7 @@ import pandas as pd
import numpy as np
import logging
import math
import hashlib
from datetime import datetime
import mysql.connector
from mysql.connector import Error
@ -97,6 +98,14 @@ def build_report_response(success: bool, message: str, data: list | None = None,
'data': data or []
}), status_code
def verify_login_password(input_password: str, stored_password: str) -> bool:
"""Accept plain text passwords and SHA-256 hashes for simple local auth."""
if input_password == stored_password:
return True
hashed_input = hashlib.sha256(input_password.encode('utf-8')).hexdigest()
return hashed_input == stored_password
def ensure_prediction_needs_column(connection, table_name: str = 'predictions') -> str | None:
needs_col = get_existing_column(
connection,
@ -477,6 +486,64 @@ def get_metadata():
}), 200
@app.route('/api/login', methods=['POST'])
def login():
"""Login using email/username and password stored in the login table."""
try:
data = request.json or {}
username_or_email = (data.get('email') or data.get('username') or '').strip()
password = data.get('password') or ''
if not username_or_email or not password:
return jsonify({
'status': 'error',
'message': 'Email/username dan password wajib diisi'
}), 400
connection = get_db_connection()
if connection is None:
return jsonify({
'status': 'error',
'message': 'Tidak dapat terhubung ke database'
}), 500
cursor = connection.cursor(dictionary=True)
cursor.execute("""
SELECT id, name, email, username, password
FROM login
WHERE email = %s OR username = %s
LIMIT 1
""", (username_or_email, username_or_email))
user = cursor.fetchone()
cursor.close()
connection.close()
if user is None or not verify_login_password(password, user['password']):
return jsonify({
'status': 'error',
'message': 'Email/username atau password salah'
}), 401
user.pop('password', None)
return jsonify({
'status': 'success',
'message': 'Login berhasil',
'data': user
}), 200
except Error as e:
logger.error(f"Login database error: {e}")
return jsonify({
'status': 'error',
'message': 'Tabel login belum tersedia atau database bermasalah'
}), 500
except Exception as e:
logger.error(f"Login error: {e}")
return jsonify({
'status': 'error',
'message': 'Terjadi kesalahan saat login'
}), 500
@app.route('/prediksi', methods=['POST'])
def prediksi():
"""

View File

@ -102,6 +102,19 @@ def create_tables():
""")
logger.info("✅ Predictions table created successfully")
# Login table
cursor.execute("""
CREATE TABLE IF NOT EXISTS login (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
logger.info("Login table created successfully")
connection.commit()
cursor.close()
connection.close()
@ -136,6 +149,29 @@ def insert_default_products():
print(f"❌ Error inserting products: {err}")
return False
def insert_default_login():
"""Insert default login account"""
try:
connection = mysql.connector.connect(**DB_CONFIG)
cursor = connection.cursor()
cursor.execute("""
INSERT INTO login (name, email, username, password)
VALUES (%s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
username = VALUES(username)
""", ('Ibu Sulastri', 'admin@sulastri.com', 'admin', 'password'))
connection.commit()
logger.info("Default login account ready")
cursor.close()
connection.close()
return True
except Error as err:
print(f"Error inserting login account: {err}")
return False
def verify_connection():
"""Verify database connection"""
try:
@ -171,6 +207,9 @@ def main():
if not insert_default_products():
return
if not insert_default_login():
return
verify_connection()
print()

View File

@ -59,6 +59,16 @@ CREATE TABLE IF NOT EXISTS predictions (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Create Login Table
CREATE TABLE IF NOT EXISTS login (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Create Recipes Table
CREATE TABLE IF NOT EXISTS recipes (
id INT AUTO_INCREMENT PRIMARY KEY,
@ -88,6 +98,13 @@ INSERT INTO products (name, category, product_type, unit, price, current_stock)
('Keju Parut 250gr', 'Keju', 'Bahan', 'kg', 28000, 10),
('Baking Powder', 'Bahan Tambahan', 'Bahan', 'kg', 8000, 35);
-- Insert Default Login Account
INSERT INTO login (name, email, username, password) VALUES
('Ibu Sulastri', 'admin@sulastri.com', 'admin', 'password')
ON DUPLICATE KEY UPDATE
name = VALUES(name),
username = VALUES(username);
-- Insert Sample Recipes
INSERT INTO recipes (recipe_name, description) VALUES
('Donat', 'Resep donat lezat'),

View File

@ -392,6 +392,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e
url: "https://pub.dev"
source: hosted
version: "2.4.13"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sky_engine:
dependency: transitive
description: flutter

View File

@ -38,6 +38,9 @@ dependencies:
# HTTP Client untuk API
http: ^1.1.0
# Local storage untuk menyimpan status login
shared_preferences: ^2.2.3
# Intl untuk date formatting
intl: ^0.19.0
@ -73,6 +76,9 @@ flutter:
# the material Icons class.
uses-material-design: true
assets:
- assets/branding/app_icon.png
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -23,13 +23,13 @@
<!-- 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="finalproject">
<meta name="apple-mobile-web-app-title" content="Tobaku Sulastri">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>finalproject</title>
<title>Tobaku Sulastri</title>
<link rel="manifest" href="manifest.json">
</head>
<body>

View File

@ -1,11 +1,11 @@
{
"name": "finalproject",
"short_name": "finalproject",
"name": "Tobaku Sulastri",
"short_name": "Tobaku",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"background_color": "#F8F1E8",
"theme_color": "#8B5A3C",
"description": "Aplikasi prediksi permintaan dan stok bahan kue Tobaku Sulastri.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [

View File

@ -90,12 +90,12 @@ BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "finalproject" "\0"
VALUE "FileDescription", "Tobaku Sulastri" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "finalproject" "\0"
VALUE "InternalName", "Tobaku Sulastri" "\0"
VALUE "LegalCopyright", "Copyright (C) 2026 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "finalproject.exe" "\0"
VALUE "ProductName", "finalproject" "\0"
VALUE "ProductName", "Tobaku Sulastri" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END

View File

@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.Create(L"finalproject", origin, size)) {
if (!window.Create(L"Tobaku Sulastri", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 22 KiB