oke
This commit is contained in:
parent
6cf9d0fb70
commit
aa530a5d09
|
|
@ -1,32 +1,44 @@
|
||||||
version.json,1770352057364,f7f931727903f67c4b2ec2c38d4c5c4e2ec1be0c03071b8bff0c27f5be3b9e6e
|
|
||||||
manifest.json,1768956954743,5edb32bebce763b2b7e55774ae4c5c4354a44df248387cc3c174e8710e85aaa1
|
manifest.json,1768956954743,5edb32bebce763b2b7e55774ae4c5c4354a44df248387cc3c174e8710e85aaa1
|
||||||
index.html,1770352031176,1fbdf325dceff31189cd7bcddde37e4eef1758ca2babb5cb593af432bd607799
|
|
||||||
flutter_bootstrap.js,1770352031163,ed17fea025df6287ef713e3df7b02d14f66b937811e9ae377701382e28cfd589
|
|
||||||
flutter.js,1767911992319,970356511bee637e57348b9e01b3218a3d1d6c4add478fdc306033b4b3e21ef4
|
flutter.js,1767911992319,970356511bee637e57348b9e01b3218a3d1d6c4add478fdc306033b4b3e21ef4
|
||||||
favicon.png,1768956954741,19880e004e8357a51f6a64f5d1fdc8828a6432fe9c781f8acb6b730eced2cd13
|
favicon.png,1768956954741,19880e004e8357a51f6a64f5d1fdc8828a6432fe9c781f8acb6b730eced2cd13
|
||||||
flutter_service_worker.js,1770352060714,32aece2e5d9468eccf8a5a82c52aeabcea5af4ddffb3a2401fb1bf1f302ce9e6
|
|
||||||
icons/Icon-512.png,1768956954742,1de493563deb283e6ad70f0d5a89cfd61e37343df3b919c9862c5a41699ac5bc
|
|
||||||
icons/Icon-maskable-192.png,1768956954742,f7ac56197638aec92f195c5344d15395c2cbf8a5ae74e846c5b4156b64f05e38
|
|
||||||
icons/Icon-192.png,1768956954741,5fd961c51d8eb53baacac01f8e53743a76f66a43390a11f5df042e8c618ad64a
|
|
||||||
assets/FontManifest.json,1770352057804,e38b95988f5d060cf9b7ce97cb5ac9236d6f4cc04a11d69567df97b2b4cbc5e5
|
|
||||||
assets/shaders/stretch_effect.frag,1770352057964,586ff23669838dd33070077916f0b971a8800f67093ae8d6eed47e6cea50926f
|
|
||||||
assets/AssetManifest.bin.json,1770352057804,6763aaa55146ec9534c671aaf5a6db93265eaaa8082767ea02eb2ac9b09cbdaa
|
|
||||||
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1770352059003,d41473de1f7708a0702d7f19327693486512db442f6ab0cf7774e6d6576f9fcb
|
|
||||||
assets/AssetManifest.bin,1770352057804,93e425eb6f3c87a588cd708f748fc5bb80fa28bf34c6d814b2bd76daa6a6ed27
|
|
||||||
assets/fonts/MaterialIcons-Regular.otf,1770352059003,400581a54bcbf9b63f6d7afd915d4713ce89ccf00ed00ac0edf5268f0de803a1
|
|
||||||
assets/shaders/ink_sparkle.frag,1770352057964,591c7517d5cb43eb91ea451e0d3f9f585cbf8298cf6c46a9144b77cb0775a406
|
|
||||||
icons/Icon-maskable-512.png,1768956954742,ea58e5c49bc4dcdc5db0a9fe2f25b2a3152f85ef9094f0d020de0aa318451a1d
|
icons/Icon-maskable-512.png,1768956954742,ea58e5c49bc4dcdc5db0a9fe2f25b2a3152f85ef9094f0d020de0aa318451a1d
|
||||||
canvaskit/skwasm_heavy.js,1767911992288,2e3aab5e61c0276a6704938362b4b3a4dc32bbb6d17d601e975e7ed03897b224
|
icons/Icon-maskable-192.png,1768956954742,f7ac56197638aec92f195c5344d15395c2cbf8a5ae74e846c5b4156b64f05e38
|
||||||
canvaskit/skwasm.js,1767911992256,6f325d22924e678a9417a777104985babcee8f99080bf90855f26bfa9829a5ea
|
icons/Icon-512.png,1768956954742,1de493563deb283e6ad70f0d5a89cfd61e37343df3b919c9862c5a41699ac5bc
|
||||||
canvaskit/canvaskit.js,1767911992193,777116800268972381a4832500bb0aafd96ab2546075bd4d96a9025feb5c7555
|
icons/Icon-192.png,1768956954741,5fd961c51d8eb53baacac01f8e53743a76f66a43390a11f5df042e8c618ad64a
|
||||||
canvaskit/chromium/canvaskit.js,1767911992241,e0f423bfd63202fb53a4dd5cffe330143bfb9eabf5e4e8198bb605442a49d3e7
|
|
||||||
assets/NOTICES,1770352057804,b877416ca37cc2a29c1969535c12cb9f74e9d950b21c916d75918e99ec0da3ed
|
|
||||||
canvaskit/chromium/canvaskit.js.symbols,1767911992241,4a8d181b0622d7a4b4254deb591c43f48dd89623c621dec6f84d0a0b2b8f77a7
|
|
||||||
canvaskit/canvaskit.js.symbols,1767911992193,b1e22d35cbc9e3b4b89516a8462c9f1f73d86e6debc790833396dff78ad698a7
|
|
||||||
canvaskit/skwasm.js.symbols,1767911992272,5d3dd3374664b262880564a3e59d64b150f4c632248fb93f913b4eebdeffca83
|
|
||||||
canvaskit/skwasm_heavy.js.symbols,1767911992288,1259742821cf62bece358301f311840038fc2a0352b0c5827f8d2137d6197d08
|
|
||||||
main.dart.js,1770352050513,8c2d615d9f867aa7edc73fc0f4d4b63eaeef9f3f3cd26b583e7938bbcc1b2e72
|
|
||||||
canvaskit/skwasm.wasm,1767911992288,4776f3bfb61a7534e2260954b88b177c11a7c73644cd536c47fcaabe5d7dc79b
|
|
||||||
canvaskit/chromium/canvaskit.wasm,1767911992256,06f70f0703803c7329be30338b79530564ddc1ab2c3789d608431697a9a30ba2
|
|
||||||
canvaskit/skwasm_heavy.wasm,1767911992319,aa021afcfd815b925d5bad0debf468bef50192c458573e4570d6da4e12c2e186
|
canvaskit/skwasm_heavy.wasm,1767911992319,aa021afcfd815b925d5bad0debf468bef50192c458573e4570d6da4e12c2e186
|
||||||
|
canvaskit/skwasm_heavy.js.symbols,1767911992288,1259742821cf62bece358301f311840038fc2a0352b0c5827f8d2137d6197d08
|
||||||
|
canvaskit/skwasm_heavy.js,1767911992288,2e3aab5e61c0276a6704938362b4b3a4dc32bbb6d17d601e975e7ed03897b224
|
||||||
|
canvaskit/skwasm.wasm,1767911992288,4776f3bfb61a7534e2260954b88b177c11a7c73644cd536c47fcaabe5d7dc79b
|
||||||
|
canvaskit/skwasm.js.symbols,1767911992272,5d3dd3374664b262880564a3e59d64b150f4c632248fb93f913b4eebdeffca83
|
||||||
|
canvaskit/skwasm.js,1767911992256,6f325d22924e678a9417a777104985babcee8f99080bf90855f26bfa9829a5ea
|
||||||
canvaskit/canvaskit.wasm,1767911992241,ac942f1920618b74351bc74bf1cf4ca078bde56e11692c0bd611e906bfc2d279
|
canvaskit/canvaskit.wasm,1767911992241,ac942f1920618b74351bc74bf1cf4ca078bde56e11692c0bd611e906bfc2d279
|
||||||
|
canvaskit/canvaskit.js.symbols,1767911992193,b1e22d35cbc9e3b4b89516a8462c9f1f73d86e6debc790833396dff78ad698a7
|
||||||
|
canvaskit/canvaskit.js,1767911992193,777116800268972381a4832500bb0aafd96ab2546075bd4d96a9025feb5c7555
|
||||||
|
canvaskit/chromium/canvaskit.wasm,1767911992256,06f70f0703803c7329be30338b79530564ddc1ab2c3789d608431697a9a30ba2
|
||||||
|
canvaskit/chromium/canvaskit.js.symbols,1767911992241,4a8d181b0622d7a4b4254deb591c43f48dd89623c621dec6f84d0a0b2b8f77a7
|
||||||
|
canvaskit/chromium/canvaskit.js,1767911992241,e0f423bfd63202fb53a4dd5cffe330143bfb9eabf5e4e8198bb605442a49d3e7
|
||||||
|
version.json,1779493407408,f7f931727903f67c4b2ec2c38d4c5c4e2ec1be0c03071b8bff0c27f5be3b9e6e
|
||||||
|
index.html,1779493279226,3ffbcfc0c9d82b851ce84d2b1bfc85cea0a34b077bdad955e7cf52643a3cf434
|
||||||
|
flutter_bootstrap.js,1779493279204,ac46b098b9b222a709fa5ec23d1808db44a01d9b73d454c9a583d07a29cea348
|
||||||
|
flutter_service_worker.js,1779493415284,df198e5b7cc03c86b760a3ec2fe6f78f064d02cf545c4fb4b4a6a4ae142c3020
|
||||||
|
splash/img/light-1x.png,1779442242116,c4d52b9b4b1cfdd74194c0192c462e0f14934e1428ab7ff99165f08dc17f9010
|
||||||
|
splash/img/dark-1x.png,1779442242114,c4d52b9b4b1cfdd74194c0192c462e0f14934e1428ab7ff99165f08dc17f9010
|
||||||
|
assets/AssetManifest.bin.json,1779493407814,e23e10111498cc3b8b40e6549dcde13cffa6f45dc78e88194f4a7e284bb68294
|
||||||
|
assets/FontManifest.json,1779493407814,e38b95988f5d060cf9b7ce97cb5ac9236d6f4cc04a11d69567df97b2b4cbc5e5
|
||||||
|
assets/AssetManifest.bin,1779493407814,fe7319a65158e186661b7edfcc1c2b6c0a6bc6823b923a13e1e932cd66407fa2
|
||||||
|
assets/shaders/stretch_effect.frag,1779493407940,586ff23669838dd33070077916f0b971a8800f67093ae8d6eed47e6cea50926f
|
||||||
|
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1779493409456,85140ea453cd3e02bd93650fe8add6a724ae8c0ac73d0c491085b301e7bd21d2
|
||||||
|
assets/shaders/ink_sparkle.frag,1779493407944,591c7517d5cb43eb91ea451e0d3f9f585cbf8298cf6c46a9144b77cb0775a406
|
||||||
|
assets/images/windy.png,1776157890991,b9d34ed004fff60a4fea365c23508823943f3f0793375f34ca9202069d9c9955
|
||||||
|
assets/images/atmosfer.png,1776157890990,c3a9ddfa5676b5f0e1cf833f73c0f09c1dfb9d2e202906c1afcdd7a50dcf2506
|
||||||
|
assets/fonts/MaterialIcons-Regular.otf,1779493409477,1c7547588fe9c202b730590aab269e1bae9988240caeba41da28091251a87dbe
|
||||||
|
assets/images/google-icon-logo.png,1776157890990,333cfafcd5c87738a4098359e18e02ff5848b2b1b819ec8aff29424c3f82d32b
|
||||||
|
splash/img/light-2x.png,1779442242116,e8cc8f611a58a4926632edfcd3cb7d95925b6895791632ea1c1739c4f454c8fa
|
||||||
|
splash/img/dark-2x.png,1779442242115,e8cc8f611a58a4926632edfcd3cb7d95925b6895791632ea1c1739c4f454c8fa
|
||||||
|
splash/img/light-3x.png,1779442242118,a1a4d566718037d913ad3287e9b7d1e6d0df8569c285ce3d3680db82b6a96812
|
||||||
|
splash/img/dark-3x.png,1779442242115,a1a4d566718037d913ad3287e9b7d1e6d0df8569c285ce3d3680db82b6a96812
|
||||||
|
splash/img/light-4x.png,1779442242118,09ff8858378bb70d5f4b0381eb98dc9a889e541aeb2dd7576da7d62f205de578
|
||||||
|
assets/images/logo_klimatologi.png,1777103040742,e042ff556e356926fcd5ff92555519a569a71316ab2f7074f86f4ea4a215f325
|
||||||
|
splash/img/dark-4x.png,1779442242116,09ff8858378bb70d5f4b0381eb98dc9a889e541aeb2dd7576da7d62f205de578
|
||||||
|
assets/NOTICES,1779493407816,62963000bb4f8b466d5b97db77a0f9563f047723e134974be6bf170da0f821b2
|
||||||
|
main.dart.js,1779493365556,91f95baa75b37e85885a3fc9b0fc953ed2245277abde8ca4682b0707131a2056
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import '../../shared/widgets/export_pdf_button.dart';
|
||||||
import 'widgets/evaporasi_chart_widget.dart';
|
import 'widgets/evaporasi_chart_widget.dart';
|
||||||
import 'widgets/evaporasi_range_selector.dart';
|
import 'widgets/evaporasi_range_selector.dart';
|
||||||
import 'widgets/evaporasi_history_list.dart';
|
import 'widgets/evaporasi_history_list.dart';
|
||||||
|
// TAMBAH baris ini setelah import yang sudah ada:
|
||||||
|
import '../../shared/utils/excel/evaporasi_excel_service.dart';
|
||||||
|
|
||||||
class EvaporasiScreen extends StatefulWidget {
|
class EvaporasiScreen extends StatefulWidget {
|
||||||
const EvaporasiScreen({super.key});
|
const EvaporasiScreen({super.key});
|
||||||
|
|
@ -19,6 +21,280 @@ class EvaporasiScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EvaporasiScreenState extends State<EvaporasiScreen> {
|
class _EvaporasiScreenState extends State<EvaporasiScreen> {
|
||||||
|
// ── Dialog export: nama file + date range ───────────────────
|
||||||
|
Future<void> _showExportDialog(
|
||||||
|
BuildContext context, EvaporasiState state) async {
|
||||||
|
DateTime firstDate = DateTime.now().subtract(const Duration(days: 365));
|
||||||
|
DateTime lastDate = DateTime.now();
|
||||||
|
if (state.history.isNotEmpty) {
|
||||||
|
final sorted = [...state.history]
|
||||||
|
..sort((a, b) => a.timestamp.compareTo(b.timestamp));
|
||||||
|
firstDate = DateTime(sorted.first.timestamp.year,
|
||||||
|
sorted.first.timestamp.month, sorted.first.timestamp.day);
|
||||||
|
lastDate = DateTime(sorted.last.timestamp.year,
|
||||||
|
sorted.last.timestamp.month, sorted.last.timestamp.day);
|
||||||
|
}
|
||||||
|
|
||||||
|
final nameController = TextEditingController(
|
||||||
|
text: 'evaporasi_${DateFormat('ddMMyyyy').format(DateTime.now())}',
|
||||||
|
);
|
||||||
|
DateTime? dateFrom;
|
||||||
|
DateTime? dateTo;
|
||||||
|
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => StatefulBuilder(
|
||||||
|
builder: (ctx, setDialogState) {
|
||||||
|
final fmt = DateFormat('dd MMM yyyy', 'id_ID');
|
||||||
|
|
||||||
|
Future<void> pickRange() async {
|
||||||
|
final range = await showDateRangePicker(
|
||||||
|
context: ctx,
|
||||||
|
firstDate: firstDate,
|
||||||
|
lastDate: lastDate,
|
||||||
|
initialDateRange: dateFrom != null && dateTo != null
|
||||||
|
? DateTimeRange(start: dateFrom!, end: dateTo!)
|
||||||
|
: null,
|
||||||
|
locale: const Locale('id', 'ID'),
|
||||||
|
builder: (ctx, child) => Theme(
|
||||||
|
data: Theme.of(ctx).copyWith(
|
||||||
|
colorScheme: ColorScheme.light(
|
||||||
|
primary: Colors.blue.shade700,
|
||||||
|
onPrimary: Colors.white,
|
||||||
|
surface: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: child!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (range != null) {
|
||||||
|
setDialogState(() {
|
||||||
|
dateFrom = range.start;
|
||||||
|
dateTo = range.end;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
shape:
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.file_download_outlined, color: Colors.blue.shade700),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
const Text('Export Excel',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 17, fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: SizedBox(
|
||||||
|
width: 340,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// ── Nama file ──────────────────────────────
|
||||||
|
const Text('Nama file',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.black54)),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
TextField(
|
||||||
|
controller: nameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'nama_file',
|
||||||
|
suffixText: '.xlsx',
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.grey.shade100,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 14, vertical: 10),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide:
|
||||||
|
BorderSide(color: Colors.blue.shade600, width: 1.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// ── Range tanggal ──────────────────────────
|
||||||
|
const Text('Filter rentang tanggal',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.black54)),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
InkWell(
|
||||||
|
onTap: pickRange,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 14, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.date_range_rounded,
|
||||||
|
size: 18, color: Colors.blue.shade600),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
dateFrom != null && dateTo != null
|
||||||
|
? '${fmt.format(dateFrom!)} → ${fmt.format(dateTo!)}'
|
||||||
|
: 'Semua data (tanpa filter)',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: dateFrom != null
|
||||||
|
? Colors.black87
|
||||||
|
: Colors.grey.shade500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (dateFrom != null)
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => setDialogState(() {
|
||||||
|
dateFrom = null;
|
||||||
|
dateTo = null;
|
||||||
|
}),
|
||||||
|
child: Icon(Icons.close_rounded,
|
||||||
|
size: 16, color: Colors.grey.shade500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (dateFrom != null) ...[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
'${state.history.where((e) {
|
||||||
|
final d = DateTime(e.timestamp.year, e.timestamp.month,
|
||||||
|
e.timestamp.day);
|
||||||
|
final from = DateTime(
|
||||||
|
dateFrom!.year, dateFrom!.month, dateFrom!.day);
|
||||||
|
final to =
|
||||||
|
DateTime(dateTo!.year, dateTo!.month, dateTo!.day);
|
||||||
|
return !d.isBefore(from) && !d.isAfter(to);
|
||||||
|
}).length} data dalam rentang ini',
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 11, color: Colors.blue.shade600),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(ctx),
|
||||||
|
child: Text('Batal',
|
||||||
|
style: TextStyle(color: Colors.grey.shade600)),
|
||||||
|
),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(ctx);
|
||||||
|
_doExport(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
nameController.text.trim(),
|
||||||
|
dateFrom,
|
||||||
|
dateTo,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.download_rounded, size: 16),
|
||||||
|
label: const Text('Export'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.blue.shade700,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
nameController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Proses export setelah dialog ditutup ────────────────────
|
||||||
|
Future<void> _doExport(
|
||||||
|
BuildContext context,
|
||||||
|
EvaporasiState state,
|
||||||
|
String customName,
|
||||||
|
DateTime? dateFrom,
|
||||||
|
DateTime? dateTo,
|
||||||
|
) async {
|
||||||
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
|
final fileName = customName.isEmpty
|
||||||
|
? 'evaporasi_${DateFormat('ddMMyyyy').format(DateTime.now())}'
|
||||||
|
: customName;
|
||||||
|
|
||||||
|
try {
|
||||||
|
messenger.showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Row(children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2, color: Colors.white)),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Text('Membuat file Excel...'),
|
||||||
|
]),
|
||||||
|
duration: Duration(seconds: 30),
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await EvaporasiExcelService.export(
|
||||||
|
currentValue: state.currentValue,
|
||||||
|
temperature: state.temperature,
|
||||||
|
waterLevel: state.waterLevel,
|
||||||
|
acuanPagi: state.currentData?.acuanPagi ?? 0.0,
|
||||||
|
weatherStatus: state.weatherStatus,
|
||||||
|
history: state.history,
|
||||||
|
fileName: fileName,
|
||||||
|
dateFrom: dateFrom,
|
||||||
|
dateTo: dateTo,
|
||||||
|
);
|
||||||
|
|
||||||
|
messenger.hideCurrentSnackBar();
|
||||||
|
messenger.showSnackBar(SnackBar(
|
||||||
|
content: Row(children: [
|
||||||
|
const Icon(Icons.check_circle, color: Colors.white),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(child: Text('$fileName.xlsx berhasil dibuat!')),
|
||||||
|
]),
|
||||||
|
backgroundColor: Colors.green.shade600,
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
));
|
||||||
|
} catch (e) {
|
||||||
|
messenger.hideCurrentSnackBar();
|
||||||
|
messenger.showSnackBar(SnackBar(
|
||||||
|
content: Text('Gagal: $e'),
|
||||||
|
backgroundColor: Colors.red.shade600,
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -32,6 +308,17 @@ class _EvaporasiScreenState extends State<EvaporasiScreen> {
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
foregroundColor: Colors.black,
|
foregroundColor: Colors.black,
|
||||||
|
actions: [
|
||||||
|
BlocBuilder<EvaporasiBloc, EvaporasiState>(
|
||||||
|
builder: (context, state) => IconButton(
|
||||||
|
tooltip: 'Export Excel',
|
||||||
|
icon: const Icon(Icons.file_download_outlined),
|
||||||
|
onPressed: state.history.isEmpty
|
||||||
|
? null
|
||||||
|
: () => _showExportDialog(context, state),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: BlocBuilder<EvaporasiBloc, EvaporasiState>(
|
body: BlocBuilder<EvaporasiBloc, EvaporasiState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
|
@ -68,8 +355,7 @@ class _EvaporasiScreenState extends State<EvaporasiScreen> {
|
||||||
// ── Range Selector ──────────────────────────
|
// ── Range Selector ──────────────────────────
|
||||||
const Text(
|
const Text(
|
||||||
'Tren Evaporasi & Suhu',
|
'Tren Evaporasi & Suhu',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
const EvaporasiRangeSelector(),
|
const EvaporasiRangeSelector(),
|
||||||
|
|
@ -249,8 +535,7 @@ class _EvaporasiScreenState extends State<EvaporasiScreen> {
|
||||||
case 'Normal':
|
case 'Normal':
|
||||||
statusColor = Colors.orange;
|
statusColor = Colors.orange;
|
||||||
statusIcon = Icons.warning_amber_rounded;
|
statusIcon = Icons.warning_amber_rounded;
|
||||||
warningText =
|
warningText = 'Sedang — evaporasi dalam batas normal, pantau kondisi.';
|
||||||
'Sedang — evaporasi dalam batas normal, pantau kondisi.';
|
|
||||||
break;
|
break;
|
||||||
case 'Tinggi':
|
case 'Tinggi':
|
||||||
statusColor = Colors.red;
|
statusColor = Colors.red;
|
||||||
|
|
@ -282,8 +567,7 @@ class _EvaporasiScreenState extends State<EvaporasiScreen> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text('Status Evaporasi',
|
const Text('Status Evaporasi',
|
||||||
style:
|
style: TextStyle(fontSize: 12, color: Colors.grey)),
|
||||||
TextStyle(fontSize: 12, color: Colors.grey)),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
state.weatherStatus,
|
state.weatherStatus,
|
||||||
|
|
@ -307,4 +591,4 @@ class _EvaporasiScreenState extends State<EvaporasiScreen> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,419 @@
|
||||||
|
// ===========================================================
|
||||||
|
// evaporasi_excel_service.dart
|
||||||
|
// Lokasi: lib/screens/monitoring/shared/utils/excel/
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:excel/excel.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:monitoring_repository/monitoring_repository.dart';
|
||||||
|
|
||||||
|
import 'excel_saver_stub.dart'
|
||||||
|
if (dart.library.html) 'excel_saver_web.dart'
|
||||||
|
if (dart.library.io) 'excel_saver_mobile.dart';
|
||||||
|
|
||||||
|
class EvaporasiExcelService {
|
||||||
|
// ── Format helper ──────────────────────────────────────────
|
||||||
|
static final _dateFmt = DateFormat('dd/MM/yyyy HH:mm:ss', 'id_ID');
|
||||||
|
static final _headerFmt = DateFormat('dd MMMM yyyy, HH:mm', 'id_ID');
|
||||||
|
|
||||||
|
// ── Warna tema ─────────────────────────────────────────────
|
||||||
|
static const _colorHeader = '1A4A8C'; // biru tua
|
||||||
|
static const _colorSubHead = '2E75B6'; // biru medium
|
||||||
|
static const _colorNormal = 'E8F4FD'; // biru sangat muda
|
||||||
|
static const _colorTinggi = 'F8D7DA'; // merah muda
|
||||||
|
static const _colorRendah = 'D1ECF1'; // biru muda
|
||||||
|
static const _colorWhite = 'FFFFFF';
|
||||||
|
static const _colorWaspada = 'FFF3CD'; // kuning muda
|
||||||
|
|
||||||
|
/// Export data evaporasi ke file Excel dan buka share sheet
|
||||||
|
static Future<void> export({
|
||||||
|
required double currentValue,
|
||||||
|
required double temperature,
|
||||||
|
required double waterLevel,
|
||||||
|
required double acuanPagi,
|
||||||
|
required String weatherStatus,
|
||||||
|
required List<Evaporasi> history,
|
||||||
|
required String fileName,
|
||||||
|
DateTime? dateFrom,
|
||||||
|
DateTime? dateTo,
|
||||||
|
}) async {
|
||||||
|
final excel = Excel.createExcel();
|
||||||
|
excel.delete('Sheet1');
|
||||||
|
|
||||||
|
_buildSummarySheet(
|
||||||
|
excel,
|
||||||
|
currentValue,
|
||||||
|
temperature,
|
||||||
|
waterLevel,
|
||||||
|
acuanPagi,
|
||||||
|
weatherStatus,
|
||||||
|
history,
|
||||||
|
dateFrom,
|
||||||
|
dateTo,
|
||||||
|
);
|
||||||
|
_buildHistorySheet(excel, history, dateFrom, dateTo);
|
||||||
|
|
||||||
|
// ── Simpan file ─────────────────────────────────────────
|
||||||
|
final bytes = excel.save();
|
||||||
|
if (bytes == null) throw Exception('Gagal membuat file Excel');
|
||||||
|
|
||||||
|
final safeName = fileName.replaceAll(RegExp(r'[\\/:*?"<>|]'), '_');
|
||||||
|
await saveAndShareExcel(Uint8List.fromList(bytes), '$safeName.xlsx');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════
|
||||||
|
// SHEET 1: Ringkasan
|
||||||
|
// ════════════════════════════════════════════════════════════
|
||||||
|
static void _buildSummarySheet(
|
||||||
|
Excel excel,
|
||||||
|
double currentValue,
|
||||||
|
double temperature,
|
||||||
|
double waterLevel,
|
||||||
|
double acuanPagi,
|
||||||
|
String weatherStatus,
|
||||||
|
List<Evaporasi> history,
|
||||||
|
DateTime? dateFrom,
|
||||||
|
DateTime? dateTo,
|
||||||
|
) {
|
||||||
|
final sheet = excel['Ringkasan'];
|
||||||
|
|
||||||
|
// -- Judul --
|
||||||
|
_setCell(sheet, 0, 0, 'DATA EVAPORASI – WEATHER STATION',
|
||||||
|
bold: true,
|
||||||
|
fontSize: 14,
|
||||||
|
bgColor: _colorHeader,
|
||||||
|
fontColor: _colorWhite);
|
||||||
|
sheet.merge(
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 0),
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- Metadata --
|
||||||
|
_setCell(sheet, 1, 0, 'Diekspor pada',
|
||||||
|
bold: true, bgColor: _colorSubHead, fontColor: _colorWhite);
|
||||||
|
_setCell(sheet, 1, 1, _headerFmt.format(DateTime.now()),
|
||||||
|
bgColor: _colorNormal);
|
||||||
|
|
||||||
|
if (dateFrom != null && dateTo != null) {
|
||||||
|
_setCell(sheet, 2, 0, 'Filter tanggal',
|
||||||
|
bold: true, bgColor: _colorSubHead, fontColor: _colorWhite);
|
||||||
|
_setCell(
|
||||||
|
sheet,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
'${DateFormat('dd MMMM yyyy', 'id_ID').format(dateFrom)} → ${DateFormat('dd MMMM yyyy', 'id_ID').format(dateTo)}',
|
||||||
|
bgColor: _colorNormal,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Nilai saat ini --
|
||||||
|
_setCell(sheet, 4, 0, 'DATA SAAT INI',
|
||||||
|
bold: true, bgColor: _colorSubHead, fontColor: _colorWhite);
|
||||||
|
sheet.merge(
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 4),
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: 4),
|
||||||
|
);
|
||||||
|
|
||||||
|
_setCell(sheet, 5, 0, 'Evaporasi (mm)', bold: true);
|
||||||
|
_setCellDouble(sheet, 5, 1, currentValue);
|
||||||
|
|
||||||
|
_setCell(sheet, 6, 0, 'Suhu Air (°C)', bold: true);
|
||||||
|
if (temperature < 0) {
|
||||||
|
_setCell(sheet, 6, 1, '-');
|
||||||
|
} else {
|
||||||
|
_setCellDouble(sheet, 6, 1, temperature);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setCell(sheet, 7, 0, 'Tinggi Air (cm)', bold: true);
|
||||||
|
_setCellDouble(sheet, 7, 1, waterLevel);
|
||||||
|
|
||||||
|
_setCell(sheet, 8, 0, 'Acuan Air Pagi (cm)', bold: true);
|
||||||
|
_setCellDouble(sheet, 8, 1, acuanPagi);
|
||||||
|
|
||||||
|
_setCell(sheet, 9, 0, 'Status', bold: true);
|
||||||
|
_setCell(sheet, 9, 1, weatherStatus,
|
||||||
|
bgColor: _statusBgColor(weatherStatus));
|
||||||
|
|
||||||
|
// -- Statistik ringkasan --
|
||||||
|
if (history.isNotEmpty) {
|
||||||
|
final evapValues = history.map((e) => e.evaporasi).toList();
|
||||||
|
final tempValues = history
|
||||||
|
.map((e) => e.suhu)
|
||||||
|
.where((s) => s >= 0)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final evapAvg = evapValues.reduce((a, b) => a + b) / evapValues.length;
|
||||||
|
final evapMax = evapValues.reduce((a, b) => a > b ? a : b);
|
||||||
|
final evapMin = evapValues.reduce((a, b) => a < b ? a : b);
|
||||||
|
|
||||||
|
_setCell(sheet, 11, 0, 'STATISTIK HISTORY',
|
||||||
|
bold: true, bgColor: _colorSubHead, fontColor: _colorWhite);
|
||||||
|
sheet.merge(
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 11),
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 3, rowIndex: 11),
|
||||||
|
);
|
||||||
|
|
||||||
|
_setCell(sheet, 12, 0, 'Jumlah data', bold: true);
|
||||||
|
_setCellInt(sheet, 12, 1, history.length);
|
||||||
|
|
||||||
|
_setCell(sheet, 13, 0, 'Rata-rata evaporasi (mm)', bold: true);
|
||||||
|
_setCellDouble(sheet, 13, 1, evapAvg);
|
||||||
|
|
||||||
|
_setCell(sheet, 14, 0, 'Evaporasi maksimum (mm)', bold: true);
|
||||||
|
_setCellDouble(sheet, 14, 1, evapMax);
|
||||||
|
|
||||||
|
_setCell(sheet, 15, 0, 'Evaporasi minimum (mm)', bold: true);
|
||||||
|
_setCellDouble(sheet, 15, 1, evapMin);
|
||||||
|
|
||||||
|
if (tempValues.isNotEmpty) {
|
||||||
|
final tempAvg =
|
||||||
|
tempValues.reduce((a, b) => a + b) / tempValues.length;
|
||||||
|
final tempMax = tempValues.reduce((a, b) => a > b ? a : b);
|
||||||
|
final tempMin = tempValues.reduce((a, b) => a < b ? a : b);
|
||||||
|
|
||||||
|
_setCell(sheet, 16, 0, 'Rata-rata suhu (°C)', bold: true);
|
||||||
|
_setCellDouble(sheet, 16, 1, tempAvg);
|
||||||
|
|
||||||
|
_setCell(sheet, 17, 0, 'Suhu maksimum (°C)', bold: true);
|
||||||
|
_setCellDouble(sheet, 17, 1, tempMax);
|
||||||
|
|
||||||
|
_setCell(sheet, 18, 0, 'Suhu minimum (°C)', bold: true);
|
||||||
|
_setCellDouble(sheet, 18, 1, tempMin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set lebar kolom
|
||||||
|
sheet.setColumnWidth(0, 26);
|
||||||
|
sheet.setColumnWidth(1, 22);
|
||||||
|
sheet.setColumnWidth(2, 18);
|
||||||
|
sheet.setColumnWidth(3, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════
|
||||||
|
// SHEET 2: Data History
|
||||||
|
// ════════════════════════════════════════════════════════════
|
||||||
|
static void _buildHistorySheet(
|
||||||
|
Excel excel,
|
||||||
|
List<Evaporasi> history,
|
||||||
|
DateTime? dateFrom,
|
||||||
|
DateTime? dateTo,
|
||||||
|
) {
|
||||||
|
final sheet = excel['Data History'];
|
||||||
|
|
||||||
|
// -- Header kolom --
|
||||||
|
final headers = [
|
||||||
|
'No',
|
||||||
|
'Tanggal',
|
||||||
|
'Waktu',
|
||||||
|
'Evaporasi (mm)',
|
||||||
|
'Tinggi Air (cm)',
|
||||||
|
'Suhu (°C)',
|
||||||
|
'Acuan Pagi (cm)',
|
||||||
|
'Status',
|
||||||
|
];
|
||||||
|
for (var i = 0; i < headers.length; i++) {
|
||||||
|
_setCell(sheet, 0, i, headers[i],
|
||||||
|
bold: true,
|
||||||
|
bgColor: _colorHeader,
|
||||||
|
fontColor: _colorWhite,
|
||||||
|
centered: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter berdasarkan rentang tanggal (inklusif kedua ujung)
|
||||||
|
final data = (dateFrom != null && dateTo != null)
|
||||||
|
? history.where((e) {
|
||||||
|
final d = DateTime(
|
||||||
|
e.timestamp.year, e.timestamp.month, e.timestamp.day);
|
||||||
|
final from =
|
||||||
|
DateTime(dateFrom.year, dateFrom.month, dateFrom.day);
|
||||||
|
final to = DateTime(dateTo.year, dateTo.month, dateTo.day);
|
||||||
|
return !d.isBefore(from) && !d.isAfter(to);
|
||||||
|
}).toList()
|
||||||
|
: history;
|
||||||
|
|
||||||
|
// Urutkan terbaru di atas
|
||||||
|
final sorted = [...data]
|
||||||
|
..sort((a, b) => b.timestamp.compareTo(a.timestamp));
|
||||||
|
|
||||||
|
// -- Isi baris data --
|
||||||
|
for (var i = 0; i < sorted.length; i++) {
|
||||||
|
final item = sorted[i];
|
||||||
|
final rowIdx = i + 1;
|
||||||
|
final status = _getStatus(item.evaporasi);
|
||||||
|
final bgColor = i.isEven ? _colorNormal : _colorWhite;
|
||||||
|
final statusBg = _statusBgColor(status);
|
||||||
|
|
||||||
|
_setCellInt(sheet, rowIdx, 0, i + 1,
|
||||||
|
bgColor: bgColor, centered: true);
|
||||||
|
_setCell(
|
||||||
|
sheet,
|
||||||
|
rowIdx,
|
||||||
|
1,
|
||||||
|
DateFormat('dd/MM/yyyy', 'id_ID').format(item.timestamp),
|
||||||
|
bgColor: bgColor);
|
||||||
|
_setCell(
|
||||||
|
sheet,
|
||||||
|
rowIdx,
|
||||||
|
2,
|
||||||
|
DateFormat('HH:mm:ss').format(item.timestamp),
|
||||||
|
bgColor: bgColor,
|
||||||
|
centered: true);
|
||||||
|
_setCellDouble(sheet, rowIdx, 3, item.evaporasi,
|
||||||
|
bgColor: bgColor, centered: true);
|
||||||
|
_setCellDouble(sheet, rowIdx, 4, item.tinggiAir,
|
||||||
|
bgColor: bgColor, centered: true);
|
||||||
|
// Suhu: tampilkan '-' jika sensor error (< 0)
|
||||||
|
if (item.suhu < 0) {
|
||||||
|
_setCell(sheet, rowIdx, 5, '-', bgColor: bgColor, centered: true);
|
||||||
|
} else {
|
||||||
|
_setCellDouble(sheet, rowIdx, 5, item.suhu,
|
||||||
|
bgColor: bgColor, centered: true);
|
||||||
|
}
|
||||||
|
_setCellDouble(sheet, rowIdx, 6, item.acuanPagi,
|
||||||
|
bgColor: bgColor, centered: true);
|
||||||
|
_setCell(sheet, rowIdx, 7, status,
|
||||||
|
bgColor: statusBg, centered: true, bold: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Footer jika kosong --
|
||||||
|
if (sorted.isEmpty) {
|
||||||
|
_setCell(sheet, 1, 0, 'Tidak ada data untuk tanggal yang dipilih',
|
||||||
|
bgColor: _colorWaspada, centered: true);
|
||||||
|
sheet.merge(
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 0, rowIndex: 1),
|
||||||
|
CellIndex.indexByColumnRow(columnIndex: 7, rowIndex: 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set lebar kolom
|
||||||
|
sheet.setColumnWidth(0, 6);
|
||||||
|
sheet.setColumnWidth(1, 14);
|
||||||
|
sheet.setColumnWidth(2, 12);
|
||||||
|
sheet.setColumnWidth(3, 18);
|
||||||
|
sheet.setColumnWidth(4, 18);
|
||||||
|
sheet.setColumnWidth(5, 14);
|
||||||
|
sheet.setColumnWidth(6, 18);
|
||||||
|
sheet.setColumnWidth(7, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════
|
||||||
|
// HELPER CELLS
|
||||||
|
// ════════════════════════════════════════════════════════════
|
||||||
|
static void _setCell(
|
||||||
|
Sheet sheet,
|
||||||
|
int row,
|
||||||
|
int col,
|
||||||
|
String value, {
|
||||||
|
bool bold = false,
|
||||||
|
double fontSize = 11,
|
||||||
|
String bgColor = _colorWhite,
|
||||||
|
String fontColor = '000000',
|
||||||
|
bool centered = false,
|
||||||
|
}) {
|
||||||
|
final cell = sheet
|
||||||
|
.cell(CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row));
|
||||||
|
cell.value = TextCellValue(value);
|
||||||
|
cell.cellStyle = CellStyle(
|
||||||
|
bold: bold,
|
||||||
|
fontSize: fontSize.toInt(),
|
||||||
|
backgroundColorHex: ExcelColor.fromHexString('#$bgColor'),
|
||||||
|
fontColorHex: ExcelColor.fromHexString('#$fontColor'),
|
||||||
|
horizontalAlign:
|
||||||
|
centered ? HorizontalAlign.Center : HorizontalAlign.Left,
|
||||||
|
verticalAlign: VerticalAlign.Center,
|
||||||
|
textWrapping: TextWrapping.WrapText,
|
||||||
|
leftBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
rightBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
topBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
bottomBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _setCellDouble(
|
||||||
|
Sheet sheet,
|
||||||
|
int row,
|
||||||
|
int col,
|
||||||
|
double value, {
|
||||||
|
String bgColor = _colorWhite,
|
||||||
|
bool centered = false,
|
||||||
|
}) {
|
||||||
|
final cell = sheet
|
||||||
|
.cell(CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row));
|
||||||
|
cell.value = DoubleCellValue(double.parse(value.toStringAsFixed(4)));
|
||||||
|
cell.cellStyle = CellStyle(
|
||||||
|
backgroundColorHex: ExcelColor.fromHexString('#$bgColor'),
|
||||||
|
horizontalAlign:
|
||||||
|
centered ? HorizontalAlign.Center : HorizontalAlign.Right,
|
||||||
|
verticalAlign: VerticalAlign.Center,
|
||||||
|
leftBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
rightBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
topBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
bottomBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _setCellInt(
|
||||||
|
Sheet sheet,
|
||||||
|
int row,
|
||||||
|
int col,
|
||||||
|
int value, {
|
||||||
|
String bgColor = _colorWhite,
|
||||||
|
bool centered = false,
|
||||||
|
}) {
|
||||||
|
final cell = sheet
|
||||||
|
.cell(CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row));
|
||||||
|
cell.value = IntCellValue(value);
|
||||||
|
cell.cellStyle = CellStyle(
|
||||||
|
backgroundColorHex: ExcelColor.fromHexString('#$bgColor'),
|
||||||
|
horizontalAlign:
|
||||||
|
centered ? HorizontalAlign.Center : HorizontalAlign.Right,
|
||||||
|
verticalAlign: VerticalAlign.Center,
|
||||||
|
leftBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
rightBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
topBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
bottomBorder: Border(
|
||||||
|
borderStyle: BorderStyle.Thin,
|
||||||
|
borderColorHex: ExcelColor.fromHexString('#CCCCCC')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _statusBgColor(String status) {
|
||||||
|
switch (status) {
|
||||||
|
case 'Tinggi':
|
||||||
|
return _colorTinggi;
|
||||||
|
case 'Normal':
|
||||||
|
return _colorWaspada;
|
||||||
|
default: // Rendah
|
||||||
|
return _colorRendah;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _getStatus(double evaporasi) {
|
||||||
|
if (evaporasi > 10.0) return 'Tinggi';
|
||||||
|
if (evaporasi >= 2.0) return 'Normal';
|
||||||
|
return 'Rendah';
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue