Perubahan Full
|
@ -22,8 +22,14 @@ if (flutterVersionName == null) {
|
|||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "com.example.beta_app1"
|
||||
namespace "com.nutrify.app"
|
||||
compileSdk flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
|
@ -42,7 +48,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
// 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.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
|
@ -51,13 +57,17 @@ android {
|
|||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<application
|
||||
android:label="Nutrify"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.example.beta_app1
|
||||
package com.nutrify.app
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
|
|
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 5.1 KiB |
BIN
flutter_01.png
Before Width: | Height: | Size: 2.9 MiB |
After Width: | Height: | Size: 15 KiB |
|
@ -427,7 +427,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
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_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
@ -484,7 +484,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
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_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 978 B |
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 654 B |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 978 B |
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.5 KiB |
|
@ -34,7 +34,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
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';
|
||||
|
||||
final response = await http.post(
|
||||
|
@ -128,7 +128,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
"images/nature.gif",
|
||||
"images/nutrify.png",
|
||||
height: 200,
|
||||
width: 200,
|
||||
),
|
||||
|
@ -216,31 +216,41 @@ class _LoginPageState extends State<LoginPage> {
|
|||
),
|
||||
child: const Text(
|
||||
'Login',
|
||||
style: TextStyle(color: Colors.white),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RegisterPage()),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
const Color.fromARGB(255, 255, 255, 255),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
),
|
||||
child: const Text(
|
||||
'Register',
|
||||
style: TextStyle(
|
||||
color: Color.fromARGB(166, 26, 21, 21)),
|
||||
const SizedBox(height: 25),
|
||||
Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'Belum memiliki akun?',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RegisterPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text(
|
||||
' Register',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -259,7 +269,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
const Text(
|
||||
"@2024",
|
||||
style: TextStyle(color: Colors.black, fontSize: 10.0),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'login.dart';
|
||||
|
@ -53,35 +54,55 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||
});
|
||||
}
|
||||
|
||||
// Validasi fullname (opsional)
|
||||
|
||||
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';
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse(url),
|
||||
body: {
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: jsonEncode({
|
||||
"username": username,
|
||||
"password": password,
|
||||
"fullname": fullname,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Akun berhasil dibuat'),
|
||||
duration: Duration(seconds: 2),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const LoginPage()),
|
||||
);
|
||||
final responseData = jsonDecode(response.body);
|
||||
if (responseData['status'] == 'success') {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Akun berhasil dibuat'),
|
||||
duration: Duration(seconds: 2),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
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 {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Halaman Registrasi'),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child:
|
||||
Image.asset("images/nature.gif", height: 200, width: 200),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
backgroundColor: Colors.white,
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
"images/nutrify.png",
|
||||
height: 200,
|
||||
width: 200,
|
||||
),
|
||||
elevation: 8,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Center(
|
||||
child: Text(
|
||||
'Register',
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(150, 0, 0, 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
elevation: 8,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Center(
|
||||
child: Text(
|
||||
usernameError,
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 12.0,
|
||||
'Register',
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(150, 0, 0, 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
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)),
|
||||
),
|
||||
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,
|
||||
hintText: 'Username',
|
||||
hintStyle: TextStyle(
|
||||
color: const Color.fromARGB(92, 0, 0, 0)
|
||||
.withOpacity(0.25),
|
||||
fontSize: 14.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: registerUser,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
if (usernameError.isNotEmpty)
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 8.0, left: 12.0),
|
||||
child: Text(
|
||||
usernameError,
|
||||
style: const TextStyle(
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -258,8 +258,8 @@ class _ChartsSectionState extends State<ChartsSection> {
|
|||
),
|
||||
primaryYAxis: NumericAxis(
|
||||
minimum: 0,
|
||||
maximum: 20,
|
||||
interval: 5,
|
||||
maximum: 1000,
|
||||
interval: 200,
|
||||
majorGridLines: MajorGridLines(width: 0.5, color: Colors.grey[300]),
|
||||
axisLine: AxisLine(width: 0),
|
||||
labelStyle: TextStyle(color: Colors.grey[700], fontSize: 12),
|
||||
|
|
|
@ -33,7 +33,7 @@ class _SplashScreenState extends State<SplashScreen> {
|
|||
backgroundColor:
|
||||
Colors.white, // Ubah sesuai warna latar belakang yang diinginkan
|
||||
body: Center(
|
||||
child: Image.asset('images/nature.gif',
|
||||
child: Image.asset('images/nutrify.png',
|
||||
width: 200,
|
||||
height:
|
||||
200), // Pastikan untuk menambahkan logo Anda di folder assets
|
||||
|
|
|
@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
|
|||
set(BINARY_NAME "beta_app1")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# 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
|
||||
# versions of CMake.
|
||||
|
|
56
pubspec.lock
|
@ -1,6 +1,14 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -65,6 +73,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -166,6 +190,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -232,6 +264,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -240,6 +280,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -661,6 +709,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.3.4 <4.0.0"
|
||||
flutter: ">=3.19.0"
|
||||
|
|
11
pubspec.yaml
|
@ -42,6 +42,7 @@ dependencies:
|
|||
liquid_progress_indicator_v2: ^0.5.0
|
||||
cached_network_image: ^3.3.1
|
||||
flutter_local_notifications: ^17.2.1+2
|
||||
flutter_launcher_icons: "^0.13.1"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -54,6 +55,12 @@ dev_dependencies:
|
|||
# rules and activating additional ones.
|
||||
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
|
||||
# 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:
|
||||
assets:
|
||||
- images/ahri_coven.gif
|
||||
- images/ahri_ori.gif
|
||||
- images/ahri_icon.jpg
|
||||
- images/nature.gif
|
||||
- images/arga.jpg
|
||||
- images/hidroponik.png
|
||||
- images/nutrify.png
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
|
|
@ -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
|
||||
));
|
|
@ -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);
|
|
@ -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');
|
||||
?>
|
|
@ -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);
|
|
@ -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();
|
|
@ -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();
|
|
@ -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();
|
|
@ -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();
|