Update codingan
This commit is contained in:
parent
3d4741b535
commit
d5b6d8c4b2
|
|
@ -3,8 +3,14 @@ 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';
|
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 '../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 {
|
class RecordingScreen extends StatefulWidget {
|
||||||
const RecordingScreen({super.key});
|
const RecordingScreen({super.key});
|
||||||
|
|
@ -17,7 +23,9 @@ 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 _isSelectionMode = false;
|
||||||
|
final Set<int> _selectedIndices = {}; // Menyimpan index foto yang dicentang
|
||||||
|
|
||||||
final recordingService = RecordingService();
|
final recordingService = RecordingService();
|
||||||
late final RealtimeChannel _channel;
|
late final RealtimeChannel _channel;
|
||||||
|
|
||||||
|
|
@ -33,7 +41,7 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
schema: 'public',
|
schema: 'public',
|
||||||
table: 'foto_dataset',
|
table: 'foto_dataset',
|
||||||
callback: (payload) {
|
callback: (payload) {
|
||||||
_loadData();
|
_loadData();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
@ -50,12 +58,12 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
setState(() => _isLoading = true);
|
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();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_intervalController.text = interval.toString();
|
_intervalController.text = interval.toString();
|
||||||
_gallery = images;
|
_gallery = images.reversed.toList();
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -94,7 +102,8 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
start
|
start
|
||||||
? 'Perekaman dimulai — interval $intervalVal menit'
|
? 'Perekaman dimulai — interval $intervalVal menit'
|
||||||
: 'Perekaman dihentikan',
|
: '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,
|
backgroundColor: start ? AppColors.success : AppColors.error,
|
||||||
),
|
),
|
||||||
|
|
@ -105,20 +114,39 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
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,
|
backgroundColor: AppColors.background,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: _isLoading
|
child: _isLoading
|
||||||
? const Center(child: CircularProgressIndicator(color: AppColors.primary))
|
? const Center(
|
||||||
|
child: CircularProgressIndicator(color: AppColors.primary))
|
||||||
: Padding(
|
: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text('Perekaman',
|
const Text(
|
||||||
style: TextStyle(color: AppColors.textMain, fontSize: 32, fontWeight: FontWeight.bold),
|
'Perekaman',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.textMain,
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const Text('Dataset Citra Kopi',
|
const Text(
|
||||||
style: TextStyle(color: AppColors.textSecondary, fontSize: 16),
|
'Dataset Citra Kopi',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.textSecondary, fontSize: 16),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
|
@ -133,79 +161,110 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.cardBg,
|
color: AppColors.cardBg,
|
||||||
borderRadius: BorderRadius.circular(20),
|
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(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text('Interval Waktu (menit)',
|
const Text(
|
||||||
style: TextStyle(color: AppColors.textMain, fontWeight: FontWeight.bold),
|
'Interval Waktu (menit)',
|
||||||
|
style: TextStyle(
|
||||||
|
color: AppColors.textMain,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _intervalController,
|
controller: _intervalController,
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
style: const TextStyle(color: AppColors.textMain),
|
style:
|
||||||
|
const TextStyle(color: AppColors.textMain),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: '30',
|
hintText: '30',
|
||||||
hintStyle: const TextStyle(color: AppColors.textSecondary),
|
hintStyle: const TextStyle(
|
||||||
|
color: AppColors.textSecondary),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: AppColors.background,
|
fillColor: AppColors.background,
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
borderSide: BorderSide(color: AppColors.textSecondary.withValues(alpha: 0.3)),
|
borderSide: BorderSide(
|
||||||
|
color: AppColors.textSecondary
|
||||||
|
.withValues(alpha: 0.3)),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
borderSide: const BorderSide(color: AppColors.primary),
|
borderSide: const BorderSide(
|
||||||
|
color: AppColors.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
if (isRecording)
|
if (isRecording)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.success.withValues(alpha: 0.15),
|
color: AppColors.success
|
||||||
|
.withValues(alpha: 0.15),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
border: Border.all(color: AppColors.success),
|
border:
|
||||||
|
Border.all(color: AppColors.success),
|
||||||
),
|
),
|
||||||
child: const Row(
|
child: const Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.circle, color: AppColors.success, size: 10),
|
Icon(Icons.circle,
|
||||||
|
color: AppColors.success, size: 10),
|
||||||
SizedBox(width: 8),
|
SizedBox(width: 8),
|
||||||
Text('Perekaman berjalan...',
|
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),
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: isRecording ? null : () => _controlRecording(true),
|
onPressed: isRecording
|
||||||
icon: const Icon(Icons.play_arrow, color: Colors.white),
|
? null
|
||||||
label: const Text('Mulai', style: TextStyle(color: Colors.white)),
|
: () => _controlRecording(true),
|
||||||
|
icon: const Icon(Icons.play_arrow,
|
||||||
|
color: Colors.white),
|
||||||
|
label: const Text('Mulai',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.white)),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.primary,
|
backgroundColor: AppColors.primary,
|
||||||
disabledBackgroundColor: AppColors.primary.withValues(alpha: 0.3),
|
disabledBackgroundColor: AppColors
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
.primary
|
||||||
|
.withValues(alpha: 0.3),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(12)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: !isRecording ? null : () => _controlRecording(false),
|
onPressed: !isRecording
|
||||||
icon: const Icon(Icons.stop, color: Colors.white),
|
? null
|
||||||
label: const Text('Berhenti', style: TextStyle(color: Colors.white)),
|
: () => _controlRecording(false),
|
||||||
|
icon: const Icon(Icons.stop,
|
||||||
|
color: Colors.white),
|
||||||
|
label: const Text('Berhenti',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.white)),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.error,
|
backgroundColor: AppColors.error,
|
||||||
disabledBackgroundColor: AppColors.error.withValues(alpha: 0.3),
|
disabledBackgroundColor: AppColors.error
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
.withValues(alpha: 0.3),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(12)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -218,16 +277,65 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// KODE BARU (Sudah Termasuk Tombol Checklist & Select All):
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text('Galeri Citra Dataset (${_gallery.length} foto)',
|
Expanded(
|
||||||
style: const TextStyle(color: AppColors.textMain, fontSize: 18, fontWeight: FontWeight.bold),
|
child: Text(
|
||||||
),
|
'Galeri Dataset (${_gallery.length} foto)',
|
||||||
IconButton(
|
style: const TextStyle(
|
||||||
icon: const Icon(Icons.refresh, color: AppColors.primary),
|
color: AppColors.textMain,
|
||||||
onPressed: _loadData,
|
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),
|
const SizedBox(height: 10),
|
||||||
|
|
@ -235,10 +343,13 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _gallery.isEmpty
|
child: _gallery.isEmpty
|
||||||
? const Center(
|
? 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(
|
: GridView.builder(
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
crossAxisSpacing: 10,
|
crossAxisSpacing: 10,
|
||||||
mainAxisSpacing: 10,
|
mainAxisSpacing: 10,
|
||||||
|
|
@ -246,98 +357,222 @@ class _RecordingScreenState extends State<RecordingScreen> {
|
||||||
itemCount: _gallery.length,
|
itemCount: _gallery.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = _gallery[index];
|
final item = _gallery[index];
|
||||||
final nomorFoto = index + 1;
|
final nomorFoto = _gallery.length - index;
|
||||||
|
|
||||||
// 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;
|
// PENYARING WAKTU: Ubah 'T' jadi Spasi agar kodingan lamamu aman
|
||||||
final jam = cleanTime.contains(' ') ? cleanTime.split(' ')[1] : '';
|
String rawTime = item['timestamp'] ??
|
||||||
final jamSingkat = jam.length >= 5 ? jam.substring(0, 5) : jam;
|
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(
|
return GestureDetector(
|
||||||
onTap: () => Navigator.pushNamed(
|
// --- FITUR BARU: Logika Tahan & Klik ---
|
||||||
context,
|
onLongPress: () {
|
||||||
'/image_result',
|
setState(() {
|
||||||
arguments: {
|
_isSelectionMode = true;
|
||||||
'id': item['id'].toString(),
|
_selectedIndices.add(index);
|
||||||
'date': tanggal,
|
});
|
||||||
'time': jamSingkat,
|
},
|
||||||
// PENYARING SENSOR 0: Jika 0, ubah jadi strip '-'
|
onTap: () {
|
||||||
'temp': (item['suhu'] == 0 || item['suhu'] == null) ? '-' : '${item['suhu']} °C',
|
if (_isSelectionMode) {
|
||||||
'humidity': (item['kelembapan'] == 0 || item['kelembapan'] == null) ? '-' : '${item['kelembapan']} %',
|
setState(() {
|
||||||
'light': (item['intensitas'] == 0 || item['intensitas'] == null) ? '-' : '${item['intensitas']} Lux',
|
if (_selectedIndices.contains(index)) {
|
||||||
'location': 'Gudang Pengering',
|
_selectedIndices.remove(index);
|
||||||
'intake': item['kipas1'] ?? 'OFF',
|
} else {
|
||||||
'exhaust': item['kipas2'] ?? 'OFF',
|
_selectedIndices.add(index);
|
||||||
'url_foto': item['url_foto'] ?? '',
|
}
|
||||||
},
|
});
|
||||||
),
|
} else {
|
||||||
child: Container(
|
// KODE LAMAMU TETAP AMAN DI SINI (Navigasi ke Detail)
|
||||||
decoration: BoxDecoration(
|
Navigator.pushNamed(
|
||||||
color: AppColors.cardBg,
|
context,
|
||||||
borderRadius: BorderRadius.circular(15),
|
'/image_result',
|
||||||
border: Border.all(color: AppColors.textSecondary.withValues(alpha: 0.15)),
|
arguments: {
|
||||||
),
|
'id': item['id'].toString(),
|
||||||
child: Column(
|
'date': tanggal,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
'time': jamSingkat,
|
||||||
children: [
|
'temp': (item['suhu'] == 0 ||
|
||||||
Expanded(
|
item['suhu'] == null)
|
||||||
flex: 3,
|
? '-'
|
||||||
child: ClipRRect(
|
: '${item['suhu']} °C',
|
||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(14)),
|
'humidity': (item['kelembapan'] ==
|
||||||
child: item['url_foto'] != null
|
0 ||
|
||||||
? Image.network(
|
item['kelembapan'] == null)
|
||||||
item['url_foto'],
|
? '-'
|
||||||
fit: BoxFit.cover,
|
: '${item['kelembapan']} %',
|
||||||
errorBuilder: (_, __, ___) => const Center(
|
'light': (item['intensitas'] == 0 ||
|
||||||
child: Icon(Icons.broken_image, color: AppColors.textSecondary, size: 40),
|
item['intensitas'] == null)
|
||||||
),
|
? '-'
|
||||||
loadingBuilder: (_, child, progress) {
|
: '${item['intensitas']} Lux',
|
||||||
if (progress == null) return child;
|
'location': 'Gudang Pengering',
|
||||||
return const Center(
|
'intake': item['kipas1'] ?? 'OFF',
|
||||||
child: CircularProgressIndicator(color: AppColors.primary, strokeWidth: 2),
|
'exhaust': item['kipas2'] ?? 'OFF',
|
||||||
);
|
'url_foto': item['url_foto'] ?? '',
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
: const Center(
|
}
|
||||||
child: Icon(Icons.image, color: AppColors.textSecondary, size: 40),
|
},
|
||||||
),
|
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,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
child: Column(
|
||||||
Container(
|
crossAxisAlignment:
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
CrossAxisAlignment.stretch,
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.transparent,
|
|
||||||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(15)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Expanded(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
flex: 3,
|
||||||
decoration: BoxDecoration(
|
child: ClipRRect(
|
||||||
color: AppColors.primary.withValues(alpha: 0.15),
|
borderRadius:
|
||||||
borderRadius: BorderRadius.circular(6),
|
const BorderRadius
|
||||||
border: Border.all(color: AppColors.primary.withValues(alpha: 0.5)),
|
.vertical(
|
||||||
),
|
top: Radius.circular(
|
||||||
child: Text('#$nomorFoto',
|
14)),
|
||||||
style: const TextStyle(color: AppColors.primary, fontSize: 10, fontWeight: FontWeight.bold),
|
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(
|
Container(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
padding:
|
||||||
children: [
|
const EdgeInsets.symmetric(
|
||||||
Text(tanggal, style: const TextStyle(color: AppColors.textSecondary, fontSize: 8)),
|
horizontal: 8,
|
||||||
Text(jamSingkat, style: const TextStyle(color: AppColors.textMain, fontSize: 10, fontWeight: FontWeight.bold)),
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import Foundation
|
||||||
import app_links
|
import app_links
|
||||||
import open_file_mac
|
import open_file_mac
|
||||||
import printing
|
import printing
|
||||||
|
import share_plus
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
|
|
@ -15,6 +16,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||||
OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin"))
|
OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin"))
|
||||||
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
||||||
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
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"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
146
pubspec.lock
146
pubspec.lock
|
|
@ -42,7 +42,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||||
|
|
@ -125,10 +125,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: code_assets
|
name: code_assets
|
||||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.2.1"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -149,10 +149,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
|
sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5+2"
|
version: "0.3.5+4"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -189,10 +189,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: equatable
|
name: equatable
|
||||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
sha256: "3bce007a596ff8b3119c45d68aaef631272537c03d30e5d4534dd24bf4c5eaa2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.1.0"
|
||||||
event_bus:
|
event_bus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -225,14 +225,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.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:
|
||||||
|
|
@ -300,18 +292,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: functions_client
|
name: functions_client
|
||||||
sha256: "94074d62167ae634127ef6095f536835063a7dc80f2b1aa306d2346ff9023996"
|
sha256: e9685e9ab852a8b8e0579c867f6c9155da27317b294b3df796a536b8b8e0d253
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0"
|
version: "2.6.4"
|
||||||
glob:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: glob
|
|
||||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.3"
|
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -324,10 +308,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: gotrue
|
name: gotrue
|
||||||
sha256: "7a4172601553e61716f5c3dd243aa3297e13308e07eb85b7853c941ba585dcf5"
|
sha256: "360bba9606e58d5acc59a033ab64ae3cf571a6868f7a7267cb8e9a204b7bf1b2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.20.0"
|
version: "2.26.0"
|
||||||
gtk:
|
gtk:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -340,10 +324,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: hooks
|
name: hooks
|
||||||
sha256: "025f060e86d2d4c3c47b56e33caf7f93bf9283340f26d23424ebcfccf34f621e"
|
sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "2.0.2"
|
||||||
html:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -380,10 +364,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.20.2"
|
version: "0.20.3"
|
||||||
jni:
|
jni:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -496,14 +480,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.11.11"
|
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:
|
||||||
|
|
@ -516,10 +492,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: objective_c
|
name: objective_c
|
||||||
sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52"
|
sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.3.0"
|
version: "9.4.1"
|
||||||
open_file:
|
open_file:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -532,18 +508,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: open_file_android
|
name: open_file_android
|
||||||
sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa"
|
sha256: f48a342b0347bf82bebf68c752f5cfce162965ce8449c5ecdafc406d294aa63d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
version: "1.1.0"
|
||||||
open_file_ios:
|
open_file_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: open_file_ios
|
name: open_file_ios
|
||||||
sha256: a5acd07ba1f304f807a97acbcc489457e1ad0aadff43c467987dd9eef814098f
|
sha256: "0e3e67dcd12f69888128f533cacc092039627dd4f1d7a95a2826b814ae4ea8aa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.1.0"
|
||||||
open_file_linux:
|
open_file_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -556,18 +532,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: open_file_mac
|
name: open_file_mac
|
||||||
sha256: cd293f6750de6438ab2390513c99128ade8c974825d4d8128886d1cda8c64d01
|
sha256: "1abe00e65f792b9baa1e15fdf4d44af5e813b1696ac307d71535543122a01e30"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.1.0"
|
||||||
open_file_platform_interface:
|
open_file_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: open_file_platform_interface
|
name: open_file_platform_interface
|
||||||
sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757"
|
sha256: dc302e4bd5c1d77acec4240bd0dbc1d9f2ee6882b1ce52bca5298369da7acf12
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.1.0"
|
||||||
open_file_web:
|
open_file_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -592,6 +568,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
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:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -612,10 +596,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.1.6"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -636,18 +620,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.2"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -708,10 +692,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: postgrest
|
name: postgrest
|
||||||
sha256: "9d61b3d4a88fcf9424d400127c54d49ed1b56ec30838fc0a33a64f31d4e694cc"
|
sha256: "10e3f195e131eec944fa872644aed89b33b5be998f3fc77c87325db3927bc646"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "2.8.0"
|
||||||
printing:
|
printing:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -748,10 +732,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: realtime_client
|
name: realtime_client
|
||||||
sha256: "7dfccf372d2f55aacfeefb6186f65a06f3ffae383fe042dbeef9d85d33487576"
|
sha256: "0cfbe0047e2591eba348938e9b4e620d5b1a8df6c374757e8eb8645252b8387f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.3"
|
version: "2.11.0"
|
||||||
record_use:
|
record_use:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -776,8 +760,16 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.28.0"
|
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:
|
share_plus_platform_interface:
|
||||||
dependency: "direct overridden"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496"
|
sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496"
|
||||||
|
|
@ -865,10 +857,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: storage_client
|
name: storage_client
|
||||||
sha256: "4801e8ca219a35e51cbb30589aba5306667ae8935b792504595a45273cef0b18"
|
sha256: "221263cfbe0c01575b7d7fe7543e44abff380eb4d38d936372844a1caa85ba12"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.2"
|
version: "2.6.0"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -889,18 +881,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: supabase
|
name: supabase
|
||||||
sha256: "40e5a8833c8834e140ef53b60a6181849667eba9ca125acb7f8e24c6a769d418"
|
sha256: "3879996256325fcc9abb03b62b12c07e4eaae2e008e1bf043cc1525184159a43"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.6"
|
version: "2.14.0"
|
||||||
supabase_flutter:
|
supabase_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: supabase_flutter
|
name: supabase_flutter
|
||||||
sha256: c02ce58abcaf86cb8055ad40bfd98bbf5b93fed3b5b56b8220d88ed03842818b
|
sha256: c9916c1cd512ebf3107ec0f83c9dca13d2e1e789629c2a5df896586bff46ce5f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.4"
|
version: "2.16.0"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -945,10 +937,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
|
sha256: "17bc677f0b301615530dd1d67e0a9828cafa2d0b6b6eae4cd3679b7eac4a273c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.29"
|
version: "6.3.30"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -985,10 +977,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f
|
sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.4.3"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1046,13 +1038,13 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
win32:
|
win32:
|
||||||
dependency: "direct overridden"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: ba7d5750e3441caa1bbe31d9e516348fcf8dfcb32aa29ef87a844a59f4d1f1d0
|
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "5.15.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1081,10 +1073,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yet_another_json_isolate
|
name: yet_another_json_isolate
|
||||||
sha256: fe45897501fa156ccefbfb9359c9462ce5dec092f05e8a56109db30be864f01e
|
sha256: eaa26beb5990b25a49d942374fd5a0c5aa67a837e03b14b4c26134aaa1ed01a9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.10.3 <4.0.0"
|
dart: ">=3.11.0 <4.0.0"
|
||||||
flutter: ">=3.38.4"
|
flutter: ">=3.41.0"
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,8 @@ dependencies:
|
||||||
open_file: ^3.3.2
|
open_file: ^3.3.2
|
||||||
path_provider: ^2.1.2
|
path_provider: ^2.1.2
|
||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
|
archive: ^3.6.1 # Untuk membuat file ZIP
|
||||||
dependency_overrides:
|
share_plus: ^7.2.2 # Untuk memunculkan popup "Save to Device / Bagikan"
|
||||||
win32: ^6.1.0
|
|
||||||
share_plus_platform_interface: ^3.4.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <app_links/app_links_plugin_c_api.h>
|
#include <app_links/app_links_plugin_c_api.h>
|
||||||
#include <printing/printing_plugin.h>
|
#include <printing/printing_plugin.h>
|
||||||
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
|
@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
||||||
PrintingPluginRegisterWithRegistrar(
|
PrintingPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("PrintingPlugin"));
|
registry->GetRegistrarForPlugin("PrintingPlugin"));
|
||||||
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
app_links
|
app_links
|
||||||
printing
|
printing
|
||||||
|
share_plus
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue