Upload proyek monitoring pengering kopi IoT
This commit is contained in:
parent
1cd4bb22ee
commit
1908432345
|
|
@ -1,3 +1,9 @@
|
|||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
|
|
@ -6,47 +12,27 @@ if (localPropertiesFile.exists()) {
|
|||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') ?: '1'
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName') ?: '1.0'
|
||||
|
||||
android {
|
||||
namespace "com.example.coffee_iot_flutter"
|
||||
compileSdkVersion 34
|
||||
compileSdk 35
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
jvmTarget = '17'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.example.coffee_iot_flutter"
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
targetSdkVersion 34
|
||||
minSdk flutter.minSdkVersion
|
||||
targetSdk 35
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
|
@ -58,10 +44,15 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
// Force pin versi androidx agar kompatibel AGP 8.6.1
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force 'androidx.browser:browser:1.8.0'
|
||||
force 'androidx.core:core:1.15.0'
|
||||
force 'androidx.core:core-ktx:1.15.0'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
|
@ -1,16 +1,3 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.8.22'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
|
|
@ -18,14 +5,16 @@ allprojects {
|
|||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
rootProject.buildDir = "../build"
|
||||
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
|
|
@ -1,17 +1,25 @@
|
|||
include ':app'
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader("UTF-8") { reader ->
|
||||
properties.load(reader)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
if (flutterSdkPath == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
plugins {
|
||||
id "com.android.application" version "8.6.1" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
}
|
||||
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
include ":app"
|
||||
|
|
@ -14,7 +14,7 @@ void main() async {
|
|||
// Initialize Supabase with the provided URL and Publishable Key
|
||||
await Supabase.initialize(
|
||||
url: 'https://ddmhzzegejbsshihzext.supabase.co',
|
||||
anonKey: 'sb_publishable_IJJ8fXD9CXfjMeu8vNJ8bw_NhWyTJKy',
|
||||
anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRkbWh6emVnZWpic3NoaWh6ZXh0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzcyMjIyNzAsImV4cCI6MjA5Mjc5ODI3MH0._r-viIQLMXuNNdg4tcn7sJTBO9QqSpSlF7hk43hu2Ks',
|
||||
);
|
||||
|
||||
runApp(
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ class _AnalyticsScreenState extends State<AnalyticsScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final args = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
|
||||
final String title = args['title'];
|
||||
final Color color = args['color'];
|
||||
final args = (ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?) ?? {};
|
||||
final String title = args['title'] ?? 'Grafik';
|
||||
final Color color = args['color'] ?? Colors.green;
|
||||
final String sensorKey = title.contains('Suhu') ? 'suhu' : title.contains('Kelembaban') ? 'kelembapan' : 'intensitas';
|
||||
|
||||
return Scaffold(
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ class ControlSettingsScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
||||
int _operatingMode = 1; // 0 for Auto, 1 for Manual
|
||||
int _operatingMode = 1; // 0 = otomatis, 1 = manual
|
||||
final TextEditingController _tempMinController = TextEditingController();
|
||||
final TextEditingController _tempMaxController = TextEditingController();
|
||||
final TextEditingController _humMinController = TextEditingController();
|
||||
final TextEditingController _humMaxController = TextEditingController();
|
||||
final TextEditingController _humMinController = TextEditingController();
|
||||
final TextEditingController _humMaxController = TextEditingController();
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
|
|
@ -25,68 +25,107 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
|||
_loadSettings();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tempMinController.dispose();
|
||||
_tempMaxController.dispose();
|
||||
_humMinController.dispose();
|
||||
_humMaxController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final data = await SupabaseService().getBatasSensor();
|
||||
if (data != null) {
|
||||
final data = await SupabaseService().getBatasSensor();
|
||||
if (data != null && mounted) {
|
||||
setState(() {
|
||||
_tempMinController.text = data['suhu_min'].toString();
|
||||
_tempMaxController.text = data['suhu_max'].toString();
|
||||
_humMinController.text = data['rh_min'].toString();
|
||||
_humMaxController.text = data['rh_max'].toString();
|
||||
// Load last saved mode from local memory
|
||||
_humMinController.text = data['rh_min'].toString();
|
||||
_humMaxController.text = data['rh_max'].toString();
|
||||
_operatingMode = prefs.getInt('op_mode') ?? 1;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error loading settings: $e');
|
||||
setState(() => _isLoading = false);
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
void _saveSettings() async {
|
||||
// 1. Validation: Prevent empty inputs
|
||||
if (_tempMinController.text.isEmpty || _tempMaxController.text.isEmpty ||
|
||||
_humMinController.text.isEmpty || _humMaxController.text.isEmpty) {
|
||||
Future<void> _saveSettings() async {
|
||||
if (_tempMinController.text.isEmpty ||
|
||||
_tempMaxController.text.isEmpty ||
|
||||
_humMinController.text.isEmpty ||
|
||||
_humMaxController.text.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Semua kolom batas harus diisi!'), backgroundColor: Colors.orange),
|
||||
const SnackBar(
|
||||
content: Text('Semua kolom batas harus diisi!'),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final mqtt = Provider.of<MqttService>(context, listen: false);
|
||||
final suhuMin = double.tryParse(_tempMinController.text) ?? 30;
|
||||
final suhuMax = double.tryParse(_tempMaxController.text) ?? 45;
|
||||
final rhMin = double.tryParse(_humMinController.text) ?? 60;
|
||||
final rhMax = double.tryParse(_humMaxController.text) ?? 70;
|
||||
|
||||
// Validasi logika
|
||||
if (suhuMin >= suhuMax) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Suhu min harus lebih kecil dari suhu max!'),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (rhMin >= rhMax) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('RH min harus lebih kecil dari RH max!'),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final mqtt = Provider.of<MqttService>(context, listen: false);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
try {
|
||||
// 2. Save Mode to Local Memory
|
||||
// Simpan mode ke local storage
|
||||
await prefs.setInt('op_mode', _operatingMode);
|
||||
|
||||
// 3. MQTT Publish
|
||||
mqtt.publish('coffee/config/temp_max', _tempMaxController.text);
|
||||
mqtt.publish('coffee/config/hum_max', _humMaxController.text);
|
||||
mqtt.publish('coffee/config/mode', _operatingMode == 0 ? "AUTO" : "MANUAL");
|
||||
// Kirim batas sensor ke ESP32 via MQTT → Flow 5 simpan ke Supabase
|
||||
mqtt.setBatasSensor(suhuMin, suhuMax, rhMin, rhMax);
|
||||
|
||||
// Kirim mode ke ESP32
|
||||
mqtt.gantiMode(_operatingMode == 0 ? "otomatis" : "manual");
|
||||
|
||||
// Update Supabase langsung juga sebagai backup
|
||||
await SupabaseService().updateBatasSensor(suhuMin, suhuMax, rhMin, rhMax);
|
||||
|
||||
// 4. Supabase Update
|
||||
await SupabaseService().updateBatasSensor(
|
||||
double.parse(_tempMinController.text),
|
||||
double.parse(_tempMaxController.text),
|
||||
double.parse(_humMinController.text),
|
||||
double.parse(_humMaxController.text),
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('✅ Pengaturan Tersimpan & Terkirim')),
|
||||
const SnackBar(
|
||||
content: Text('✅ Pengaturan Tersimpan & Terkirim ke ESP32'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('❌ Gagal: $e'), backgroundColor: Colors.red),
|
||||
SnackBar(
|
||||
content: Text('❌ Gagal: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -99,84 +138,133 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
|||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: _isLoading
|
||||
? const Center(child: CircularProgressIndicator(color: Colors.green))
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildInputCard('Suhu Min (°C)', '32', _tempMinController)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: _buildInputCard('Suhu Max (°C)', '48', _tempMaxController)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildInputCard('Kelembaban Min (%)', '65', _humMinController)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: _buildInputCard('Kelembaban Max (%)', '75', _humMaxController)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1A1A),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: _isLoading
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(color: Colors.green))
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Mode Operasi', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
RadioListTile<int>(
|
||||
value: 0,
|
||||
groupValue: _operatingMode,
|
||||
onChanged: (val) => setState(() => _operatingMode = val!),
|
||||
title: const Text('Mode Otomatis', style: TextStyle(color: Colors.white)),
|
||||
activeColor: Colors.green,
|
||||
subtitle: const Text('Kipas bekerja otomatis berdasarkan sensor', style: TextStyle(color: Colors.grey, fontSize: 10)),
|
||||
const Text(
|
||||
'Pengaturan',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
RadioListTile<int>(
|
||||
value: 1,
|
||||
groupValue: _operatingMode,
|
||||
onChanged: (val) => setState(() => _operatingMode = val!),
|
||||
title: const Text('Mode Manual', style: TextStyle(color: Colors.white)),
|
||||
activeColor: Colors.green,
|
||||
subtitle: const Text('Kontrol penuh kipas melalui dashboard HP', style: TextStyle(color: Colors.grey, fontSize: 10)),
|
||||
const Text(
|
||||
'Batas Sensor & Mode Operasi',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
// ── Batas Suhu ──
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildInputCard(
|
||||
'Suhu Min (°C)', '30', _tempMinController)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: _buildInputCard(
|
||||
'Suhu Max (°C)', '45', _tempMaxController)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
// ── Batas RH ──
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildInputCard(
|
||||
'RH Min (%)', '60', _humMinController)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: _buildInputCard(
|
||||
'RH Max (%)', '70', _humMaxController)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// ── Mode Operasi ──
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1A1A),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Mode Operasi',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16)),
|
||||
const SizedBox(height: 10),
|
||||
RadioListTile<int>(
|
||||
value: 0,
|
||||
groupValue: _operatingMode,
|
||||
onChanged: (val) =>
|
||||
setState(() => _operatingMode = val!),
|
||||
title: const Text('Mode Otomatis',
|
||||
style: TextStyle(color: Colors.white)),
|
||||
activeColor: Colors.green,
|
||||
subtitle: const Text(
|
||||
'Kipas bekerja otomatis berdasarkan sensor',
|
||||
style: TextStyle(
|
||||
color: Colors.grey, fontSize: 10),
|
||||
),
|
||||
),
|
||||
RadioListTile<int>(
|
||||
value: 1,
|
||||
groupValue: _operatingMode,
|
||||
onChanged: (val) =>
|
||||
setState(() => _operatingMode = val!),
|
||||
title: const Text('Mode Manual',
|
||||
style: TextStyle(color: Colors.white)),
|
||||
activeColor: Colors.green,
|
||||
subtitle: const Text(
|
||||
'Kontrol penuh kipas melalui dashboard HP',
|
||||
style: TextStyle(
|
||||
color: Colors.grey, fontSize: 10),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// ── Tombol Simpan ──
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 55,
|
||||
child: ElevatedButton(
|
||||
onPressed: _saveSettings,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Simpan & Kirim ke ESP32',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 100),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 55,
|
||||
child: ElevatedButton(
|
||||
onPressed: _saveSettings,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
),
|
||||
child: const Text('Simpan Pengaturan', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 100),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInputCard(String title, String hint, TextEditingController controller) {
|
||||
Widget _buildInputCard(
|
||||
String title, String hint, TextEditingController controller) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -186,7 +274,11 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: const TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold)),
|
||||
Text(title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: controller,
|
||||
|
|
@ -208,4 +300,4 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,14 +46,34 @@ class ImageResultScreen extends StatelessWidget {
|
|||
color: const Color(0xFF1A1A1A),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.image, size: 80, color: Colors.grey),
|
||||
const SizedBox(height: 10),
|
||||
Text('Citra #${args['id']}', style: const TextStyle(color: Colors.grey)),
|
||||
],
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: args['url_foto'] != null && args['url_foto'].toString().isNotEmpty
|
||||
? Image.network(
|
||||
args['url_foto'],
|
||||
fit: BoxFit.cover,
|
||||
loadingBuilder: (context, child, progress) {
|
||||
if (progress == null) return child;
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(color: Colors.green),
|
||||
);
|
||||
},
|
||||
errorBuilder: (context, error, stack) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.broken_image, size: 80, color: Colors.grey),
|
||||
Text('Citra #${args['id']}',
|
||||
style: const TextStyle(color: Colors.grey)),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.image, size: 80, color: Colors.grey),
|
||||
Text('Citra #${args['id']}',
|
||||
style: const TextStyle(color: Colors.grey)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
bool _isLoading = false;
|
||||
bool _obscurePassword = true; // Tambahan: State untuk mata password
|
||||
|
||||
Future<void> _handleLogin() async {
|
||||
setState(() => _isLoading = true);
|
||||
|
|
@ -80,13 +81,24 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
const Divider(color: Colors.grey),
|
||||
TextField(
|
||||
controller: _passwordController,
|
||||
obscureText: true,
|
||||
obscureText: _obscurePassword, // Menggunakan variabel state
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: Icon(Icons.lock, color: Colors.green),
|
||||
suffixIcon: Icon(Icons.visibility, color: Colors.grey),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.lock, color: Colors.green),
|
||||
// Menggunakan IconButton untuk aksi klik
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscurePassword ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
hintText: 'Password',
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
hintStyle: const TextStyle(color: Colors.grey),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
|
|
@ -127,4 +139,4 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,30 +23,9 @@ class _MonitoringScreenState extends State<MonitoringScreen> {
|
|||
Future<void> _initData() async {
|
||||
setState(() => _isInitialLoading = true);
|
||||
await _loadLimits();
|
||||
await _loadLastSensorData();
|
||||
if (mounted) setState(() => _isInitialLoading = false);
|
||||
}
|
||||
|
||||
Future<void> _loadLastSensorData() async {
|
||||
try {
|
||||
final lastLog = await SupabaseService().getLatestLog();
|
||||
if (lastLog != null && mounted) {
|
||||
final mqtt = Provider.of<MqttService>(context, listen: false);
|
||||
// Update local MQTT service state with last DB values if currently "0" or default
|
||||
// This ensures the user sees the last known data immediately
|
||||
mqtt.updateFromLastLog(
|
||||
lastLog['suhu'].toString(),
|
||||
lastLog['kelembapan'].toString(),
|
||||
lastLog['intensitas'].toString(),
|
||||
lastLog['kipas1'] == 'ON',
|
||||
lastLog['kipas2'] == 'ON'
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error loading last log: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadLimits() async {
|
||||
try {
|
||||
final data = await SupabaseService().getBatasSensor();
|
||||
|
|
@ -64,180 +43,299 @@ class _MonitoringScreenState extends State<MonitoringScreen> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mqtt = Provider.of<MqttService>(context);
|
||||
final curTemp = double.tryParse(mqtt.temp) ?? 0;
|
||||
final curHum = double.tryParse(mqtt.humidity) ?? 0;
|
||||
final curTemp = double.tryParse(mqtt.suhu) ?? 0;
|
||||
final curHum = double.tryParse(mqtt.kelembapan) ?? 0;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: _isInitialLoading
|
||||
? const Center(child: CircularProgressIndicator(color: Colors.green))
|
||||
: RefreshIndicator(
|
||||
onRefresh: _initData,
|
||||
color: Colors.green,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Monitoring',
|
||||
style: TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Text(
|
||||
'Sistem Pengeringan Kopi',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: mqtt.client?.connectionStatus?.state == MqttConnectionState.connected
|
||||
? Colors.green : Colors.red,
|
||||
child: _isInitialLoading
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(color: Colors.green))
|
||||
: RefreshIndicator(
|
||||
onRefresh: _initData,
|
||||
color: Colors.green,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// ── Header ──
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Monitoring',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
mqtt.client?.connectionStatus?.state == MqttConnectionState.connected
|
||||
? 'Connected' : 'Disconnected',
|
||||
style: TextStyle(
|
||||
color: mqtt.client?.connectionStatus?.state == MqttConnectionState.connected
|
||||
? Colors.green : Colors.red,
|
||||
fontSize: 12,
|
||||
const Text(
|
||||
'Sistem Pengeringan Kopi',
|
||||
style: TextStyle(
|
||||
color: Colors.grey, fontSize: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const CircleAvatar(
|
||||
backgroundColor: Color(0xFF1A1A1A),
|
||||
radius: 25,
|
||||
child: Icon(Icons.person, color: Colors.green),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 15,
|
||||
mainAxisSpacing: 15,
|
||||
children: [
|
||||
_buildSensorCard(
|
||||
'Suhu',
|
||||
'${mqtt.temp}°C',
|
||||
Icons.thermostat,
|
||||
curTemp > mqtt.maxTemp ? Colors.red : Colors.redAccent,
|
||||
isAlert: curTemp > mqtt.maxTemp,
|
||||
onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Suhu (°C)', 'color': Colors.redAccent, 'value': '${mqtt.temp}°C'}),
|
||||
),
|
||||
_buildSensorCard(
|
||||
'Kelembaban',
|
||||
'${mqtt.humidity}%',
|
||||
Icons.water_drop,
|
||||
curHum > mqtt.maxHum ? Colors.red : Colors.cyan,
|
||||
isAlert: curHum > mqtt.maxHum,
|
||||
onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Kelembaban (%)', 'color': Colors.cyan, 'value': '${mqtt.humidity}%'}),
|
||||
),
|
||||
_buildSensorCard(
|
||||
'Cahaya',
|
||||
'${mqtt.light} Lux',
|
||||
Icons.wb_sunny,
|
||||
Colors.orange,
|
||||
onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Cahaya (Lux)', 'color': Colors.orange, 'value': '${mqtt.light} Lux'}),
|
||||
),
|
||||
_buildControlCard(
|
||||
'Intake',
|
||||
mqtt.isIntakeOn,
|
||||
(val) async {
|
||||
final status = val ? 'ON' : 'OFF';
|
||||
mqtt.publish('coffee/intake', status);
|
||||
await SupabaseService().logFanAction(status, mqtt.isExhaustOn ? 'ON' : 'OFF', curTemp, curHum, double.tryParse(mqtt.light) ?? 0);
|
||||
},
|
||||
mqtt,
|
||||
),
|
||||
_buildControlCard(
|
||||
'Exhaust',
|
||||
mqtt.isExhaustOn,
|
||||
(val) async {
|
||||
final status = val ? 'ON' : 'OFF';
|
||||
mqtt.publish('coffee/exhaust', status);
|
||||
await SupabaseService().logFanAction(mqtt.isIntakeOn ? 'ON' : 'OFF', status, curTemp, curHum, double.tryParse(mqtt.light) ?? 0);
|
||||
},
|
||||
mqtt,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (curTemp > mqtt.maxTemp || curHum > mqtt.maxHum)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.red),
|
||||
),
|
||||
child: Row(
|
||||
children: const [
|
||||
Icon(Icons.warning_amber_rounded, color: Colors.red),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Peringatan: Parameter pengeringan melewati batas aman!',
|
||||
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold, fontSize: 12),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: mqtt.client?.connectionStatus
|
||||
?.state ==
|
||||
MqttConnectionState.connected
|
||||
? Colors.green
|
||||
: Colors.red,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
mqtt.client?.connectionStatus?.state ==
|
||||
MqttConnectionState.connected
|
||||
? 'Connected'
|
||||
: 'Disconnected',
|
||||
style: TextStyle(
|
||||
color: mqtt.client?.connectionStatus
|
||||
?.state ==
|
||||
MqttConnectionState.connected
|
||||
? Colors.green
|
||||
: Colors.red,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const CircleAvatar(
|
||||
backgroundColor: Color(0xFF1A1A1A),
|
||||
radius: 25,
|
||||
child:
|
||||
Icon(Icons.person, color: Colors.green),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// ── Timestamp ──
|
||||
Text(
|
||||
'Update: ${mqtt.timestamp}',
|
||||
style: const TextStyle(
|
||||
color: Colors.grey, fontSize: 11),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// ── Mode Badge ──
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: mqtt.mode == 'manual'
|
||||
? Colors.orange.withOpacity(0.2)
|
||||
: Colors.green.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: mqtt.mode == 'manual'
|
||||
? Colors.orange
|
||||
: Colors.green,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
'Mode: ${mqtt.mode.toUpperCase()}',
|
||||
style: TextStyle(
|
||||
color: mqtt.mode == 'manual'
|
||||
? Colors.orange
|
||||
: Colors.green,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// ── Grid Sensor + Kontrol ──
|
||||
GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 15,
|
||||
mainAxisSpacing: 15,
|
||||
children: [
|
||||
_buildSensorCard(
|
||||
'Suhu',
|
||||
'${mqtt.suhu}°C',
|
||||
Icons.thermostat,
|
||||
curTemp > mqtt.maxSuhu
|
||||
? Colors.red
|
||||
: Colors.redAccent,
|
||||
isAlert: curTemp > mqtt.maxSuhu,
|
||||
onTap: () => Navigator.pushNamed(
|
||||
context,
|
||||
'/analytics',
|
||||
arguments: {
|
||||
'title': 'Grafik Suhu (°C)',
|
||||
'color': Colors.redAccent,
|
||||
'value': '${mqtt.suhu}°C',
|
||||
},
|
||||
),
|
||||
),
|
||||
_buildSensorCard(
|
||||
'Kelembaban',
|
||||
'${mqtt.kelembapan}%',
|
||||
Icons.water_drop,
|
||||
curHum > mqtt.maxRh
|
||||
? Colors.red
|
||||
: Colors.cyan,
|
||||
isAlert: curHum > mqtt.maxRh,
|
||||
onTap: () => Navigator.pushNamed(
|
||||
context,
|
||||
'/analytics',
|
||||
arguments: {
|
||||
'title': 'Grafik Kelembaban (%)',
|
||||
'color': Colors.cyan,
|
||||
'value': '${mqtt.kelembapan}%',
|
||||
},
|
||||
),
|
||||
),
|
||||
_buildSensorCard(
|
||||
'Cahaya',
|
||||
'${mqtt.intensitas} Lux',
|
||||
Icons.wb_sunny,
|
||||
Colors.orange,
|
||||
onTap: () => Navigator.pushNamed(
|
||||
context,
|
||||
'/analytics',
|
||||
arguments: {
|
||||
'title': 'Grafik Cahaya (Lux)',
|
||||
'color': Colors.orange,
|
||||
'value': '${mqtt.intensitas} Lux',
|
||||
},
|
||||
),
|
||||
),
|
||||
_buildControlCard(
|
||||
'Kipas 1 (Exhaust)',
|
||||
mqtt.isKipas1On,
|
||||
(val) async {
|
||||
mqtt.perintahKipas("1", val);
|
||||
await SupabaseService().logFanAction(
|
||||
val ? 'ON' : 'OFF',
|
||||
mqtt.isKipas2On ? 'ON' : 'OFF',
|
||||
curTemp,
|
||||
curHum,
|
||||
double.tryParse(mqtt.intensitas) ?? 0,
|
||||
);
|
||||
},
|
||||
mqtt,
|
||||
),
|
||||
_buildControlCard(
|
||||
'Kipas 2 (Intake)',
|
||||
mqtt.isKipas2On,
|
||||
(val) async {
|
||||
mqtt.perintahKipas("2", val);
|
||||
await SupabaseService().logFanAction(
|
||||
mqtt.isKipas1On ? 'ON' : 'OFF',
|
||||
val ? 'ON' : 'OFF',
|
||||
curTemp,
|
||||
curHum,
|
||||
double.tryParse(mqtt.intensitas) ?? 0,
|
||||
);
|
||||
},
|
||||
mqtt,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// ── Alert Banner ──
|
||||
if (curTemp > mqtt.maxSuhu || curHum > mqtt.maxRh)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.red),
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.warning_amber_rounded,
|
||||
color: Colors.red),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Peringatan: Parameter pengeringan melewati batas aman!',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSensorCard(String title, String value, IconData icon, Color color, {bool isAlert = false, VoidCallback? onTap}) {
|
||||
Widget _buildSensorCard(
|
||||
String title,
|
||||
String value,
|
||||
IconData icon,
|
||||
Color color, {
|
||||
bool isAlert = false,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
color: isAlert ? Colors.red.withOpacity(0.1) : const Color(0xFF1A1A1A),
|
||||
color: isAlert
|
||||
? Colors.red.withOpacity(0.1)
|
||||
: const Color(0xFF1A1A1A),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: isAlert ? Border.all(color: Colors.red, width: 2) : null,
|
||||
border:
|
||||
isAlert ? Border.all(color: Colors.red, width: 2) : null,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, color: color, size: 40),
|
||||
const SizedBox(height: 10),
|
||||
Text(title, style: const TextStyle(color: Colors.grey, fontSize: 14)),
|
||||
Text(title,
|
||||
style:
|
||||
const TextStyle(color: Colors.grey, fontSize: 14)),
|
||||
const SizedBox(height: 5),
|
||||
Text(value, style: const TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold)),
|
||||
Text(value,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildControlCard(String title, bool isOn, Function(bool) onChanged, MqttService mqtt) {
|
||||
Widget _buildControlCard(
|
||||
String title,
|
||||
bool isOn,
|
||||
Function(bool) onChanged,
|
||||
MqttService mqtt,
|
||||
) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -249,20 +347,40 @@ class _MonitoringScreenState extends State<MonitoringScreen> {
|
|||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.settings_input_component, color: Colors.green, size: 20),
|
||||
const Icon(Icons.settings_input_component,
|
||||
color: Colors.green, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Text(title, style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold)),
|
||||
Expanded(
|
||||
child: Text(title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Text('Mode: ${mqtt.currentMode}', style: const TextStyle(color: Colors.green, fontSize: 9, fontWeight: FontWeight.bold)),
|
||||
Text(
|
||||
'Mode: ${mqtt.mode.toUpperCase()}',
|
||||
style: const TextStyle(
|
||||
color: Colors.green,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(isOn ? 'ON' : 'OFF', style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||
Text(isOn ? 'ON' : 'OFF',
|
||||
style: const TextStyle(
|
||||
color: Colors.grey, fontSize: 12)),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
child: Switch(value: isOn, onChanged: onChanged, activeColor: Colors.green),
|
||||
child: Switch(
|
||||
value: isOn,
|
||||
onChanged:
|
||||
mqtt.mode == 'manual' ? onChanged : null,
|
||||
activeColor: Colors.green,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -270,4 +388,4 @@ class _MonitoringScreenState extends State<MonitoringScreen> {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import '../services/mqtt_service.dart';
|
||||
import '../services/supabase_service.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
class RecordingScreen extends StatefulWidget {
|
||||
const RecordingScreen({super.key});
|
||||
|
|
@ -14,43 +15,91 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
|||
final TextEditingController _intervalController = TextEditingController();
|
||||
List<Map<String, dynamic>> _gallery = [];
|
||||
bool _isLoading = true;
|
||||
bool _isRecording = false;
|
||||
|
||||
late final RealtimeChannel _channel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadData();
|
||||
|
||||
// 🔥 REALTIME LISTENER SUPABASE (AUTO REFRESH GALERI)
|
||||
_channel = Supabase.instance.client
|
||||
.channel('realtime-foto')
|
||||
.onPostgresChanges(
|
||||
event: PostgresChangeEvent.insert,
|
||||
schema: 'public',
|
||||
table: 'foto_dataset',
|
||||
callback: (payload) {
|
||||
_loadData(); // reload gallery otomatis saat foto baru masuk
|
||||
},
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_intervalController.dispose();
|
||||
|
||||
// 🔥 MATIKAN REALTIME LISTENER
|
||||
Supabase.instance.client.removeChannel(_channel);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadData() async {
|
||||
setState(() => _isLoading = true);
|
||||
try {
|
||||
final interval = await SupabaseService().getIntervalSetting();
|
||||
final images = await SupabaseService().getFotoDataset();
|
||||
setState(() {
|
||||
_intervalController.text = interval.toString();
|
||||
_gallery = images;
|
||||
_isLoading = false;
|
||||
});
|
||||
final images = await SupabaseService().getFotoDataset();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_intervalController.text = interval.toString();
|
||||
_gallery = images;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error loading recording data: $e');
|
||||
setState(() => _isLoading = false);
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
void _controlRecording(bool start) async {
|
||||
Future<void> _controlRecording(bool start) async {
|
||||
final mqtt = Provider.of<MqttService>(context, listen: false);
|
||||
final intervalVal = int.tryParse(_intervalController.text) ?? 30;
|
||||
|
||||
if (intervalVal <= 0) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Interval harus lebih dari 0 menit!'),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (start) {
|
||||
mqtt.publish('coffee/record/interval', intervalVal.toString());
|
||||
mqtt.publish('coffee/record/cmd', 'START');
|
||||
// Simpan interval ke Supabase
|
||||
await SupabaseService().updateIntervalSetting(intervalVal);
|
||||
// Kirim interval ke ESP32 via MQTT
|
||||
mqtt.setIntervalFoto(intervalVal);
|
||||
// Trigger foto pertama langsung
|
||||
mqtt.triggerFoto();
|
||||
setState(() => _isRecording = true);
|
||||
} else {
|
||||
mqtt.publish('coffee/record/cmd', 'STOP');
|
||||
setState(() => _isRecording = false);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(start ? 'Perekaman Dimulai' : 'Perekaman Berhenti')),
|
||||
SnackBar(
|
||||
content: Text(start
|
||||
? '📸 Perekaman dimulai — interval ${intervalVal} menit'
|
||||
: '⏹ Perekaman dihentikan'),
|
||||
backgroundColor: start ? Colors.green : Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -60,119 +109,230 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
|||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: _isLoading
|
||||
? const Center(child: CircularProgressIndicator(color: Colors.green))
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1A1A),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: _isLoading
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(color: Colors.green))
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Interval Waktu (menit)', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: _intervalController,
|
||||
keyboardType: TextInputType.number,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: InputDecoration(
|
||||
hintText: '30',
|
||||
hintStyle: const TextStyle(color: Colors.grey),
|
||||
filled: true,
|
||||
fillColor: Colors.black,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
borderSide: const BorderSide(color: Colors.grey),
|
||||
),
|
||||
// ── Header ──
|
||||
const Text(
|
||||
'Perekaman',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Text(
|
||||
'Dataset Citra Kopi',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// ── Panel Kontrol ──
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1A1A),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Interval Waktu (menit)',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: _intervalController,
|
||||
keyboardType: TextInputType.number,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: InputDecoration(
|
||||
hintText: '30',
|
||||
hintStyle:
|
||||
const TextStyle(color: Colors.grey),
|
||||
filled: true,
|
||||
fillColor: Colors.black,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
borderSide:
|
||||
const BorderSide(color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
// Status perekaman
|
||||
if (_isRecording)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: Colors.green),
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.circle,
|
||||
color: Colors.green, size: 10),
|
||||
SizedBox(width: 8),
|
||||
Text('Perekaman berjalan...',
|
||||
style: TextStyle(
|
||||
color: Colors.green,
|
||||
fontSize: 12)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _isRecording
|
||||
? null
|
||||
: () => _controlRecording(true),
|
||||
icon: const Icon(Icons.play_arrow,
|
||||
color: Colors.white),
|
||||
label: const Text('Mulai',
|
||||
style:
|
||||
TextStyle(color: Colors.white)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
disabledBackgroundColor:
|
||||
Colors.green.withOpacity(0.3),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: !_isRecording
|
||||
? null
|
||||
: () => _controlRecording(false),
|
||||
icon: const Icon(Icons.stop,
|
||||
color: Colors.white),
|
||||
label: const Text('Berhenti',
|
||||
style:
|
||||
TextStyle(color: Colors.white)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.redAccent,
|
||||
disabledBackgroundColor:
|
||||
Colors.redAccent.withOpacity(0.3),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _controlRecording(true),
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
|
||||
child: const Text('Mulai Perekaman', style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
|
||||
// ── Header Galeri ──
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'Galeri Citra Dataset',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh,
|
||||
color: Colors.green),
|
||||
onPressed: _loadData,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _controlRecording(false),
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent),
|
||||
child: const Text('Berhenti Perekaman', style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
|
||||
// ── Grid Galeri ──
|
||||
Expanded(
|
||||
child: _gallery.isEmpty
|
||||
? const Center(
|
||||
child: Text(
|
||||
'Belum ada data citra',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
)
|
||||
: GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisSpacing: 10,
|
||||
),
|
||||
itemCount: _gallery.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _gallery[index];
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.pushNamed(
|
||||
context,
|
||||
'/image_result',
|
||||
arguments: {
|
||||
'id': item['id'].toString(),
|
||||
'date': item['timestamp']
|
||||
?.split(' ')[0] ??
|
||||
'',
|
||||
'time': item['timestamp']
|
||||
?.split(' ')[1] ??
|
||||
'',
|
||||
'temp': '${item['suhu']} °C',
|
||||
'humidity':
|
||||
'${item['kelembapan']} %',
|
||||
'light':
|
||||
'${item['intensitas']} Lux',
|
||||
'location': 'Gudang Pengering',
|
||||
'intake': item['kipas1'] ?? 'OFF',
|
||||
'exhaust': item['kipas2'] ?? 'OFF',
|
||||
'url_foto': item['url_foto'] ?? '',
|
||||
},
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1A1A),
|
||||
borderRadius:
|
||||
BorderRadius.circular(15),
|
||||
image: item['url_foto'] != null
|
||||
? DecorationImage(
|
||||
image: NetworkImage(
|
||||
item['url_foto']),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: item['url_foto'] == null
|
||||
? const Center(
|
||||
child: Icon(Icons.image,
|
||||
color: Colors.grey,
|
||||
size: 50),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('Galeri Citra Dataset', style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh, color: Colors.green),
|
||||
onPressed: _loadData,
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: _gallery.isEmpty
|
||||
? const Center(child: Text('Belum ada data citra', style: TextStyle(color: Colors.grey)))
|
||||
: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisSpacing: 10,
|
||||
),
|
||||
itemCount: _gallery.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _gallery[index];
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.pushNamed(context, '/image_result', arguments: {
|
||||
'id': item['id'].toString(),
|
||||
'date': item['timestamp']?.split(' ')[0] ?? '',
|
||||
'time': item['timestamp']?.split(' ')[1] ?? '',
|
||||
'temp': '${item['suhu']} °C',
|
||||
'humidity': '${item['kelembapan']} %',
|
||||
'light': '${item['intensitas']} Lux',
|
||||
'location': 'Gudang Pengering',
|
||||
'intake': item['kipas1'] ?? 'OFF',
|
||||
'exhaust': item['kipas2'] ?? 'OFF'
|
||||
}),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1A1A),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
image: item['url_foto'] != null ? DecorationImage(
|
||||
image: NetworkImage(item['url_foto']),
|
||||
fit: BoxFit.cover,
|
||||
) : null,
|
||||
),
|
||||
child: item['url_foto'] == null
|
||||
? const Icon(Icons.image, color: Colors.grey, size: 50)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
bool _isLoading = false;
|
||||
bool _obscurePassword = true; // Tambahan: State untuk mata password
|
||||
|
||||
Future<void> _handleRegister() async {
|
||||
setState(() => _isLoading = true);
|
||||
|
|
@ -99,13 +100,24 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
const Divider(color: Colors.grey),
|
||||
TextField(
|
||||
controller: _passwordController,
|
||||
obscureText: true,
|
||||
obscureText: _obscurePassword, // Menggunakan variabel state
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: Icon(Icons.lock, color: Colors.green),
|
||||
suffixIcon: Icon(Icons.visibility, color: Colors.grey),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.lock, color: Colors.green),
|
||||
// Menggunakan IconButton untuk aksi klik
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscurePassword ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
hintText: 'Password',
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
hintStyle: const TextStyle(color: Colors.grey),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
|
|
@ -148,4 +160,4 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +1,194 @@
|
|||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:mqtt_client/mqtt_client.dart';
|
||||
import 'package:mqtt_client/mqtt_server_client.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class MqttService extends ChangeNotifier {
|
||||
MqttServerClient? client;
|
||||
String _temp = "25";
|
||||
String _humidity = "55";
|
||||
String _light = "125";
|
||||
bool _isIntakeOn = false;
|
||||
bool _isExhaustOn = false;
|
||||
String _currentMode = "MANUAL";
|
||||
double _maxTemp = 48.0;
|
||||
double _maxHum = 75.0;
|
||||
|
||||
String get temp => _temp;
|
||||
String get humidity => _humidity;
|
||||
String get light => _light;
|
||||
bool get isIntakeOn => _isIntakeOn;
|
||||
bool get isExhaustOn => _isExhaustOn;
|
||||
String get currentMode => _currentMode;
|
||||
double get maxTemp => _maxTemp;
|
||||
double get maxHum => _maxHum;
|
||||
// Data sensor
|
||||
String _suhu = "0";
|
||||
String _kelembapan = "0";
|
||||
String _intensitas = "0";
|
||||
bool _isKipas1On = false;
|
||||
bool _isKipas2On = false;
|
||||
String _mode = "otomatis";
|
||||
String _timestamp = "-";
|
||||
|
||||
void setLimits(double t, double h) {
|
||||
_maxTemp = t;
|
||||
_maxHum = h;
|
||||
// Batas sensor
|
||||
double _maxSuhu = 45.0;
|
||||
double _maxRh = 70.0;
|
||||
|
||||
// Getter sensor
|
||||
String get suhu => _suhu;
|
||||
String get kelembapan => _kelembapan;
|
||||
String get intensitas => _intensitas;
|
||||
bool get isKipas1On => _isKipas1On;
|
||||
bool get isKipas2On => _isKipas2On;
|
||||
String get mode => _mode;
|
||||
String get timestamp => _timestamp;
|
||||
|
||||
// Getter batas
|
||||
double get maxSuhu => _maxSuhu;
|
||||
double get maxRh => _maxRh;
|
||||
|
||||
Function(String, bool)? onConnectionResult;
|
||||
|
||||
void setLimits(double suhuMax, double rhMax) {
|
||||
_maxSuhu = suhuMax;
|
||||
_maxRh = rhMax;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Callback for UI notifications
|
||||
Function(String, bool)? onConnectionResult;
|
||||
|
||||
Future<bool> connect() async {
|
||||
final String clientId = 'cli_kopi_${DateTime.now().millisecondsSinceEpoch % 10000}';
|
||||
|
||||
final String clientId =
|
||||
'flutter_kopi_${DateTime.now().millisecondsSinceEpoch % 10000}';
|
||||
|
||||
client = MqttServerClient.withPort(
|
||||
'1f7929299fd649388da5f2e234152531.s1.eu.hivemq.cloud',
|
||||
clientId,
|
||||
8883
|
||||
's1fd277a.ala.asia-southeast1.emqxsl.com', // EMQX host baru
|
||||
clientId,
|
||||
8883,
|
||||
);
|
||||
|
||||
|
||||
client!.secure = true;
|
||||
client!.setProtocolV311();
|
||||
client!.onBadCertificate = (dynamic cert) => true;
|
||||
client!.keepAlivePeriod = 20;
|
||||
client!.connectTimeoutPeriod = 10000;
|
||||
|
||||
client!.onDisconnected = onDisconnected;
|
||||
client!.onConnected = onConnected;
|
||||
client!.logging(on: true);
|
||||
client!.logging(on: kDebugMode);
|
||||
|
||||
final connMess = MqttConnectMessage()
|
||||
.withClientIdentifier(clientId)
|
||||
.authenticateAs('kopi_user', 'Kopikopi1')
|
||||
.startClean();
|
||||
|
||||
client!.connectionMessage = connMess;
|
||||
|
||||
try {
|
||||
debugPrint('MQTT: Connecting to HiveMQ...');
|
||||
await client!.connect();
|
||||
} catch (e) {
|
||||
debugPrint('MQTT Exception: $e');
|
||||
onConnectionResult?.call('Koneksi Gagal: $e', false);
|
||||
client!.disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (client?.connectionStatus?.state == MqttConnectionState.connected) {
|
||||
debugPrint('MQTT: SUCCESS!');
|
||||
debugPrint('MQTT: Terhubung!');
|
||||
onConnectionResult?.call('✅ Terhubung ke Server Kopi', true);
|
||||
_subscribeToTopics();
|
||||
notifyListeners();
|
||||
return true;
|
||||
} else {
|
||||
onConnectionResult?.call('❌ Koneksi Terputus', false);
|
||||
onConnectionResult?.call('❌ Koneksi Gagal', false);
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _subscribeToTopics() {
|
||||
client!.subscribe("coffee/temp", MqttQos.atMostOnce);
|
||||
client!.subscribe("coffee/humidity", MqttQos.atMostOnce);
|
||||
client!.subscribe("coffee/light", MqttQos.atMostOnce);
|
||||
client!.subscribe("coffee/intake", MqttQos.atMostOnce);
|
||||
client!.subscribe("coffee/exhaust", MqttQos.atMostOnce);
|
||||
client!.subscribe("coffee/config/mode", MqttQos.atMostOnce);
|
||||
client!.subscribe("kopi/sensor", MqttQos.atMostOnce);
|
||||
client!.subscribe("kopi/relay/kipas1", MqttQos.atMostOnce);
|
||||
client!.subscribe("kopi/relay/kipas2", MqttQos.atMostOnce);
|
||||
client!.subscribe("kopi/relay/mode", MqttQos.atMostOnce);
|
||||
|
||||
client!.updates!.listen((List<MqttReceivedMessage<MqttMessage?>>? c) {
|
||||
final recMess = c![0].payload as MqttPublishMessage;
|
||||
final pt = MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
|
||||
if (c == null || c.isEmpty) return;
|
||||
|
||||
final recMess = c[0].payload as MqttPublishMessage;
|
||||
final pt = MqttPublishPayload.bytesToStringAsString(
|
||||
recMess.payload.message);
|
||||
final topic = c[0].topic;
|
||||
|
||||
if (topic == "coffee/temp") _temp = pt;
|
||||
if (topic == "coffee/humidity") _humidity = pt;
|
||||
if (topic == "coffee/light") _light = pt;
|
||||
if (topic == "coffee/intake") _isIntakeOn = (pt == "ON");
|
||||
if (topic == "coffee/exhaust") _isExhaustOn = (pt == "ON");
|
||||
if (topic == "coffee/config/mode") _currentMode = pt;
|
||||
|
||||
|
||||
if (topic == "kopi/sensor") {
|
||||
try {
|
||||
final data = jsonDecode(pt);
|
||||
_suhu = data['suhu'].toString();
|
||||
_kelembapan = data['kelembapan'].toString();
|
||||
_intensitas = data['intensitas'].toString();
|
||||
_isKipas1On = data['kipas1'] == 'ON';
|
||||
_isKipas2On = data['kipas2'] == 'ON';
|
||||
_mode = data['mode'] ?? 'otomatis';
|
||||
_timestamp = data['timestamp'] ?? '-';
|
||||
} catch (e) {
|
||||
debugPrint('MQTT parse error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (topic == "kopi/relay/kipas1") _isKipas1On = (pt == "ON");
|
||||
if (topic == "kopi/relay/kipas2") _isKipas2On = (pt == "ON");
|
||||
if (topic == "kopi/relay/mode") _mode = pt;
|
||||
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
void publish(String topic, String message) {
|
||||
if (topic == "coffee/intake") _isIntakeOn = (message == "ON");
|
||||
if (topic == "coffee/exhaust") _isExhaustOn = (message == "ON");
|
||||
notifyListeners();
|
||||
// Kirim perintah kipas manual
|
||||
void perintahKipas(String nomorKipas, bool nyala) {
|
||||
final payload = jsonEncode({
|
||||
"kipas": nomorKipas,
|
||||
"status": nyala ? "ON" : "OFF",
|
||||
});
|
||||
_publish("kopi/relay/manual", payload);
|
||||
}
|
||||
|
||||
if (client?.connectionStatus?.state == MqttConnectionState.connected) {
|
||||
final builder = MqttClientPayloadBuilder();
|
||||
builder.addString(message);
|
||||
client!.publishMessage(topic, MqttQos.exactlyOnce, builder.payload!);
|
||||
// Ganti mode otomatis/manual
|
||||
void gantiMode(String mode) {
|
||||
_publish("kopi/relay/mode", mode);
|
||||
}
|
||||
|
||||
// Kirim interval foto dalam menit
|
||||
void setIntervalFoto(int menit) {
|
||||
_publish("kopi/setting/interval", menit.toString());
|
||||
}
|
||||
|
||||
// Trigger foto manual
|
||||
void triggerFoto() {
|
||||
_publish("kopi/trigger/foto", "AMBIL");
|
||||
}
|
||||
|
||||
// Kirim batas sensor ke ESP32 dan Node-RED
|
||||
void setBatasSensor(
|
||||
double suhuMin, double suhuMax, double rhMin, double rhMax) {
|
||||
final payload = jsonEncode({
|
||||
"suhu_min": suhuMin,
|
||||
"suhu_max": suhuMax,
|
||||
"rh_min": rhMin,
|
||||
"rh_max": rhMax,
|
||||
});
|
||||
_publish("kopi/setting/batas", payload);
|
||||
}
|
||||
|
||||
void _publish(String topic, String message) {
|
||||
if (client?.connectionStatus?.state != MqttConnectionState.connected) {
|
||||
debugPrint('MQTT: Tidak terhubung, tidak bisa publish');
|
||||
return;
|
||||
}
|
||||
final builder = MqttClientPayloadBuilder();
|
||||
builder.addString(message);
|
||||
client!.publishMessage(topic, MqttQos.atMostOnce, builder.payload!);
|
||||
debugPrint('MQTT publish → $topic : $message');
|
||||
}
|
||||
|
||||
void onConnected() {
|
||||
debugPrint('MQTT: onConnected');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void onDisconnected() {
|
||||
debugPrint('MQTT: onDisconnected');
|
||||
|
||||
// 🔥 AUTO RECONNECT
|
||||
Future.delayed(const Duration(seconds: 3), () {
|
||||
debugPrint('MQTT: mencoba reconnect...');
|
||||
connect();
|
||||
});
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void updateFromLastLog(String t, String h, String l, bool i, bool e) {
|
||||
_temp = t;
|
||||
_humidity = h;
|
||||
_light = l;
|
||||
_isIntakeOn = i;
|
||||
_isExhaustOn = e;
|
||||
notifyListeners();
|
||||
void disconnect() {
|
||||
client?.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
jni
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,11 @@ import FlutterMacOS
|
|||
import Foundation
|
||||
|
||||
import app_links
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
||||
|
|
|
|||
482
pubspec.lock
482
pubspec.lock
|
|
@ -5,82 +5,122 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "3ced568a5d9e309e99af71285666f1f3117bddd0bd5b3317979dccc1a40cada4"
|
||||
sha256: "3462d9defc61565fde4944858b59bec5be2b9d5b05f20aed190adb3ad08a7abc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.5.1"
|
||||
version: "7.0.0"
|
||||
app_links_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_linux
|
||||
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
app_links_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_platform_interface
|
||||
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
app_links_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_web
|
||||
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
version: "2.13.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.2"
|
||||
version: "1.19.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "3.0.7"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
version: "1.0.9"
|
||||
dart_jsonwebtoken:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_jsonwebtoken
|
||||
sha256: ad84e60181696513d04d5f2078e0bbc20365b911f46f647797317414bdc88fbe
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
equatable:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -101,18 +141,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.2.0"
|
||||
ffi_leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi_leak_tracker
|
||||
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -125,10 +173,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: fl_chart
|
||||
sha256: c1e26c7e48496be85104c16c040950b0436674cdf0737f3f6e95511b2529b592
|
||||
sha256: b938f77d042cbcd822936a7a359a7235bad8bd72070de1f827efc2cc297ac888
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.63.0"
|
||||
version: "1.2.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
|
@ -138,10 +186,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "6.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
|
@ -156,58 +204,82 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: functions_client
|
||||
sha256: a49876ebae32a50eb62483c5c5ac80ed0d8da34f98ccc23986b03a8d28cee07c
|
||||
sha256: "94074d62167ae634127ef6095f536835063a7dc80f2b1aa306d2346ff9023996"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.5.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
gotrue:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gotrue
|
||||
sha256: d6362dff9a54f8c1c372bb137c858b4024c16407324d34e6473e59623c9b9f50
|
||||
sha256: "7a4172601553e61716f5c3dd243aa3297e13308e07eb85b7853c941ba585dcf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.1"
|
||||
version: "2.20.0"
|
||||
gtk:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gtk
|
||||
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||
sha256: "4ff85b2a16724029dd9e5bbb5a94b6918f9973f74ba571c949d2002801879cf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
html:
|
||||
version: "2.2.0"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||
name: hooks
|
||||
sha256: "025f060e86d2d4c3c47b56e33caf7f93bf9283340f26d23424ebcfccf34f621e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.4"
|
||||
version: "1.0.3"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
version: "0.20.2"
|
||||
jni:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: jni
|
||||
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
jni_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: jni_flutter
|
||||
sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
jwt_decode:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -216,62 +288,94 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "6.1.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "2.0.0"
|
||||
mqtt_client:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mqtt_client
|
||||
sha256: "930a951a748a3feacd57bde9a608740c724f7afb0e1be1ef56f3e1fe9f689cf9"
|
||||
sha256: "41c8edd3bc8efc80c1c8ebfb40081c24d12d13085faca96b9280a624eca2d893"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.3.0"
|
||||
version: "10.11.11"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.6"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -280,38 +384,54 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.3.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "51f0d2c554cfbc9d6a312ab35152fc77e2f0b758ce9f1a444a3a1e5b8f3c6b7f"
|
||||
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.3.1"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -332,18 +452,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -352,14 +472,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
postgrest:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: postgrest
|
||||
sha256: b74dc0f57b5dca5ce9f57a54b08110bf41d6fc8a0483c0fec10c79e9aa0fb2bb
|
||||
sha256: "9d61b3d4a88fcf9424d400127c54d49ed1b56ec30838fc0a33a64f31d4e694cc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.7.0"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -368,14 +496,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
realtime_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: realtime_client
|
||||
sha256: e3089dac2121917cc0c72d42ab056fea0abbaf3c2229048fc50e64bafc731adf
|
||||
sha256: "7dfccf372d2f55aacfeefb6186f65a06f3ffae383fe042dbeef9d85d33487576"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.7.3"
|
||||
record_use:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: record_use
|
||||
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
retry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -396,199 +540,183 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.5.5"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
||||
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.4.23"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.5"
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.2"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
version: "0.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.10.2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.12.1"
|
||||
storage_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: storage_client
|
||||
sha256: "9f9ed283943313b23a1b27139bb18986e9b152a6d34530232c702c468d98e91a"
|
||||
sha256: "4801e8ca219a35e51cbb30589aba5306667ae8935b792504595a45273cef0b18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.5.2"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.4.1"
|
||||
supabase:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: supabase
|
||||
sha256: c3ebddba69ddcf16d8b78e8c44c4538b0193d1cf944fde3b72eb5b279892a370
|
||||
sha256: "40e5a8833c8834e140ef53b60a6181849667eba9ca125acb7f8e24c6a769d418"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.3"
|
||||
version: "2.10.6"
|
||||
supabase_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: supabase_flutter
|
||||
sha256: "3b5b5b492e342f63f301605d0c66f6528add285b5744f53c9fd9abd5ffdbce5b"
|
||||
sha256: c02ce58abcaf86cb8055ad40bfd98bbf5b93fed3b5b56b8220d88ed03842818b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.8.4"
|
||||
version: "2.12.4"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
version: "0.7.10"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
universal_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_html
|
||||
sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_io
|
||||
sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "1.4.0"
|
||||
url_launcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
|
||||
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.4"
|
||||
version: "6.3.2"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
|
||||
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
version: "6.3.29"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
||||
sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.4"
|
||||
version: "6.4.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
||||
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.2.2"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
|
||||
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
version: "3.2.5"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -601,66 +729,90 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
|
||||
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.4.2"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
||||
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.1.5"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.2.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4-beta"
|
||||
version: "1.1.1"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "3.0.3"
|
||||
win32:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: win32
|
||||
sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067
|
||||
sha256: ba7d5750e3441caa1bbe31d9e516348fcf8dfcb32aa29ef87a844a59f4d1f1d0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.8"
|
||||
version: "6.1.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
yet_another_json_isolate:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yet_another_json_isolate
|
||||
sha256: "56155e9e0002cc51ea7112857bbcdc714d4c35e176d43e4d3ee233009ff410c9"
|
||||
sha256: fe45897501fa156ccefbfb9359c9462ce5dec092f05e8a56109db30be864f01e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.1.0"
|
||||
sdks:
|
||||
dart: ">=3.1.0 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
dart: ">=3.10.3 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
|
|
|
|||
16
pubspec.yaml
16
pubspec.yaml
|
|
@ -30,16 +30,16 @@ environment:
|
|||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
cupertino_icons: ^1.0.8
|
||||
supabase_flutter: ^2.5.0
|
||||
mqtt_client: ^10.2.0
|
||||
cupertino_icons: ^1.0.9
|
||||
supabase_flutter: ^2.12.4
|
||||
mqtt_client: ^10.11.11
|
||||
provider: ^6.1.1
|
||||
fl_chart: 0.63.0
|
||||
intl: ^0.18.1
|
||||
shared_preferences: ^2.2.0
|
||||
fl_chart: ^1.2.0
|
||||
intl: ^0.20.2
|
||||
shared_preferences: ^2.5.5
|
||||
|
||||
dependency_overrides:
|
||||
win32: 5.0.8
|
||||
win32: ^6.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
@ -50,7 +50,7 @@ dev_dependencies:
|
|||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^3.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
jni
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
|
|
|||
Loading…
Reference in New Issue