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/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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
|||
146
pubspec.lock
146
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"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
app_links
|
||||
printing
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue