Update codingan

This commit is contained in:
M.Nizar Fahrurrozi 2026-07-28 12:10:36 +07:00
parent 3d4741b535
commit d5b6d8c4b2
6 changed files with 533 additions and 206 deletions

View File

@ -3,8 +3,14 @@ import 'package:provider/provider.dart';
import '../services/mqtt_service.dart';
import '../services/supabase_service.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import '../constants/app_colors.dart';
import '../constants/app_colors.dart';
import '../services/recording_service.dart'; // Memori Abadi Timer Tetap Dipakai
import 'dart:io';
import 'dart:convert';
import 'package:archive/archive_io.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:http/http.dart' as http;
class RecordingScreen extends StatefulWidget {
const RecordingScreen({super.key});
@ -17,7 +23,9 @@ class _RecordingScreenState extends State<RecordingScreen> {
final TextEditingController _intervalController = TextEditingController();
List<Map<String, dynamic>> _gallery = [];
bool _isLoading = true;
bool _isSelectionMode = false;
final Set<int> _selectedIndices = {}; // Menyimpan index foto yang dicentang
final recordingService = RecordingService();
late final RealtimeChannel _channel;
@ -33,7 +41,7 @@ class _RecordingScreenState extends State<RecordingScreen> {
schema: 'public',
table: 'foto_dataset',
callback: (payload) {
_loadData();
_loadData();
},
)
.subscribe();
@ -50,12 +58,12 @@ class _RecordingScreenState extends State<RecordingScreen> {
setState(() => _isLoading = true);
try {
final interval = await SupabaseService().getIntervalSetting();
final images = await SupabaseService().getFotoDataset();
final images = await SupabaseService().getFotoDataset();
if (mounted) {
setState(() {
_intervalController.text = interval.toString();
_gallery = images;
_isLoading = false;
_gallery = images.reversed.toList();
_isLoading = false;
});
}
} catch (e) {
@ -94,7 +102,8 @@ class _RecordingScreenState extends State<RecordingScreen> {
start
? 'Perekaman dimulai — interval $intervalVal menit'
: 'Perekaman dihentikan',
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
style: const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
backgroundColor: start ? AppColors.success : AppColors.error,
),
@ -105,20 +114,39 @@ class _RecordingScreenState extends State<RecordingScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: _selectedIndices.isNotEmpty
? FloatingActionButton.extended(
onPressed: () => _downloadDatasetZip(),
backgroundColor: AppColors.primary,
icon: const Icon(Icons.download, color: Colors.white),
label: Text(
'Download ZIP (${_selectedIndices.length})',
style: const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
)
: null,
backgroundColor: AppColors.background,
body: SafeArea(
child: _isLoading
? const Center(child: CircularProgressIndicator(color: AppColors.primary))
? const Center(
child: CircularProgressIndicator(color: AppColors.primary))
: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Perekaman',
style: TextStyle(color: AppColors.textMain, fontSize: 32, fontWeight: FontWeight.bold),
const Text(
'Perekaman',
style: TextStyle(
color: AppColors.textMain,
fontSize: 32,
fontWeight: FontWeight.bold),
),
const Text('Dataset Citra Kopi',
style: TextStyle(color: AppColors.textSecondary, fontSize: 16),
const Text(
'Dataset Citra Kopi',
style: TextStyle(
color: AppColors.textSecondary, fontSize: 16),
),
const SizedBox(height: 20),
@ -133,79 +161,110 @@ class _RecordingScreenState extends State<RecordingScreen> {
decoration: BoxDecoration(
color: AppColors.cardBg,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColors.textSecondary.withValues(alpha: 0.15)),
border: Border.all(
color: AppColors.textSecondary
.withValues(alpha: 0.15)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Interval Waktu (menit)',
style: TextStyle(color: AppColors.textMain, fontWeight: FontWeight.bold),
const Text(
'Interval Waktu (menit)',
style: TextStyle(
color: AppColors.textMain,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
TextField(
controller: _intervalController,
keyboardType: TextInputType.number,
style: const TextStyle(color: AppColors.textMain),
style:
const TextStyle(color: AppColors.textMain),
decoration: InputDecoration(
hintText: '30',
hintStyle: const TextStyle(color: AppColors.textSecondary),
hintStyle: const TextStyle(
color: AppColors.textSecondary),
filled: true,
fillColor: AppColors.background,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: AppColors.textSecondary.withValues(alpha: 0.3)),
borderSide: BorderSide(
color: AppColors.textSecondary
.withValues(alpha: 0.3)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(color: AppColors.primary),
borderSide: const BorderSide(
color: AppColors.primary),
),
),
),
const SizedBox(height: 15),
if (isRecording)
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppColors.success.withValues(alpha: 0.15),
color: AppColors.success
.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: AppColors.success),
border:
Border.all(color: AppColors.success),
),
child: const Row(
children: [
Icon(Icons.circle, color: AppColors.success, size: 10),
Icon(Icons.circle,
color: AppColors.success, size: 10),
SizedBox(width: 8),
Text('Perekaman berjalan...',
style: TextStyle(color: AppColors.success, fontSize: 12, fontWeight: FontWeight.bold)),
style: TextStyle(
color: AppColors.success,
fontSize: 12,
fontWeight: FontWeight.bold)),
],
),
),
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)),
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: AppColors.primary,
disabledBackgroundColor: AppColors.primary.withValues(alpha: 0.3),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
disabledBackgroundColor: AppColors
.primary
.withValues(alpha: 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)),
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: AppColors.error,
disabledBackgroundColor: AppColors.error.withValues(alpha: 0.3),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
disabledBackgroundColor: AppColors.error
.withValues(alpha: 0.3),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(12)),
),
),
),
@ -218,16 +277,65 @@ class _RecordingScreenState extends State<RecordingScreen> {
),
const SizedBox(height: 20),
// KODE BARU (Sudah Termasuk Tombol Checklist & Select All):
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Galeri Citra Dataset (${_gallery.length} foto)',
style: const TextStyle(color: AppColors.textMain, fontSize: 18, fontWeight: FontWeight.bold),
),
IconButton(
icon: const Icon(Icons.refresh, color: AppColors.primary),
onPressed: _loadData,
Expanded(
child: Text(
'Galeri Dataset (${_gallery.length} foto)',
style: const TextStyle(
color: AppColors.textMain,
fontSize: 18,
fontWeight: FontWeight.bold),
),
),
Row(
children: [
if (_isSelectionMode) ...[
IconButton(
icon: const Icon(Icons.select_all,
color: AppColors.primary),
tooltip: 'Pilih Semua',
onPressed: () {
setState(() {
if (_selectedIndices.length ==
_gallery.length) {
_selectedIndices
.clear(); // Batal pilih semua
} else {
_selectedIndices.addAll(List.generate(
_gallery.length,
(i) => i)); // Pilih semua
}
});
},
),
IconButton(
icon: const Icon(Icons.close,
color: AppColors.error),
tooltip: 'Batal Mode Seleksi',
onPressed: () => setState(() {
_isSelectionMode = false;
_selectedIndices.clear();
}),
),
] else ...[
IconButton(
icon: const Icon(Icons.checklist,
color: AppColors.primary),
tooltip: 'Pilih Foto',
onPressed: () =>
setState(() => _isSelectionMode = true),
),
IconButton(
icon: const Icon(Icons.refresh,
color: AppColors.primary),
onPressed: _loadData,
),
],
],
)
],
),
const SizedBox(height: 10),
@ -235,10 +343,13 @@ class _RecordingScreenState extends State<RecordingScreen> {
Expanded(
child: _gallery.isEmpty
? const Center(
child: Text('Belum ada data citra', style: TextStyle(color: AppColors.textSecondary)),
child: Text('Belum ada data citra',
style: TextStyle(
color: AppColors.textSecondary)),
)
: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
@ -246,98 +357,222 @@ class _RecordingScreenState extends State<RecordingScreen> {
itemCount: _gallery.length,
itemBuilder: (context, index) {
final item = _gallery[index];
final nomorFoto = index + 1;
// PENYARING WAKTU: Ubah 'T' jadi Spasi agar kodingan lamamu aman
String rawTime = item['timestamp'] ?? item['created_at'] ?? '';
String cleanTime = rawTime.replaceAll('T', ' ');
final nomorFoto = _gallery.length - index;
final tanggal = cleanTime.contains(' ') ? cleanTime.split(' ')[0] : cleanTime;
final jam = cleanTime.contains(' ') ? cleanTime.split(' ')[1] : '';
final jamSingkat = jam.length >= 5 ? jam.substring(0, 5) : jam;
// PENYARING WAKTU: Ubah 'T' jadi Spasi agar kodingan lamamu aman
String rawTime = item['timestamp'] ??
item['created_at'] ??
'';
String cleanTime = rawTime.replaceAll('T', ' ');
final tanggal = cleanTime.contains(' ')
? cleanTime.split(' ')[0]
: cleanTime;
final jam = cleanTime.contains(' ')
? cleanTime.split(' ')[1]
: '';
final jamSingkat =
jam.length >= 5 ? jam.substring(0, 5) : jam;
return GestureDetector(
onTap: () => Navigator.pushNamed(
context,
'/image_result',
arguments: {
'id': item['id'].toString(),
'date': tanggal,
'time': jamSingkat,
// PENYARING SENSOR 0: Jika 0, ubah jadi strip '-'
'temp': (item['suhu'] == 0 || item['suhu'] == null) ? '-' : '${item['suhu']} °C',
'humidity': (item['kelembapan'] == 0 || item['kelembapan'] == null) ? '-' : '${item['kelembapan']} %',
'light': (item['intensitas'] == 0 || item['intensitas'] == null) ? '-' : '${item['intensitas']} Lux',
'location': 'Gudang Pengering',
'intake': item['kipas1'] ?? 'OFF',
'exhaust': item['kipas2'] ?? 'OFF',
'url_foto': item['url_foto'] ?? '',
},
),
child: Container(
decoration: BoxDecoration(
color: AppColors.cardBg,
borderRadius: BorderRadius.circular(15),
border: Border.all(color: AppColors.textSecondary.withValues(alpha: 0.15)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
flex: 3,
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(14)),
child: item['url_foto'] != null
? Image.network(
item['url_foto'],
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const Center(
child: Icon(Icons.broken_image, color: AppColors.textSecondary, size: 40),
),
loadingBuilder: (_, child, progress) {
if (progress == null) return child;
return const Center(
child: CircularProgressIndicator(color: AppColors.primary, strokeWidth: 2),
);
},
)
: const Center(
child: Icon(Icons.image, color: AppColors.textSecondary, size: 40),
),
// --- FITUR BARU: Logika Tahan & Klik ---
onLongPress: () {
setState(() {
_isSelectionMode = true;
_selectedIndices.add(index);
});
},
onTap: () {
if (_isSelectionMode) {
setState(() {
if (_selectedIndices.contains(index)) {
_selectedIndices.remove(index);
} else {
_selectedIndices.add(index);
}
});
} else {
// KODE LAMAMU TETAP AMAN DI SINI (Navigasi ke Detail)
Navigator.pushNamed(
context,
'/image_result',
arguments: {
'id': item['id'].toString(),
'date': tanggal,
'time': jamSingkat,
'temp': (item['suhu'] == 0 ||
item['suhu'] == null)
? '-'
: '${item['suhu']} °C',
'humidity': (item['kelembapan'] ==
0 ||
item['kelembapan'] == null)
? '-'
: '${item['kelembapan']} %',
'light': (item['intensitas'] == 0 ||
item['intensitas'] == null)
? '-'
: '${item['intensitas']} Lux',
'location': 'Gudang Pengering',
'intake': item['kipas1'] ?? 'OFF',
'exhaust': item['kipas2'] ?? 'OFF',
'url_foto': item['url_foto'] ?? '',
},
);
}
},
child: Stack(
children: [
// KODE WADAH FOTO LAMAMU (Hanya ditambah opacity saat dicentang)
Opacity(
opacity:
_selectedIndices.contains(index)
? 0.6
: 1.0,
child: Container(
decoration: BoxDecoration(
color: AppColors.cardBg,
borderRadius:
BorderRadius.circular(15),
border: Border.all(
color: _selectedIndices
.contains(index)
? AppColors.primary
: AppColors.textSecondary
.withValues(alpha: 0.15),
width: _selectedIndices
.contains(index)
? 2.0
: 1.0,
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: const BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(15)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: AppColors.primary.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: AppColors.primary.withValues(alpha: 0.5)),
),
child: Text('#$nomorFoto',
style: const TextStyle(color: AppColors.primary, fontSize: 10, fontWeight: FontWeight.bold),
Expanded(
flex: 3,
child: ClipRRect(
borderRadius:
const BorderRadius
.vertical(
top: Radius.circular(
14)),
child: item['url_foto'] !=
null
? Image.network(
item['url_foto'],
fit: BoxFit.cover,
errorBuilder:
(_, __, ___) =>
const Center(
child: Icon(
Icons
.broken_image,
color: AppColors
.textSecondary,
size: 40),
),
)
: const Center(
child: Icon(
Icons.image,
color: AppColors
.textSecondary,
size: 40),
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(tanggal, style: const TextStyle(color: AppColors.textSecondary, fontSize: 8)),
Text(jamSingkat, style: const TextStyle(color: AppColors.textMain, fontSize: 10, fontWeight: FontWeight.bold)),
],
Container(
padding:
const EdgeInsets.symmetric(
horizontal: 8,
vertical: 6),
decoration: const BoxDecoration(
color: Colors.transparent,
borderRadius:
BorderRadius.vertical(
bottom:
Radius.circular(
15)),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Container(
padding: const EdgeInsets
.symmetric(
horizontal: 6,
vertical: 2),
decoration: BoxDecoration(
color: AppColors.primary
.withValues(
alpha: 0.15),
borderRadius:
BorderRadius
.circular(6),
),
child: Text(
'#$nomorFoto',
style: const TextStyle(
color: AppColors
.primary,
fontSize: 10,
fontWeight:
FontWeight
.bold),
),
),
Column(
crossAxisAlignment:
CrossAxisAlignment
.end,
children: [
Text(tanggal,
style: const TextStyle(
color: AppColors
.textSecondary,
fontSize: 8)),
Text(jamSingkat,
style: const TextStyle(
color: AppColors
.textMain,
fontSize: 10,
fontWeight:
FontWeight
.bold)),
],
),
],
),
),
],
),
),
],
),
),
// --- FITUR BARU: Ikon Centang ---
if (_isSelectionMode)
Positioned(
top: 8,
right: 8,
child: Icon(
_selectedIndices.contains(index)
? Icons.check_circle
: Icons.radio_button_unchecked,
color:
_selectedIndices.contains(index)
? AppColors.primary
: Colors.white,
shadows: const [
Shadow(
blurRadius: 2.0,
color: Colors.black45)
],
),
),
],
),
);
},
@ -349,4 +584,100 @@ class _RecordingScreenState extends State<RecordingScreen> {
),
);
}
}
// =====================================================================
// --- FITUR BARU: Fungsi Membuat ZIP dan Ekspor ---
// =====================================================================
Future<void> _downloadDatasetZip() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(color: AppColors.primary),
const SizedBox(height: 20),
Text('Mengunduh & Membungkus ${_selectedIndices.length} Data...',
textAlign: TextAlign.center),
],
),
),
);
try {
final archive = Archive();
// Membuat header (judul kolom) untuk file Excel/CSV
String csvContent =
"Nama_File,Tanggal,Jam,Suhu(C),Kelembapan(%),Intensitas_Cahaya(Lux),Kipas_1,Kipas_2\n";
for (int index in _selectedIndices) {
final item = _gallery[index];
// Membersihkan format waktu
String rawTime = item['timestamp'] ?? item['created_at'] ?? '';
String cleanTime = rawTime.replaceAll('T', ' ');
final tanggal =
cleanTime.contains(' ') ? cleanTime.split(' ')[0] : cleanTime;
final jam = cleanTime.contains(' ') ? cleanTime.split(' ')[1] : '';
final jamSingkat = jam.length >= 5 ? jam.substring(0, 5) : jam;
// Nama file gambar
String fileName =
'IMG_${tanggal}_${jamSingkat.replaceAll(':', '-')}.jpg';
// Unduh gambar dari Supabase
if (item['url_foto'] != null &&
item['url_foto'].toString().isNotEmpty) {
final response = await http.get(Uri.parse(item['url_foto']));
if (response.statusCode == 200) {
archive.addFile(ArchiveFile(
fileName, response.bodyBytes.length, response.bodyBytes));
}
}
// Tambah baris data ke Excel/CSV
csvContent +=
"$fileName,$tanggal,$jamSingkat,${item['suhu']},${item['kelembapan']},${item['intensitas']},${item['kipas1'] ?? 'OFF'},${item['kipas2'] ?? 'OFF'}\n";
}
// Masukkan file CSV ke dalam ZIP
final csvBytes = utf8.encode(csvContent);
archive.addFile(
ArchiveFile("Data_Sensor_Dataset.csv", csvBytes.length, csvBytes));
// Proses kompresi file ZIP
final zipBytes = ZipEncoder().encode(archive);
// Simpan sementara di memori HP
final tempDir = await getTemporaryDirectory();
final zipPath = '${tempDir.path}/Dataset_Kopi_IoT.zip';
final zipFile = File(zipPath);
await zipFile.writeAsBytes(zipBytes!);
if (mounted) Navigator.pop(context); // Tutup loading dialog
// Buka popup Share/Save untuk disimpan ke HP pengguna
await Share.shareXFiles(
[XFile(zipPath)],
text:
'Ini adalah Dataset Kopi berformat ZIP. Ekstrak untuk melihat foto dan tabel Excel.',
);
// Reset mode seleksi
setState(() {
_isSelectionMode = false;
_selectedIndices.clear();
});
} catch (e) {
if (mounted) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Gagal membuat ZIP: $e'),
backgroundColor: AppColors.error),
);
}
}
}
}

View File

@ -8,6 +8,7 @@ import Foundation
import app_links
import open_file_mac
import printing
import share_plus
import shared_preferences_foundation
import url_launcher_macos
@ -15,6 +16,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin"))
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View File

@ -42,7 +42,7 @@ packages:
source: hosted
version: "1.0.4"
archive:
dependency: transitive
dependency: "direct main"
description:
name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
@ -125,10 +125,10 @@ packages:
dependency: transitive
description:
name: code_assets
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "1.2.1"
collection:
dependency: transitive
description:
@ -149,10 +149,10 @@ packages:
dependency: transitive
description:
name: cross_file
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51"
url: "https://pub.dev"
source: hosted
version: "0.3.5+2"
version: "0.3.5+4"
crypto:
dependency: transitive
description:
@ -189,10 +189,10 @@ packages:
dependency: transitive
description:
name: equatable
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
sha256: "3bce007a596ff8b3119c45d68aaef631272537c03d30e5d4534dd24bf4c5eaa2"
url: "https://pub.dev"
source: hosted
version: "2.0.8"
version: "2.1.0"
event_bus:
dependency: transitive
description:
@ -225,14 +225,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
ffi_leak_tracker:
dependency: transitive
description:
name: ffi_leak_tracker
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
file:
dependency: transitive
description:
@ -300,18 +292,10 @@ packages:
dependency: transitive
description:
name: functions_client
sha256: "94074d62167ae634127ef6095f536835063a7dc80f2b1aa306d2346ff9023996"
sha256: e9685e9ab852a8b8e0579c867f6c9155da27317b294b3df796a536b8b8e0d253
url: "https://pub.dev"
source: hosted
version: "2.5.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.6.4"
google_fonts:
dependency: "direct main"
description:
@ -324,10 +308,10 @@ packages:
dependency: transitive
description:
name: gotrue
sha256: "7a4172601553e61716f5c3dd243aa3297e13308e07eb85b7853c941ba585dcf5"
sha256: "360bba9606e58d5acc59a033ab64ae3cf571a6868f7a7267cb8e9a204b7bf1b2"
url: "https://pub.dev"
source: hosted
version: "2.20.0"
version: "2.26.0"
gtk:
dependency: transitive
description:
@ -340,10 +324,10 @@ packages:
dependency: transitive
description:
name: hooks
sha256: "025f060e86d2d4c3c47b56e33caf7f93bf9283340f26d23424ebcfccf34f621e"
sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
version: "2.0.2"
html:
dependency: transitive
description:
@ -380,10 +364,10 @@ packages:
dependency: "direct main"
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
version: "0.20.3"
jni:
dependency: transitive
description:
@ -496,14 +480,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "10.11.11"
native_toolchain_c:
dependency: transitive
description:
name: native_toolchain_c
sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572"
url: "https://pub.dev"
source: hosted
version: "0.17.6"
nested:
dependency: transitive
description:
@ -516,10 +492,10 @@ packages:
dependency: transitive
description:
name: objective_c
sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed"
url: "https://pub.dev"
source: hosted
version: "9.3.0"
version: "9.4.1"
open_file:
dependency: "direct main"
description:
@ -532,18 +508,18 @@ packages:
dependency: transitive
description:
name: open_file_android
sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa"
sha256: f48a342b0347bf82bebf68c752f5cfce162965ce8449c5ecdafc406d294aa63d
url: "https://pub.dev"
source: hosted
version: "1.0.6"
version: "1.1.0"
open_file_ios:
dependency: transitive
description:
name: open_file_ios
sha256: a5acd07ba1f304f807a97acbcc489457e1ad0aadff43c467987dd9eef814098f
sha256: "0e3e67dcd12f69888128f533cacc092039627dd4f1d7a95a2826b814ae4ea8aa"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
version: "1.1.0"
open_file_linux:
dependency: transitive
description:
@ -556,18 +532,18 @@ packages:
dependency: transitive
description:
name: open_file_mac
sha256: cd293f6750de6438ab2390513c99128ade8c974825d4d8128886d1cda8c64d01
sha256: "1abe00e65f792b9baa1e15fdf4d44af5e813b1696ac307d71535543122a01e30"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
version: "1.1.0"
open_file_platform_interface:
dependency: transitive
description:
name: open_file_platform_interface
sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757"
sha256: dc302e4bd5c1d77acec4240bd0dbc1d9f2ee6882b1ce52bca5298369da7acf12
url: "https://pub.dev"
source: hosted
version: "1.0.3"
version: "1.1.0"
open_file_web:
dependency: transitive
description:
@ -592,6 +568,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
passkeys_platform_interface:
dependency: transitive
description:
name: passkeys_platform_interface
sha256: "9610bd136b3382500390912ddd8517ee99505228b1af7b507b9c907f7e99a47d"
url: "https://pub.dev"
source: hosted
version: "2.8.0"
path:
dependency: transitive
description:
@ -612,10 +596,10 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.1.6"
path_provider_android:
dependency: transitive
description:
@ -636,18 +620,18 @@ packages:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.2"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.3"
path_provider_windows:
dependency: transitive
description:
@ -708,10 +692,10 @@ packages:
dependency: transitive
description:
name: postgrest
sha256: "9d61b3d4a88fcf9424d400127c54d49ed1b56ec30838fc0a33a64f31d4e694cc"
sha256: "10e3f195e131eec944fa872644aed89b33b5be998f3fc77c87325db3927bc646"
url: "https://pub.dev"
source: hosted
version: "2.7.0"
version: "2.8.0"
printing:
dependency: "direct main"
description:
@ -748,10 +732,10 @@ packages:
dependency: transitive
description:
name: realtime_client
sha256: "7dfccf372d2f55aacfeefb6186f65a06f3ffae383fe042dbeef9d85d33487576"
sha256: "0cfbe0047e2591eba348938e9b4e620d5b1a8df6c374757e8eb8645252b8387f"
url: "https://pub.dev"
source: hosted
version: "2.7.3"
version: "2.11.0"
record_use:
dependency: transitive
description:
@ -776,8 +760,16 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.28.0"
share_plus:
dependency: "direct main"
description:
name: share_plus
sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900"
url: "https://pub.dev"
source: hosted
version: "7.2.2"
share_plus_platform_interface:
dependency: "direct overridden"
dependency: transitive
description:
name: share_plus_platform_interface
sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496"
@ -865,10 +857,10 @@ packages:
dependency: transitive
description:
name: storage_client
sha256: "4801e8ca219a35e51cbb30589aba5306667ae8935b792504595a45273cef0b18"
sha256: "221263cfbe0c01575b7d7fe7543e44abff380eb4d38d936372844a1caa85ba12"
url: "https://pub.dev"
source: hosted
version: "2.5.2"
version: "2.6.0"
stream_channel:
dependency: transitive
description:
@ -889,18 +881,18 @@ packages:
dependency: transitive
description:
name: supabase
sha256: "40e5a8833c8834e140ef53b60a6181849667eba9ca125acb7f8e24c6a769d418"
sha256: "3879996256325fcc9abb03b62b12c07e4eaae2e008e1bf043cc1525184159a43"
url: "https://pub.dev"
source: hosted
version: "2.10.6"
version: "2.14.0"
supabase_flutter:
dependency: "direct main"
description:
name: supabase_flutter
sha256: c02ce58abcaf86cb8055ad40bfd98bbf5b93fed3b5b56b8220d88ed03842818b
sha256: c9916c1cd512ebf3107ec0f83c9dca13d2e1e789629c2a5df896586bff46ce5f
url: "https://pub.dev"
source: hosted
version: "2.12.4"
version: "2.16.0"
term_glyph:
dependency: transitive
description:
@ -945,10 +937,10 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
sha256: "17bc677f0b301615530dd1d67e0a9828cafa2d0b6b6eae4cd3679b7eac4a273c"
url: "https://pub.dev"
source: hosted
version: "6.3.29"
version: "6.3.30"
url_launcher_ios:
dependency: transitive
description:
@ -985,10 +977,10 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.3"
url_launcher_windows:
dependency: transitive
description:
@ -1046,13 +1038,13 @@ packages:
source: hosted
version: "3.0.3"
win32:
dependency: "direct overridden"
dependency: transitive
description:
name: win32
sha256: ba7d5750e3441caa1bbe31d9e516348fcf8dfcb32aa29ef87a844a59f4d1f1d0
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
url: "https://pub.dev"
source: hosted
version: "6.1.0"
version: "5.15.0"
xdg_directories:
dependency: transitive
description:
@ -1081,10 +1073,10 @@ packages:
dependency: transitive
description:
name: yet_another_json_isolate
sha256: fe45897501fa156ccefbfb9359c9462ce5dec092f05e8a56109db30be864f01e
sha256: eaa26beb5990b25a49d942374fd5a0c5aa67a837e03b14b4c26134aaa1ed01a9
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
sdks:
dart: ">=3.10.3 <4.0.0"
flutter: ">=3.38.4"
dart: ">=3.11.0 <4.0.0"
flutter: ">=3.41.0"

View File

@ -44,10 +44,8 @@ dependencies:
open_file: ^3.3.2
path_provider: ^2.1.2
google_fonts: ^6.2.1
dependency_overrides:
win32: ^6.1.0
share_plus_platform_interface: ^3.4.0
archive: ^3.6.1 # Untuk membuat file ZIP
share_plus: ^7.2.2 # Untuk memunculkan popup "Save to Device / Bagikan"
dev_dependencies:
flutter_test:

View File

@ -8,6 +8,7 @@
#include <app_links/app_links_plugin_c_api.h>
#include <printing/printing_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
PrintingPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PrintingPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
app_links
printing
share_plus
url_launcher_windows
)