Perubahan Full

This commit is contained in:
Nararga Maulana_TKJ2 2024-07-24 23:37:00 +07:00
parent fb2a93889e
commit 773cc55ea3
49 changed files with 727 additions and 196 deletions

BIN
Store.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

View File

@ -22,8 +22,14 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0' flutterVersionName = '1.0'
} }
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android { android {
namespace "com.example.beta_app1" namespace "com.nutrify.app"
compileSdk flutter.compileSdkVersion compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
@ -42,7 +48,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.beta_app1" applicationId "com.nutrify.app"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion minSdkVersion flutter.minSdkVersion
@ -51,13 +57,17 @@ android {
versionName flutterVersionName versionName flutterVersionName
} }
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. signingConfig signingConfigs.release
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
minifyEnabled true
shrinkResources true
} }
} }
} }

View File

@ -6,7 +6,7 @@
<application <application
android:label="Nutrify" android:label="Nutrify"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/launcher_icon">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

View File

@ -1,4 +1,4 @@
package com.example.beta_app1 package com.nutrify.app
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

BIN
images/nutrify.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -427,7 +427,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@ -484,7 +484,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -34,7 +34,7 @@ class _LoginPageState extends State<LoginPage> {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
String mqttServerIp = String mqttServerIp =
prefs.getString('mqtt_server_ip') ?? '192.168.183.153'; prefs.getString('mqtt_server_ip') ?? '192.168.182.153';
String url = 'http://$mqttServerIp/test_api/login.php'; String url = 'http://$mqttServerIp/test_api/login.php';
final response = await http.post( final response = await http.post(
@ -128,7 +128,7 @@ class _LoginPageState extends State<LoginPage> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Image.asset( Image.asset(
"images/nature.gif", "images/nutrify.png",
height: 200, height: 200,
width: 200, width: 200,
), ),
@ -216,31 +216,41 @@ class _LoginPageState extends State<LoginPage> {
), ),
child: const Text( child: const Text(
'Login', 'Login',
style: TextStyle(color: Colors.white), style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 25),
ElevatedButton( Center(
onPressed: () { child: Row(
Navigator.push( mainAxisAlignment: MainAxisAlignment.center,
context, children: [
MaterialPageRoute( const Text(
builder: (context) => RegisterPage()), 'Belum memiliki akun?',
); style: TextStyle(
}, color: Colors.black,
style: ElevatedButton.styleFrom( fontSize: 14.0,
backgroundColor: ),
const Color.fromARGB(255, 255, 255, 255), ),
shape: RoundedRectangleBorder( GestureDetector(
borderRadius: BorderRadius.circular(12.0), onTap: () {
), Navigator.push(
padding: const EdgeInsets.symmetric(vertical: 16.0), context,
minimumSize: const Size(double.infinity, 50), MaterialPageRoute(
), builder: (context) => RegisterPage(),
child: const Text( ),
'Register', );
style: TextStyle( },
color: Color.fromARGB(166, 26, 21, 21)), child: const Text(
' Register',
style: TextStyle(
color: Colors.red,
fontSize: 14.0,
),
),
),
],
), ),
), ),
], ],
@ -259,7 +269,7 @@ class _LoginPageState extends State<LoginPage> {
const Text( const Text(
"@2024", "@2024",
style: TextStyle(color: Colors.black, fontSize: 10.0), style: TextStyle(color: Colors.black, fontSize: 10.0),
) ),
], ],
), ),
), ),

View File

@ -1,3 +1,4 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'login.dart'; import 'login.dart';
@ -53,35 +54,55 @@ class _RegisterPageState extends State<RegisterPage> {
}); });
} }
// Validasi fullname (opsional)
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
String mqttServerIp = prefs.getString('mqtt_server_ip') ?? '192.168.0.1'; String mqttServerIp =
prefs.getString('mqtt_server_ip') ?? '192.168.182.153';
String url = 'http://$mqttServerIp/test_api/register.php'; String url = 'http://$mqttServerIp/test_api/register.php';
final response = await http.post( final response = await http.post(
Uri.parse(url), Uri.parse(url),
body: { headers: {"Content-Type": "application/json"},
body: jsonEncode({
"username": username, "username": username,
"password": password, "password": password,
"fullname": fullname, "fullname": fullname,
}, }),
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar( final responseData = jsonDecode(response.body);
const SnackBar( if (responseData['status'] == 'success') {
content: Text('Akun berhasil dibuat'), ScaffoldMessenger.of(context).showSnackBar(
duration: Duration(seconds: 2), const SnackBar(
backgroundColor: Colors.green, content: Text('Akun berhasil dibuat'),
), duration: Duration(seconds: 2),
); backgroundColor: Colors.green,
Navigator.push( ),
context, );
MaterialPageRoute(builder: (context) => const LoginPage()), Navigator.push(
); context,
MaterialPageRoute(builder: (context) => const LoginPage()),
);
} else {
setState(() {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(responseData['message']),
duration: Duration(seconds: 2),
backgroundColor: Colors.red,
),
);
});
}
} else { } else {
setState(() { setState(() {
// Handle error ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Failed to register user'),
duration: Duration(seconds: 2),
backgroundColor: Colors.red,
),
);
}); });
} }
} }
@ -89,154 +110,208 @@ class _RegisterPageState extends State<RegisterPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( backgroundColor: Colors.white,
title: const Text('Halaman Registrasi'), body: Center(
), child: SingleChildScrollView(
body: SingleChildScrollView( child: Center(
child: Center( child: Padding(
child: Padding( padding: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(15.0), child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
children: <Widget>[ Image.asset(
Padding( "images/nutrify.png",
padding: const EdgeInsets.all(10.0), height: 200,
child: width: 200,
Image.asset("images/nature.gif", height: 200, width: 200),
),
const SizedBox(
height: 10,
),
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
), ),
elevation: 8, const SizedBox(
margin: const EdgeInsets.symmetric(horizontal: 16.0), height: 20,
child: Padding( ),
padding: const EdgeInsets.all(20.0), Card(
child: Column( shape: RoundedRectangleBorder(
crossAxisAlignment: CrossAxisAlignment.start, borderRadius: BorderRadius.circular(15.0),
children: <Widget>[ ),
const Center( elevation: 8,
child: Text( margin: const EdgeInsets.symmetric(horizontal: 16.0),
'Register', child: Padding(
style: TextStyle( padding: const EdgeInsets.all(20.0),
fontSize: 24.0, child: Column(
fontFamily: 'Poppins', crossAxisAlignment: CrossAxisAlignment.start,
fontWeight: FontWeight.bold, children: <Widget>[
color: Color.fromARGB(150, 0, 0, 0), const Center(
),
),
),
const SizedBox(height: 10),
TextFormField(
controller: usernameController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.person),
labelText: 'Username',
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
),
hintText: 'Username',
hintStyle: TextStyle(
color: const Color.fromARGB(92, 0, 0, 0)
.withOpacity(0.25),
fontSize: 14.0,
),
),
),
if (usernameError.isNotEmpty)
Padding(
padding:
const EdgeInsets.only(top: 8.0, left: 12.0),
child: Text( child: Text(
usernameError, 'Register',
style: const TextStyle( style: TextStyle(
color: Colors.red, fontSize: 24.0,
fontSize: 12.0, fontFamily: 'Poppins',
fontWeight: FontWeight.bold,
color: Color.fromARGB(150, 0, 0, 0),
), ),
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
TextFormField( TextFormField(
controller: fullnameController, controller: usernameController,
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: const Icon(Icons.perm_identity), prefixIcon: const Icon(Icons.person),
labelText: 'Fullname', labelText: 'Username',
border: const OutlineInputBorder( border: const OutlineInputBorder(
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(12.0)), BorderRadius.all(Radius.circular(12.0)),
),
hintText: 'Fullname',
hintStyle: TextStyle(
color: Colors.black.withOpacity(0.25),
fontSize: 14.0,
),
),
),
const SizedBox(height: 10),
TextFormField(
controller: passwordController,
obscureText: _obscureText,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.password),
labelText: 'Password',
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
),
hintText: 'Password',
hintStyle: TextStyle(
color: Colors.black.withOpacity(0.25),
fontSize: 14.0,
),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility
: Icons.visibility_off,
), ),
onPressed: () { hintText: 'Username',
setState(() { hintStyle: TextStyle(
_obscureText = !_obscureText; color: const Color.fromARGB(92, 0, 0, 0)
}); .withOpacity(0.25),
}, fontSize: 14.0,
),
),
),
if (passwordError.isNotEmpty)
Padding(
padding:
const EdgeInsets.only(top: 8.0, left: 12.0),
child: Text(
passwordError,
style: const TextStyle(
color: Colors.red,
fontSize: 12.0,
), ),
), ),
), ),
const SizedBox( if (usernameError.isNotEmpty)
height: 20, Padding(
), padding:
ElevatedButton( const EdgeInsets.only(top: 8.0, left: 12.0),
onPressed: registerUser, child: Text(
style: ElevatedButton.styleFrom( usernameError,
shape: RoundedRectangleBorder( style: const TextStyle(
borderRadius: BorderRadius.circular(12.0), color: Colors.red,
fontSize: 12.0,
),
),
),
const SizedBox(height: 10),
TextFormField(
controller: fullnameController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.perm_identity),
labelText: 'Fullname',
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
),
hintText: 'Fullname',
hintStyle: TextStyle(
color: Colors.black.withOpacity(0.25),
fontSize: 14.0,
),
), ),
padding: const EdgeInsets.symmetric(vertical: 16.0),
minimumSize: const Size(double.infinity, 50),
), ),
child: const Text('Daftar'), const SizedBox(height: 10),
), TextFormField(
], controller: passwordController,
obscureText: _obscureText,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.password),
labelText: 'Password',
border: const OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
),
hintText: 'Password',
hintStyle: TextStyle(
color: Colors.black.withOpacity(0.25),
fontSize: 14.0,
),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility
: Icons.visibility_off,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
if (passwordError.isNotEmpty)
Padding(
padding:
const EdgeInsets.only(top: 8.0, left: 12.0),
child: Text(
passwordError,
style: const TextStyle(
color: Colors.red,
fontSize: 12.0,
),
),
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: registerUser,
style: ElevatedButton.styleFrom(
backgroundColor: Color.fromARGB(255, 221, 41, 41),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
padding:
const EdgeInsets.symmetric(vertical: 16.0),
minimumSize: const Size(double.infinity, 50),
),
child: const Text(
'Daftar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Sudah memiliki akun?',
style: TextStyle(
color: Colors.black,
fontSize: 14.0,
),
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LoginPage(),
),
);
},
child: const Text(
' Masuk',
style: TextStyle(
color: Colors.green,
fontSize: 14.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
), ),
), ),
), const SizedBox(height: 20),
], const Text(
"Apps Made For",
style: TextStyle(color: Colors.black, fontSize: 10),
),
const Text(
"Nutrient Hydroponic Control",
style: TextStyle(color: Colors.black, fontSize: 10.0),
),
const Text(
"@2024",
style: TextStyle(color: Colors.black, fontSize: 10.0),
),
],
),
), ),
), ),
), ),

View File

@ -258,8 +258,8 @@ class _ChartsSectionState extends State<ChartsSection> {
), ),
primaryYAxis: NumericAxis( primaryYAxis: NumericAxis(
minimum: 0, minimum: 0,
maximum: 20, maximum: 1000,
interval: 5, interval: 200,
majorGridLines: MajorGridLines(width: 0.5, color: Colors.grey[300]), majorGridLines: MajorGridLines(width: 0.5, color: Colors.grey[300]),
axisLine: AxisLine(width: 0), axisLine: AxisLine(width: 0),
labelStyle: TextStyle(color: Colors.grey[700], fontSize: 12), labelStyle: TextStyle(color: Colors.grey[700], fontSize: 12),

View File

@ -33,7 +33,7 @@ class _SplashScreenState extends State<SplashScreen> {
backgroundColor: backgroundColor:
Colors.white, // Ubah sesuai warna latar belakang yang diinginkan Colors.white, // Ubah sesuai warna latar belakang yang diinginkan
body: Center( body: Center(
child: Image.asset('images/nature.gif', child: Image.asset('images/nutrify.png',
width: 200, width: 200,
height: height:
200), // Pastikan untuk menambahkan logo Anda di folder assets 200), // Pastikan untuk menambahkan logo Anda di folder assets

View File

@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "beta_app1") set(BINARY_NAME "beta_app1")
# The unique GTK application identifier for this application. See: # The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID # https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.beta_app1") set(APPLICATION_ID "com.nutrify.app")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.

View File

@ -1,6 +1,14 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
archive:
dependency: transitive
description:
name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
url: "https://pub.dev"
source: hosted
version: "3.6.1"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -65,6 +73,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.3.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -166,6 +190,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.2" version: "3.3.2"
flutter_launcher_icons:
dependency: "direct main"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -232,6 +264,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
url: "https://pub.dev"
source: hosted
version: "4.2.0"
intl: intl:
dependency: transitive dependency: transitive
description: description:
@ -240,6 +280,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.18.1"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -661,6 +709,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.5.0" version: "6.5.0"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks: sdks:
dart: ">=3.3.4 <4.0.0" dart: ">=3.3.4 <4.0.0"
flutter: ">=3.19.0" flutter: ">=3.19.0"

View File

@ -42,6 +42,7 @@ dependencies:
liquid_progress_indicator_v2: ^0.5.0 liquid_progress_indicator_v2: ^0.5.0
cached_network_image: ^3.3.1 cached_network_image: ^3.3.1
flutter_local_notifications: ^17.2.1+2 flutter_local_notifications: ^17.2.1+2
flutter_launcher_icons: "^0.13.1"
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -54,6 +55,12 @@ dev_dependencies:
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^4.0.0 flutter_lints: ^4.0.0
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "images/nutrify.png"
min_sdk_android: 21 # android min sdk min:16, default 21
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
@ -66,12 +73,10 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- images/ahri_coven.gif
- images/ahri_ori.gif
- images/ahri_icon.jpg - images/ahri_icon.jpg
- images/nature.gif - images/nature.gif
- images/arga.jpg - images/arga.jpg
- images/hidroponik.png - images/nutrify.png
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware # https://flutter.dev/assets-and-images/#resolution-aware

93
test_api/data_chart.php Normal file
View File

@ -0,0 +1,93 @@
<?php
// Koneksi ke database
$conn = mysqli_connect("localhost", "root", "", "tugas_akhir");
// Periksa koneksi
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Mendapatkan tanggal mulai dan akhir dari permintaan
$startDate = isset($_GET['start_date']) ? $_GET['start_date'] : null;
$endDate = isset($_GET['end_date']) ? $_GET['end_date'] : null;
// Menyiapkan klausa WHERE untuk filter tanggal
$whereClause = "";
if ($startDate && $endDate) {
$whereClause = " WHERE timestamp BETWEEN '$startDate' AND '$endDate'";
}
$data = array();
$data1 = array();
$data2 = array();
$data3 = array();
// Mengambil data dari tabel tb_waterflow1 termasuk kolom timestamp dan id
$sql = "SELECT id_wf1 as x, timestamp, waterflow1_value as y1 FROM tb_waterflow1 $whereClause";
$result = $conn->query($sql);
if ($result) {
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$data[] = $row;
}
}
} else {
echo "Error: " . $conn->error;
}
// Mengambil data dari tabel tb_waterflow2 termasuk kolom timestamp dan id
$sql1 = "SELECT id_wf2 as x, timestamp, waterflow2_value as y2 FROM tb_waterflow2 $whereClause";
$result1 = $conn->query($sql1);
if ($result1) {
if ($result1->num_rows > 0) {
while ($row = $result1->fetch_assoc()) {
$data1[] = $row;
}
}
} else {
echo "Error: " . $conn->error;
}
// Mengambil data dari tabel tb_ppm termasuk kolom timestamp dan id
$sql2 = "SELECT id_ppm as x, timestamp, ppm_value as y3 FROM tb_ppm $whereClause";
$result2 = $conn->query($sql2);
if ($result2) {
if ($result2->num_rows > 0) {
while ($row = $result2->fetch_assoc()) {
$data2[] = $row;
}
}
} else {
echo "Error: " . $conn->error;
}
// Mengambil data dari tabel tb_waterlevel termasuk kolom timestamp dan id
$sql3 = "SELECT id_wl as x, timestamp, ultrasonic_value as y FROM tb_waterlevel $whereClause";
$result3 = $conn->query($sql3);
if ($result3) {
if ($result3->num_rows > 0) {
while ($row = $result3->fetch_assoc()) {
$data3[] = $row;
}
}
} else {
echo "Error: " . $conn->error;
}
// Tutup koneksi
$conn->close();
// Mengatur header untuk respon JSON
header('Content-Type: application/json');
// Mengirimkan data sebagai JSON
echo json_encode(array(
'tb_waterflow1' => $data,
'tb_waterflow2' => $data1,
'tb_ppm' => $data2,
'tb_waterlevel' => $data3
));

42
test_api/data_plant.php Normal file
View File

@ -0,0 +1,42 @@
<?php
// Koneksi ke database MySQL
$conn = mysqli_connect("localhost", "root", "", "tugas_akhir");
// Periksa koneksi
if (!$conn) {
die("Koneksi ke database gagal: " . mysqli_connect_error());
}
// Kueri untuk mengambil data tanaman
$sql = "SELECT id_plant, title, desk, kategori, img, date FROM tb_plant";
// Jalankan kueri SQL
$result = mysqli_query($conn, $sql);
// Inisialisasi array untuk menyimpan data tanaman
$plants = array();
// Base URL untuk gambar (sesuaikan dengan alamat server Anda)
$base_url = 'http://' . $_SERVER['SERVER_ADDR'] . '/Upload/uploads/';
// Periksa apakah kueri berhasil dijalankan
if (mysqli_num_rows($result) > 0) {
// Ambil setiap baris data tanaman dan tambahkan ke array
while ($row = mysqli_fetch_assoc($result)) {
// Tambahkan URL penuh untuk gambar
$row['img'] = $base_url . $row['img'];
$plants[] = $row;
}
}
// Konversi array ke format JSON
$json_response = json_encode($plants);
// Set header untuk respon JSON
header('Content-Type: application/json');
// Tampilkan respon JSON
echo $json_response;
// Tutup koneksi database
mysqli_close($conn);

27
test_api/dbconnection.php Normal file
View File

@ -0,0 +1,27 @@
<!-- <?php
function dbconnection()
{
$con = mysqli_connect("localhost", "root", "", "tugas_akhir");
return $con;
}
?> -->
<!-- <?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "tugas_akhir";
// Create connection
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
?> -->
<?php
$con = mysqli_connect('localhost', 'root', '', 'tugas_akhir') or die('tidak terkoneksi');
?>

43
test_api/get_data.php Normal file
View File

@ -0,0 +1,43 @@
<?php
// Koneksi ke database MySQL
$conn = mysqli_connect("localhost", "root", "", "tugas_akhir");
// Periksa koneksi
if (!$conn) {
die("Koneksi ke database gagal: " . mysqli_connect_error());
}
// Periksa apakah parameter username diberikan
if (isset($_GET['username'])) {
$username = $_GET['username'];
$sql = "SELECT username, fullname FROM tb_login WHERE username = '$username'";
} else {
// Tidak ada username, kembalikan semua data pengguna
$sql = "SELECT username, fullname FROM tb_login";
}
// Jalankan kueri SQL
$result = mysqli_query($conn, $sql);
// Inisialisasi array untuk menyimpan data pengguna
$users = array();
// Periksa apakah kueri berhasil dijalankan
if (mysqli_num_rows($result) > 0) {
// Ambil setiap baris data pengguna dan tambahkan ke array
while ($row = mysqli_fetch_assoc($result)) {
$users[] = $row;
}
}
// Konversi array ke format JSON
$json_response = json_encode($users);
// Set header untuk respon JSON
header('Content-Type: application/json');
// Tampilkan respon JSON
echo $json_response;
// Tutup koneksi database
mysqli_close($conn);

44
test_api/login.php Normal file
View File

@ -0,0 +1,44 @@
<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
$conn = new mysqli("localhost", "root", "", "tugas_akhir");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$username = $_POST['username'];
$password = $_POST['password'];
// Query untuk mengambil hash password berdasarkan username
$query = $conn->prepare("SELECT * FROM tb_login WHERE username=?");
$query->bind_param("s", $username);
$query->execute();
$result = $query->get_result();
if ($result->num_rows == 1) {
$data = $result->fetch_assoc();
$hashedPassword = $data['password'];
// Verifikasi password
if (password_verify($password, $hashedPassword)) {
$token = bin2hex(random_bytes(10)); // Generate a random token with 10 bytes
// Simpan token ke database
$updateQuery = $conn->prepare("UPDATE tb_login SET token=? WHERE username=?");
$updateQuery->bind_param("ss", $token, $username);
if ($updateQuery->execute()) {
$data['token'] = $token;
echo json_encode(array("success" => true, "data" => $data));
} else {
echo json_encode(array("success" => false, "message" => "Failed to update token"));
}
} else {
echo json_encode(array("success" => false, "message" => "Invalid username or password."));
}
} else {
echo json_encode(array("success" => false, "message" => "Invalid username or password."));
}
$conn->close();

21
test_api/logout.php Normal file
View File

@ -0,0 +1,21 @@
<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
$conn = new mysqli("localhost", "root", "", "tugas_akhir");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$token = $_POST['token'];
$query = "UPDATE tb_login SET token='' WHERE token='$token'";
if (mysqli_query($conn, $query)) {
echo json_encode(["success" => true]);
} else {
echo json_encode(["error" => "Failed to update token."]);
}
$conn->close();

57
test_api/register.php Normal file
View File

@ -0,0 +1,57 @@
<?php
header("Content-Type: application/json; charset=UTF-8");
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "tugas_akhir";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
echo json_encode(array("status" => "error", "message" => "Connection failed: " . $conn->connect_error));
exit();
}
// Get POST data
$data = json_decode(file_get_contents("php://input"), true);
$username = $data['username'];
$password = $data['password'];
$fullname = $data['fullname'];
// Validate input data
if (empty($username) || empty($password) || empty($fullname)) {
echo json_encode(array("status" => "error", "message" => "All fields are required."));
exit();
}
// Check if username already exists
$sql = "SELECT * FROM tb_login WHERE username = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo json_encode(array("status" => "error", "message" => "Username already exists."));
exit();
}
// Hash the password
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Insert new user
$sql = "INSERT INTO tb_login (username, password, fullname) VALUES (?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("sss", $username, $hashed_password, $fullname);
if ($stmt->execute()) {
echo json_encode(array("status" => "success", "message" => "Registration successful."));
} else {
echo json_encode(array("status" => "error", "message" => "Registration failed."));
}
// Close connections
$stmt->close();
$conn->close();

48
test_api/save_msg.php Normal file
View File

@ -0,0 +1,48 @@
<?php
header('Content-Type: application/json');
// Database connection
$host = 'localhost';
$db = 'tugas_akhir';
$user = 'root';
$pass = '';
$conn = new mysqli($host, $user, $pass, $db);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Get JSON input
$input = file_get_contents('php://input');
$data = json_decode($input, true);
$topic = $data['topic'];
$value = $data['value'];
$speed = isset($data['speed']) ? $data['speed'] : null;
if ($topic == 'sensor/waterflow1') {
$stmt = $conn->prepare("INSERT INTO tb_waterflow1 (timestamp, waterflow1_value, waterflow1_speed) VALUES (NOW(), ?, ?)");
$stmt->bind_param("dd", $value, $speed);
} else if ($topic == 'sensor/waterflow2') {
$stmt = $conn->prepare("INSERT INTO tb_waterflow2 (timestamp, waterflow2_value, waterflow2_speed) VALUES (NOW(), ?, ?)");
$stmt->bind_param("dd", $value, $speed);
} else if ($topic == 'sensor/tds') {
$stmt = $conn->prepare("INSERT INTO tb_ppm (timestamp, ppm_value) VALUES (NOW(), ?)");
$stmt->bind_param("d", $value);
} else if ($topic == 'sensor/ultrasonic') {
$stmt = $conn->prepare("INSERT INTO tb_waterlevel (timestamp, ultrasonic_value) VALUES (NOW(), ?)");
$stmt->bind_param("d", $value);
} else {
echo json_encode(["status" => "error", "message" => "Unknown topic"]);
exit;
}
if ($stmt->execute()) {
echo json_encode(["status" => "success"]);
} else {
echo json_encode(["status" => "error", "message" => $stmt->error]);
}
$stmt->close();
$conn->close();

BIN
upload-keystore.jks Normal file

Binary file not shown.