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 localProperties = new Properties()
|
||||||
def localPropertiesFile = rootProject.file('local.properties')
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
if (localPropertiesFile.exists()) {
|
if (localPropertiesFile.exists()) {
|
||||||
|
|
@ -6,47 +12,27 @@ if (localPropertiesFile.exists()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') ?: '1'
|
||||||
if (flutterRoot == null) {
|
def flutterVersionName = localProperties.getProperty('flutter.versionName') ?: '1.0'
|
||||||
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"
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "com.example.coffee_iot_flutter"
|
namespace "com.example.coffee_iot_flutter"
|
||||||
compileSdkVersion 34
|
compileSdk 35
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = '1.8'
|
jvmTarget = '17'
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.example.coffee_iot_flutter"
|
applicationId "com.example.coffee_iot_flutter"
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdk flutter.minSdkVersion
|
||||||
targetSdkVersion 34
|
targetSdk 35
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
@ -58,10 +44,15 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flutter {
|
// Force pin versi androidx agar kompatibel AGP 8.6.1
|
||||||
source '../..'
|
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 {
|
flutter {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
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 {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|
@ -18,14 +5,16 @@ allprojects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.buildDir = '../build'
|
rootProject.buildDir = "../build"
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("clean", Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
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
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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")
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
def properties = new Properties()
|
|
||||||
|
|
||||||
if (localPropertiesFile.exists()) {
|
repositories {
|
||||||
localPropertiesFile.withReader("UTF-8") { reader ->
|
google()
|
||||||
properties.load(reader)
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
plugins {
|
||||||
if (flutterSdkPath == null) {
|
id "com.android.application" version "8.6.1" apply false
|
||||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
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
|
// Initialize Supabase with the provided URL and Publishable Key
|
||||||
await Supabase.initialize(
|
await Supabase.initialize(
|
||||||
url: 'https://ddmhzzegejbsshihzext.supabase.co',
|
url: 'https://ddmhzzegejbsshihzext.supabase.co',
|
||||||
anonKey: 'sb_publishable_IJJ8fXD9CXfjMeu8vNJ8bw_NhWyTJKy',
|
anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRkbWh6emVnZWpic3NoaWh6ZXh0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzcyMjIyNzAsImV4cCI6MjA5Mjc5ODI3MH0._r-viIQLMXuNNdg4tcn7sJTBO9QqSpSlF7hk43hu2Ks',
|
||||||
);
|
);
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ class _AnalyticsScreenState extends State<AnalyticsScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final args = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
|
final args = (ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?) ?? {};
|
||||||
final String title = args['title'];
|
final String title = args['title'] ?? 'Grafik';
|
||||||
final Color color = args['color'];
|
final Color color = args['color'] ?? Colors.green;
|
||||||
final String sensorKey = title.contains('Suhu') ? 'suhu' : title.contains('Kelembaban') ? 'kelembapan' : 'intensitas';
|
final String sensorKey = title.contains('Suhu') ? 'suhu' : title.contains('Kelembaban') ? 'kelembapan' : 'intensitas';
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ class ControlSettingsScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
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 _tempMinController = TextEditingController();
|
||||||
final TextEditingController _tempMaxController = TextEditingController();
|
final TextEditingController _tempMaxController = TextEditingController();
|
||||||
final TextEditingController _humMinController = TextEditingController();
|
final TextEditingController _humMinController = TextEditingController();
|
||||||
final TextEditingController _humMaxController = TextEditingController();
|
final TextEditingController _humMaxController = TextEditingController();
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -25,68 +25,107 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
||||||
_loadSettings();
|
_loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_tempMinController.dispose();
|
||||||
|
_tempMaxController.dispose();
|
||||||
|
_humMinController.dispose();
|
||||||
|
_humMaxController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadSettings() async {
|
Future<void> _loadSettings() async {
|
||||||
try {
|
try {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final data = await SupabaseService().getBatasSensor();
|
final data = await SupabaseService().getBatasSensor();
|
||||||
if (data != null) {
|
if (data != null && mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_tempMinController.text = data['suhu_min'].toString();
|
_tempMinController.text = data['suhu_min'].toString();
|
||||||
_tempMaxController.text = data['suhu_max'].toString();
|
_tempMaxController.text = data['suhu_max'].toString();
|
||||||
_humMinController.text = data['rh_min'].toString();
|
_humMinController.text = data['rh_min'].toString();
|
||||||
_humMaxController.text = data['rh_max'].toString();
|
_humMaxController.text = data['rh_max'].toString();
|
||||||
// Load last saved mode from local memory
|
|
||||||
_operatingMode = prefs.getInt('op_mode') ?? 1;
|
_operatingMode = prefs.getInt('op_mode') ?? 1;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error loading settings: $e');
|
debugPrint('Error loading settings: $e');
|
||||||
setState(() => _isLoading = false);
|
if (mounted) setState(() => _isLoading = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _saveSettings() async {
|
Future<void> _saveSettings() async {
|
||||||
// 1. Validation: Prevent empty inputs
|
if (_tempMinController.text.isEmpty ||
|
||||||
if (_tempMinController.text.isEmpty || _tempMaxController.text.isEmpty ||
|
_tempMaxController.text.isEmpty ||
|
||||||
_humMinController.text.isEmpty || _humMaxController.text.isEmpty) {
|
_humMinController.text.isEmpty ||
|
||||||
|
_humMaxController.text.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
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;
|
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();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2. Save Mode to Local Memory
|
// Simpan mode ke local storage
|
||||||
await prefs.setInt('op_mode', _operatingMode);
|
await prefs.setInt('op_mode', _operatingMode);
|
||||||
|
|
||||||
// 3. MQTT Publish
|
// Kirim batas sensor ke ESP32 via MQTT → Flow 5 simpan ke Supabase
|
||||||
mqtt.publish('coffee/config/temp_max', _tempMaxController.text);
|
mqtt.setBatasSensor(suhuMin, suhuMax, rhMin, rhMax);
|
||||||
mqtt.publish('coffee/config/hum_max', _humMaxController.text);
|
|
||||||
mqtt.publish('coffee/config/mode', _operatingMode == 0 ? "AUTO" : "MANUAL");
|
// 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) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
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) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('❌ Gagal: $e'), backgroundColor: Colors.red),
|
SnackBar(
|
||||||
|
content: Text('❌ Gagal: $e'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -99,84 +138,133 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: _isLoading
|
child: _isLoading
|
||||||
? const Center(child: CircularProgressIndicator(color: Colors.green))
|
? const Center(
|
||||||
: SingleChildScrollView(
|
child: CircularProgressIndicator(color: Colors.green))
|
||||||
child: Padding(
|
: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(20.0),
|
child: Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.all(20.0),
|
||||||
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: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text('Mode Operasi', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
const Text(
|
||||||
const SizedBox(height: 10),
|
'Pengaturan',
|
||||||
RadioListTile<int>(
|
style: TextStyle(
|
||||||
value: 0,
|
color: Colors.white,
|
||||||
groupValue: _operatingMode,
|
fontSize: 32,
|
||||||
onChanged: (val) => setState(() => _operatingMode = val!),
|
fontWeight: FontWeight.bold),
|
||||||
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>(
|
const Text(
|
||||||
value: 1,
|
'Batas Sensor & Mode Operasi',
|
||||||
groupValue: _operatingMode,
|
style: TextStyle(color: Colors.grey, fontSize: 16),
|
||||||
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: 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(
|
return Container(
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(15),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
@ -186,7 +274,11 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
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),
|
const SizedBox(height: 10),
|
||||||
TextField(
|
TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
|
@ -208,4 +300,4 @@ class _ControlSettingsScreenState extends State<ControlSettingsScreen> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,14 +46,34 @@ class ImageResultScreen extends StatelessWidget {
|
||||||
color: const Color(0xFF1A1A1A),
|
color: const Color(0xFF1A1A1A),
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
child: Column(
|
clipBehavior: Clip.hardEdge,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: args['url_foto'] != null && args['url_foto'].toString().isNotEmpty
|
||||||
children: [
|
? Image.network(
|
||||||
const Icon(Icons.image, size: 80, color: Colors.grey),
|
args['url_foto'],
|
||||||
const SizedBox(height: 10),
|
fit: BoxFit.cover,
|
||||||
Text('Citra #${args['id']}', style: const TextStyle(color: Colors.grey)),
|
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),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||||
final _emailController = TextEditingController();
|
final _emailController = TextEditingController();
|
||||||
final _passwordController = TextEditingController();
|
final _passwordController = TextEditingController();
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
bool _obscurePassword = true; // Tambahan: State untuk mata password
|
||||||
|
|
||||||
Future<void> _handleLogin() async {
|
Future<void> _handleLogin() async {
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
@ -80,13 +81,24 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||||
const Divider(color: Colors.grey),
|
const Divider(color: Colors.grey),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: true,
|
obscureText: _obscurePassword, // Menggunakan variabel state
|
||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.lock, color: Colors.green),
|
prefixIcon: const Icon(Icons.lock, color: Colors.green),
|
||||||
suffixIcon: Icon(Icons.visibility, color: Colors.grey),
|
// Menggunakan IconButton untuk aksi klik
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_obscurePassword ? Icons.visibility : Icons.visibility_off,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_obscurePassword = !_obscurePassword;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
hintText: 'Password',
|
hintText: 'Password',
|
||||||
hintStyle: TextStyle(color: Colors.grey),
|
hintStyle: const TextStyle(color: Colors.grey),
|
||||||
border: InputBorder.none,
|
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 {
|
Future<void> _initData() async {
|
||||||
setState(() => _isInitialLoading = true);
|
setState(() => _isInitialLoading = true);
|
||||||
await _loadLimits();
|
await _loadLimits();
|
||||||
await _loadLastSensorData();
|
|
||||||
if (mounted) setState(() => _isInitialLoading = false);
|
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 {
|
Future<void> _loadLimits() async {
|
||||||
try {
|
try {
|
||||||
final data = await SupabaseService().getBatasSensor();
|
final data = await SupabaseService().getBatasSensor();
|
||||||
|
|
@ -64,180 +43,299 @@ class _MonitoringScreenState extends State<MonitoringScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final mqtt = Provider.of<MqttService>(context);
|
final mqtt = Provider.of<MqttService>(context);
|
||||||
final curTemp = double.tryParse(mqtt.temp) ?? 0;
|
final curTemp = double.tryParse(mqtt.suhu) ?? 0;
|
||||||
final curHum = double.tryParse(mqtt.humidity) ?? 0;
|
final curHum = double.tryParse(mqtt.kelembapan) ?? 0;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: _isInitialLoading
|
child: _isInitialLoading
|
||||||
? const Center(child: CircularProgressIndicator(color: Colors.green))
|
? const Center(
|
||||||
: RefreshIndicator(
|
child: CircularProgressIndicator(color: Colors.green))
|
||||||
onRefresh: _initData,
|
: RefreshIndicator(
|
||||||
color: Colors.green,
|
onRefresh: _initData,
|
||||||
child: SingleChildScrollView(
|
color: Colors.green,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
padding: const EdgeInsets.all(20.0),
|
child: Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.all(20.0),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// ── Header ──
|
||||||
children: [
|
Row(
|
||||||
Column(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Column(
|
||||||
const Text(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
'Monitoring',
|
children: [
|
||||||
style: TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold),
|
const Text(
|
||||||
),
|
'Monitoring',
|
||||||
const Text(
|
style: TextStyle(
|
||||||
'Sistem Pengeringan Kopi',
|
color: Colors.white,
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 16),
|
fontSize: 32,
|
||||||
),
|
fontWeight: FontWeight.bold),
|
||||||
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 Text(
|
||||||
const SizedBox(width: 8),
|
'Sistem Pengeringan Kopi',
|
||||||
Text(
|
style: TextStyle(
|
||||||
mqtt.client?.connectionStatus?.state == MqttConnectionState.connected
|
color: Colors.grey, fontSize: 16),
|
||||||
? 'Connected' : 'Disconnected',
|
|
||||||
style: TextStyle(
|
|
||||||
color: mqtt.client?.connectionStatus?.state == MqttConnectionState.connected
|
|
||||||
? Colors.green : Colors.red,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 5),
|
||||||
],
|
Row(
|
||||||
),
|
children: [
|
||||||
],
|
Container(
|
||||||
),
|
width: 8,
|
||||||
const CircleAvatar(
|
height: 8,
|
||||||
backgroundColor: Color(0xFF1A1A1A),
|
decoration: BoxDecoration(
|
||||||
radius: 25,
|
shape: BoxShape.circle,
|
||||||
child: Icon(Icons.person, color: Colors.green),
|
color: mqtt.client?.connectionStatus
|
||||||
),
|
?.state ==
|
||||||
],
|
MqttConnectionState.connected
|
||||||
),
|
? Colors.green
|
||||||
const SizedBox(height: 30),
|
: Colors.red,
|
||||||
GridView.count(
|
),
|
||||||
shrinkWrap: true,
|
),
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
const SizedBox(width: 8),
|
||||||
crossAxisCount: 2,
|
Text(
|
||||||
crossAxisSpacing: 15,
|
mqtt.client?.connectionStatus?.state ==
|
||||||
mainAxisSpacing: 15,
|
MqttConnectionState.connected
|
||||||
children: [
|
? 'Connected'
|
||||||
_buildSensorCard(
|
: 'Disconnected',
|
||||||
'Suhu',
|
style: TextStyle(
|
||||||
'${mqtt.temp}°C',
|
color: mqtt.client?.connectionStatus
|
||||||
Icons.thermostat,
|
?.state ==
|
||||||
curTemp > mqtt.maxTemp ? Colors.red : Colors.redAccent,
|
MqttConnectionState.connected
|
||||||
isAlert: curTemp > mqtt.maxTemp,
|
? Colors.green
|
||||||
onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Suhu (°C)', 'color': Colors.redAccent, 'value': '${mqtt.temp}°C'}),
|
: Colors.red,
|
||||||
),
|
fontSize: 12,
|
||||||
_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}%'}),
|
const CircleAvatar(
|
||||||
),
|
backgroundColor: Color(0xFF1A1A1A),
|
||||||
_buildSensorCard(
|
radius: 25,
|
||||||
'Cahaya',
|
child:
|
||||||
'${mqtt.light} Lux',
|
Icon(Icons.person, color: Colors.green),
|
||||||
Icons.wb_sunny,
|
),
|
||||||
Colors.orange,
|
],
|
||||||
onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Cahaya (Lux)', 'color': Colors.orange, 'value': '${mqtt.light} Lux'}),
|
),
|
||||||
),
|
const SizedBox(height: 10),
|
||||||
_buildControlCard(
|
|
||||||
'Intake',
|
// ── Timestamp ──
|
||||||
mqtt.isIntakeOn,
|
Text(
|
||||||
(val) async {
|
'Update: ${mqtt.timestamp}',
|
||||||
final status = val ? 'ON' : 'OFF';
|
style: const TextStyle(
|
||||||
mqtt.publish('coffee/intake', status);
|
color: Colors.grey, fontSize: 11),
|
||||||
await SupabaseService().logFanAction(status, mqtt.isExhaustOn ? 'ON' : 'OFF', curTemp, curHum, double.tryParse(mqtt.light) ?? 0);
|
),
|
||||||
},
|
const SizedBox(height: 20),
|
||||||
mqtt,
|
|
||||||
),
|
// ── Mode Badge ──
|
||||||
_buildControlCard(
|
Container(
|
||||||
'Exhaust',
|
padding: const EdgeInsets.symmetric(
|
||||||
mqtt.isExhaustOn,
|
horizontal: 12, vertical: 6),
|
||||||
(val) async {
|
decoration: BoxDecoration(
|
||||||
final status = val ? 'ON' : 'OFF';
|
color: mqtt.mode == 'manual'
|
||||||
mqtt.publish('coffee/exhaust', status);
|
? Colors.orange.withOpacity(0.2)
|
||||||
await SupabaseService().logFanAction(mqtt.isIntakeOn ? 'ON' : 'OFF', status, curTemp, curHum, double.tryParse(mqtt.light) ?? 0);
|
: Colors.green.withOpacity(0.2),
|
||||||
},
|
borderRadius: BorderRadius.circular(20),
|
||||||
mqtt,
|
border: Border.all(
|
||||||
),
|
color: mqtt.mode == 'manual'
|
||||||
],
|
? Colors.orange
|
||||||
),
|
: Colors.green,
|
||||||
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),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
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(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(15),
|
||||||
decoration: BoxDecoration(
|
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),
|
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(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, color: color, size: 40),
|
Icon(icon, color: color, size: 40),
|
||||||
const SizedBox(height: 10),
|
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),
|
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(
|
return Container(
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(15),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
@ -249,20 +347,40 @@ class _MonitoringScreenState extends State<MonitoringScreen> {
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
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),
|
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(),
|
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(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
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(
|
SizedBox(
|
||||||
height: 30,
|
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 'package:provider/provider.dart';
|
||||||
import '../services/mqtt_service.dart';
|
import '../services/mqtt_service.dart';
|
||||||
import '../services/supabase_service.dart';
|
import '../services/supabase_service.dart';
|
||||||
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||||
|
|
||||||
class RecordingScreen extends StatefulWidget {
|
class RecordingScreen extends StatefulWidget {
|
||||||
const RecordingScreen({super.key});
|
const RecordingScreen({super.key});
|
||||||
|
|
@ -14,43 +15,91 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
final TextEditingController _intervalController = TextEditingController();
|
final TextEditingController _intervalController = TextEditingController();
|
||||||
List<Map<String, dynamic>> _gallery = [];
|
List<Map<String, dynamic>> _gallery = [];
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
|
bool _isRecording = false;
|
||||||
|
|
||||||
|
late final RealtimeChannel _channel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadData();
|
_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 {
|
Future<void> _loadData() async {
|
||||||
|
setState(() => _isLoading = true);
|
||||||
try {
|
try {
|
||||||
final interval = await SupabaseService().getIntervalSetting();
|
final interval = await SupabaseService().getIntervalSetting();
|
||||||
final images = await SupabaseService().getFotoDataset();
|
final images = await SupabaseService().getFotoDataset();
|
||||||
setState(() {
|
if (mounted) {
|
||||||
_intervalController.text = interval.toString();
|
setState(() {
|
||||||
_gallery = images;
|
_intervalController.text = interval.toString();
|
||||||
_isLoading = false;
|
_gallery = images;
|
||||||
});
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error loading recording data: $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 mqtt = Provider.of<MqttService>(context, listen: false);
|
||||||
final intervalVal = int.tryParse(_intervalController.text) ?? 30;
|
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) {
|
if (start) {
|
||||||
mqtt.publish('coffee/record/interval', intervalVal.toString());
|
// Simpan interval ke Supabase
|
||||||
mqtt.publish('coffee/record/cmd', 'START');
|
|
||||||
await SupabaseService().updateIntervalSetting(intervalVal);
|
await SupabaseService().updateIntervalSetting(intervalVal);
|
||||||
|
// Kirim interval ke ESP32 via MQTT
|
||||||
|
mqtt.setIntervalFoto(intervalVal);
|
||||||
|
// Trigger foto pertama langsung
|
||||||
|
mqtt.triggerFoto();
|
||||||
|
setState(() => _isRecording = true);
|
||||||
} else {
|
} else {
|
||||||
mqtt.publish('coffee/record/cmd', 'STOP');
|
setState(() => _isRecording = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
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(
|
return Scaffold(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: _isLoading
|
child: _isLoading
|
||||||
? const Center(child: CircularProgressIndicator(color: Colors.green))
|
? const Center(
|
||||||
: Padding(
|
child: CircularProgressIndicator(color: Colors.green))
|
||||||
padding: const EdgeInsets.all(20.0),
|
: Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.all(20.0),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFF1A1A1A),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text('Interval Waktu (menit)', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
|
// ── Header ──
|
||||||
const SizedBox(height: 10),
|
const Text(
|
||||||
TextField(
|
'Perekaman',
|
||||||
controller: _intervalController,
|
style: TextStyle(
|
||||||
keyboardType: TextInputType.number,
|
color: Colors.white,
|
||||||
style: const TextStyle(color: Colors.white),
|
fontSize: 32,
|
||||||
decoration: InputDecoration(
|
fontWeight: FontWeight.bold),
|
||||||
hintText: '30',
|
),
|
||||||
hintStyle: const TextStyle(color: Colors.grey),
|
const Text(
|
||||||
filled: true,
|
'Dataset Citra Kopi',
|
||||||
fillColor: Colors.black,
|
style: TextStyle(color: Colors.grey, fontSize: 16),
|
||||||
border: OutlineInputBorder(
|
),
|
||||||
borderRadius: BorderRadius.circular(15),
|
const SizedBox(height: 20),
|
||||||
borderSide: const BorderSide(color: Colors.grey),
|
|
||||||
),
|
// ── 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),
|
const SizedBox(height: 20),
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
// ── Header Galeri ──
|
||||||
height: 50,
|
Row(
|
||||||
child: ElevatedButton(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
onPressed: () => _controlRecording(true),
|
children: [
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
|
const Text(
|
||||||
child: const Text('Mulai Perekaman', style: TextStyle(color: Colors.white)),
|
'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),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
// ── Grid Galeri ──
|
||||||
height: 50,
|
Expanded(
|
||||||
child: ElevatedButton(
|
child: _gallery.isEmpty
|
||||||
onPressed: () => _controlRecording(false),
|
? const Center(
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent),
|
child: Text(
|
||||||
child: const Text('Berhenti Perekaman', style: TextStyle(color: Colors.white)),
|
'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 _emailController = TextEditingController();
|
||||||
final _passwordController = TextEditingController();
|
final _passwordController = TextEditingController();
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
bool _obscurePassword = true; // Tambahan: State untuk mata password
|
||||||
|
|
||||||
Future<void> _handleRegister() async {
|
Future<void> _handleRegister() async {
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
@ -99,13 +100,24 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||||
const Divider(color: Colors.grey),
|
const Divider(color: Colors.grey),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: true,
|
obscureText: _obscurePassword, // Menggunakan variabel state
|
||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.lock, color: Colors.green),
|
prefixIcon: const Icon(Icons.lock, color: Colors.green),
|
||||||
suffixIcon: Icon(Icons.visibility, color: Colors.grey),
|
// Menggunakan IconButton untuk aksi klik
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_obscurePassword ? Icons.visibility : Icons.visibility_off,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_obscurePassword = !_obscurePassword;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
hintText: 'Password',
|
hintText: 'Password',
|
||||||
hintStyle: TextStyle(color: Colors.grey),
|
hintStyle: const TextStyle(color: Colors.grey),
|
||||||
border: InputBorder.none,
|
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_client.dart';
|
||||||
import 'package:mqtt_client/mqtt_server_client.dart';
|
import 'package:mqtt_client/mqtt_server_client.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
class MqttService extends ChangeNotifier {
|
class MqttService extends ChangeNotifier {
|
||||||
MqttServerClient? client;
|
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;
|
// Data sensor
|
||||||
String get humidity => _humidity;
|
String _suhu = "0";
|
||||||
String get light => _light;
|
String _kelembapan = "0";
|
||||||
bool get isIntakeOn => _isIntakeOn;
|
String _intensitas = "0";
|
||||||
bool get isExhaustOn => _isExhaustOn;
|
bool _isKipas1On = false;
|
||||||
String get currentMode => _currentMode;
|
bool _isKipas2On = false;
|
||||||
double get maxTemp => _maxTemp;
|
String _mode = "otomatis";
|
||||||
double get maxHum => _maxHum;
|
String _timestamp = "-";
|
||||||
|
|
||||||
void setLimits(double t, double h) {
|
// Batas sensor
|
||||||
_maxTemp = t;
|
double _maxSuhu = 45.0;
|
||||||
_maxHum = h;
|
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();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback for UI notifications
|
|
||||||
Function(String, bool)? onConnectionResult;
|
|
||||||
|
|
||||||
Future<bool> connect() async {
|
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(
|
client = MqttServerClient.withPort(
|
||||||
'1f7929299fd649388da5f2e234152531.s1.eu.hivemq.cloud',
|
's1fd277a.ala.asia-southeast1.emqxsl.com', // EMQX host baru
|
||||||
clientId,
|
clientId,
|
||||||
8883
|
8883,
|
||||||
);
|
);
|
||||||
|
|
||||||
client!.secure = true;
|
client!.secure = true;
|
||||||
client!.setProtocolV311();
|
client!.setProtocolV311();
|
||||||
client!.onBadCertificate = (dynamic cert) => true;
|
client!.onBadCertificate = (dynamic cert) => true;
|
||||||
client!.keepAlivePeriod = 20;
|
client!.keepAlivePeriod = 20;
|
||||||
client!.connectTimeoutPeriod = 10000;
|
client!.connectTimeoutPeriod = 10000;
|
||||||
|
|
||||||
client!.onDisconnected = onDisconnected;
|
client!.onDisconnected = onDisconnected;
|
||||||
client!.onConnected = onConnected;
|
client!.onConnected = onConnected;
|
||||||
client!.logging(on: true);
|
client!.logging(on: kDebugMode);
|
||||||
|
|
||||||
final connMess = MqttConnectMessage()
|
final connMess = MqttConnectMessage()
|
||||||
.withClientIdentifier(clientId)
|
.withClientIdentifier(clientId)
|
||||||
.authenticateAs('kopi_user', 'Kopikopi1')
|
.authenticateAs('kopi_user', 'Kopikopi1')
|
||||||
.startClean();
|
.startClean();
|
||||||
|
|
||||||
client!.connectionMessage = connMess;
|
client!.connectionMessage = connMess;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
debugPrint('MQTT: Connecting to HiveMQ...');
|
|
||||||
await client!.connect();
|
await client!.connect();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('MQTT Exception: $e');
|
debugPrint('MQTT Exception: $e');
|
||||||
onConnectionResult?.call('Koneksi Gagal: $e', false);
|
onConnectionResult?.call('Koneksi Gagal: $e', false);
|
||||||
client!.disconnect();
|
client!.disconnect();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client?.connectionStatus?.state == MqttConnectionState.connected) {
|
if (client?.connectionStatus?.state == MqttConnectionState.connected) {
|
||||||
debugPrint('MQTT: SUCCESS!');
|
debugPrint('MQTT: Terhubung!');
|
||||||
onConnectionResult?.call('✅ Terhubung ke Server Kopi', true);
|
onConnectionResult?.call('✅ Terhubung ke Server Kopi', true);
|
||||||
_subscribeToTopics();
|
_subscribeToTopics();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
onConnectionResult?.call('❌ Koneksi Terputus', false);
|
onConnectionResult?.call('❌ Koneksi Gagal', false);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _subscribeToTopics() {
|
void _subscribeToTopics() {
|
||||||
client!.subscribe("coffee/temp", MqttQos.atMostOnce);
|
client!.subscribe("kopi/sensor", MqttQos.atMostOnce);
|
||||||
client!.subscribe("coffee/humidity", MqttQos.atMostOnce);
|
client!.subscribe("kopi/relay/kipas1", MqttQos.atMostOnce);
|
||||||
client!.subscribe("coffee/light", MqttQos.atMostOnce);
|
client!.subscribe("kopi/relay/kipas2", MqttQos.atMostOnce);
|
||||||
client!.subscribe("coffee/intake", MqttQos.atMostOnce);
|
client!.subscribe("kopi/relay/mode", MqttQos.atMostOnce);
|
||||||
client!.subscribe("coffee/exhaust", MqttQos.atMostOnce);
|
|
||||||
client!.subscribe("coffee/config/mode", MqttQos.atMostOnce);
|
|
||||||
|
|
||||||
client!.updates!.listen((List<MqttReceivedMessage<MqttMessage?>>? c) {
|
client!.updates!.listen((List<MqttReceivedMessage<MqttMessage?>>? c) {
|
||||||
final recMess = c![0].payload as MqttPublishMessage;
|
if (c == null || c.isEmpty) return;
|
||||||
final pt = MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
|
|
||||||
|
final recMess = c[0].payload as MqttPublishMessage;
|
||||||
|
final pt = MqttPublishPayload.bytesToStringAsString(
|
||||||
|
recMess.payload.message);
|
||||||
final topic = c[0].topic;
|
final topic = c[0].topic;
|
||||||
|
|
||||||
if (topic == "coffee/temp") _temp = pt;
|
if (topic == "kopi/sensor") {
|
||||||
if (topic == "coffee/humidity") _humidity = pt;
|
try {
|
||||||
if (topic == "coffee/light") _light = pt;
|
final data = jsonDecode(pt);
|
||||||
if (topic == "coffee/intake") _isIntakeOn = (pt == "ON");
|
_suhu = data['suhu'].toString();
|
||||||
if (topic == "coffee/exhaust") _isExhaustOn = (pt == "ON");
|
_kelembapan = data['kelembapan'].toString();
|
||||||
if (topic == "coffee/config/mode") _currentMode = pt;
|
_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();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void publish(String topic, String message) {
|
// Kirim perintah kipas manual
|
||||||
if (topic == "coffee/intake") _isIntakeOn = (message == "ON");
|
void perintahKipas(String nomorKipas, bool nyala) {
|
||||||
if (topic == "coffee/exhaust") _isExhaustOn = (message == "ON");
|
final payload = jsonEncode({
|
||||||
notifyListeners();
|
"kipas": nomorKipas,
|
||||||
|
"status": nyala ? "ON" : "OFF",
|
||||||
|
});
|
||||||
|
_publish("kopi/relay/manual", payload);
|
||||||
|
}
|
||||||
|
|
||||||
if (client?.connectionStatus?.state == MqttConnectionState.connected) {
|
// Ganti mode otomatis/manual
|
||||||
final builder = MqttClientPayloadBuilder();
|
void gantiMode(String mode) {
|
||||||
builder.addString(message);
|
_publish("kopi/relay/mode", mode);
|
||||||
client!.publishMessage(topic, MqttQos.exactlyOnce, builder.payload!);
|
}
|
||||||
|
|
||||||
|
// 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() {
|
void onConnected() {
|
||||||
|
debugPrint('MQTT: onConnected');
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDisconnected() {
|
void onDisconnected() {
|
||||||
|
debugPrint('MQTT: onDisconnected');
|
||||||
|
|
||||||
|
// 🔥 AUTO RECONNECT
|
||||||
|
Future.delayed(const Duration(seconds: 3), () {
|
||||||
|
debugPrint('MQTT: mencoba reconnect...');
|
||||||
|
connect();
|
||||||
|
});
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateFromLastLog(String t, String h, String l, bool i, bool e) {
|
void disconnect() {
|
||||||
_temp = t;
|
client?.disconnect();
|
||||||
_humidity = h;
|
|
||||||
_light = l;
|
|
||||||
_isIntakeOn = i;
|
|
||||||
_isExhaustOn = e;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,11 @@ import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import app_links
|
import app_links
|
||||||
import path_provider_foundation
|
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
482
pubspec.lock
482
pubspec.lock
|
|
@ -5,82 +5,122 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: app_links
|
name: app_links
|
||||||
sha256: "3ced568a5d9e309e99af71285666f1f3117bddd0bd5b3317979dccc1a40cada4"
|
sha256: "3462d9defc61565fde4944858b59bec5be2b9d5b05f20aed190adb3ad08a7abc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.13.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.1"
|
||||||
charcode:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: charcode
|
|
||||||
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.4.0"
|
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: clock
|
name: clock
|
||||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.7"
|
||||||
csslib:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: csslib
|
|
||||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.2"
|
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
equatable:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -101,18 +141,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.3"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -125,10 +173,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: fl_chart
|
name: fl_chart
|
||||||
sha256: c1e26c7e48496be85104c16c040950b0436674cdf0737f3f6e95511b2529b592
|
sha256: b938f77d042cbcd822936a7a359a7235bad8bd72070de1f827efc2cc297ac888
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.63.0"
|
version: "1.2.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -138,10 +186,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_lints
|
||||||
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "6.0.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -156,58 +204,82 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: functions_client
|
name: functions_client
|
||||||
sha256: a49876ebae32a50eb62483c5c5ac80ed0d8da34f98ccc23986b03a8d28cee07c
|
sha256: "94074d62167ae634127ef6095f536835063a7dc80f2b1aa306d2346ff9023996"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
gotrue:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: gotrue
|
name: gotrue
|
||||||
sha256: d6362dff9a54f8c1c372bb137c858b4024c16407324d34e6473e59623c9b9f50
|
sha256: "7a4172601553e61716f5c3dd243aa3297e13308e07eb85b7853c941ba585dcf5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.1"
|
version: "2.20.0"
|
||||||
gtk:
|
gtk:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: gtk
|
name: gtk
|
||||||
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
sha256: "4ff85b2a16724029dd9e5bbb5a94b6918f9973f74ba571c949d2002801879cf5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.2.0"
|
||||||
html:
|
hooks:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: html
|
name: hooks
|
||||||
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
sha256: "025f060e86d2d4c3c47b56e33caf7f93bf9283340f26d23424ebcfccf34f621e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.4"
|
version: "1.0.3"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.6.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.1.2"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
jwt_decode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -216,62 +288,94 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.1"
|
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:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: lints
|
name: lints
|
||||||
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "6.1.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: logging
|
name: logging
|
||||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.16"
|
version: "0.12.19"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.13.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.17.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "2.0.0"
|
||||||
mqtt_client:
|
mqtt_client:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mqtt_client
|
name: mqtt_client
|
||||||
sha256: "930a951a748a3feacd57bde9a608740c724f7afb0e1be1ef56f3e1fe9f689cf9"
|
sha256: "41c8edd3bc8efc80c1c8ebfb40081c24d12d13085faca96b9280a624eca2d893"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -280,38 +384,54 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
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:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.3"
|
version: "1.9.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.5"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "51f0d2c554cfbc9d6a312ab35152fc77e2f0b758ce9f1a444a3a1e5b8f3c6b7f"
|
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.3"
|
version: "2.3.1"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
|
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.6.0"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -332,18 +452,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.3.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "3.1.6"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -352,14 +472,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
|
pointycastle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pointycastle
|
||||||
|
sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
postgrest:
|
postgrest:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: postgrest
|
name: postgrest
|
||||||
sha256: b74dc0f57b5dca5ce9f57a54b08110bf41d6fc8a0483c0fec10c79e9aa0fb2bb
|
sha256: "9d61b3d4a88fcf9424d400127c54d49ed1b56ec30838fc0a33a64f31d4e694cc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.7.0"
|
||||||
provider:
|
provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -368,14 +496,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.5+1"
|
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:
|
realtime_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: realtime_client
|
name: realtime_client
|
||||||
sha256: e3089dac2121917cc0c72d42ab056fea0abbaf3c2229048fc50e64bafc731adf
|
sha256: "7dfccf372d2f55aacfeefb6186f65a06f3ffae383fe042dbeef9d85d33487576"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
retry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -396,199 +540,183 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.3"
|
version: "2.5.5"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.4.23"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_foundation
|
name: shared_preferences_foundation
|
||||||
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.5"
|
version: "2.5.6"
|
||||||
shared_preferences_linux:
|
shared_preferences_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_linux
|
name: shared_preferences_linux
|
||||||
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.4.1"
|
||||||
shared_preferences_platform_interface:
|
shared_preferences_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_platform_interface
|
name: shared_preferences_platform_interface
|
||||||
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.4.2"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_web
|
name: shared_preferences_web
|
||||||
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
|
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.4.3"
|
||||||
shared_preferences_windows:
|
shared_preferences_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_windows
|
name: shared_preferences_windows
|
||||||
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.4.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.99"
|
version: "0.0.0"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.12.1"
|
||||||
storage_client:
|
storage_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: storage_client
|
name: storage_client
|
||||||
sha256: "9f9ed283943313b23a1b27139bb18986e9b152a6d34530232c702c468d98e91a"
|
sha256: "4801e8ca219a35e51cbb30589aba5306667ae8935b792504595a45273cef0b18"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.5.2"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.4"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.4.1"
|
||||||
supabase:
|
supabase:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: supabase
|
name: supabase
|
||||||
sha256: c3ebddba69ddcf16d8b78e8c44c4538b0193d1cf944fde3b72eb5b279892a370
|
sha256: "40e5a8833c8834e140ef53b60a6181849667eba9ca125acb7f8e24c6a769d418"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.3"
|
version: "2.10.6"
|
||||||
supabase_flutter:
|
supabase_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: supabase_flutter
|
name: supabase_flutter
|
||||||
sha256: "3b5b5b492e342f63f301605d0c66f6528add285b5744f53c9fd9abd5ffdbce5b"
|
sha256: c02ce58abcaf86cb8055ad40bfd98bbf5b93fed3b5b56b8220d88ed03842818b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.4"
|
version: "2.12.4"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.2"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.7.10"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.4.0"
|
||||||
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"
|
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
|
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.4"
|
version: "6.3.2"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
|
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.3.29"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.2.4"
|
version: "6.4.1"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.2.2"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
|
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.5"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -601,66 +729,90 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
|
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.4.2"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_windows
|
name: url_launcher_windows
|
||||||
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.5"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "3.0.3"
|
||||||
win32:
|
win32:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067
|
sha256: ba7d5750e3441caa1bbe31d9e516348fcf8dfcb32aa29ef87a844a59f4d1f1d0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.8"
|
version: "6.1.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
yet_another_json_isolate:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yet_another_json_isolate
|
name: yet_another_json_isolate
|
||||||
sha256: "56155e9e0002cc51ea7112857bbcdc714d4c35e176d43e4d3ee233009ff410c9"
|
sha256: fe45897501fa156ccefbfb9359c9462ce5dec092f05e8a56109db30be864f01e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.1.0 <4.0.0"
|
dart: ">=3.10.3 <4.0.0"
|
||||||
flutter: ">=3.13.0"
|
flutter: ">=3.38.4"
|
||||||
|
|
|
||||||
16
pubspec.yaml
16
pubspec.yaml
|
|
@ -30,16 +30,16 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.9
|
||||||
supabase_flutter: ^2.5.0
|
supabase_flutter: ^2.12.4
|
||||||
mqtt_client: ^10.2.0
|
mqtt_client: ^10.11.11
|
||||||
provider: ^6.1.1
|
provider: ^6.1.1
|
||||||
fl_chart: 0.63.0
|
fl_chart: ^1.2.0
|
||||||
intl: ^0.18.1
|
intl: ^0.20.2
|
||||||
shared_preferences: ^2.2.0
|
shared_preferences: ^2.5.5
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
win32: 5.0.8
|
win32: ^6.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
@ -50,7 +50,7 @@ dev_dependencies:
|
||||||
# activated in the `analysis_options.yaml` file located at the root of your
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# 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
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue