From d5b6d8c4b247f50dfcaf94f72b1c0d9e9c6703bf Mon Sep 17 00:00:00 2001 From: "M.Nizar Fahrurrozi" Date: Tue, 28 Jul 2026 12:10:36 +0700 Subject: [PATCH] Update codingan --- lib/screens/recording_screen.dart | 581 ++++++++++++++---- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 146 +++-- pubspec.yaml | 6 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 6 files changed, 533 insertions(+), 206 deletions(-) diff --git a/lib/screens/recording_screen.dart b/lib/screens/recording_screen.dart index 11258ce..908ede0 100644 --- a/lib/screens/recording_screen.dart +++ b/lib/screens/recording_screen.dart @@ -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 { final TextEditingController _intervalController = TextEditingController(); List> _gallery = []; bool _isLoading = true; - + bool _isSelectionMode = false; + final Set _selectedIndices = {}; // Menyimpan index foto yang dicentang + final recordingService = RecordingService(); late final RealtimeChannel _channel; @@ -33,7 +41,7 @@ class _RecordingScreenState extends State { schema: 'public', table: 'foto_dataset', callback: (payload) { - _loadData(); + _loadData(); }, ) .subscribe(); @@ -50,12 +58,12 @@ class _RecordingScreenState extends State { 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 { 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 { @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 { 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 { ), 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 { 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 { 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 { ), ); } -} \ No newline at end of file + + // ===================================================================== + // --- FITUR BARU: Fungsi Membuat ZIP dan Ekspor --- + // ===================================================================== + Future _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), + ); + } + } + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 625df93..09e9dfc 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -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")) } diff --git a/pubspec.lock b/pubspec.lock index f332c76..ab1d523 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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" diff --git a/pubspec.yaml b/pubspec.yaml index 0d97b2f..2759627 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 7fa44f6..6afae54 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include 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")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 9139445..47a8a1a 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST app_links printing + share_plus url_launcher_windows )