chore: normalize line endings (CRLF -> LF)
No content changes: git diff --ignore-all-space over these files is empty. The churn came from editing on Windows against a repo checked out with LF.
This commit is contained in:
1 parent
15566a6951
commit
caf8e98378
315 files changed
+86950
-86950
No files matched your search
@@ -1,324 +1,324 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import '../../core/location/location_service.dart';
|
||||
import '../../core/utils/exif_helper.dart';
|
||||
import '../../config/app_config.dart';
|
||||
import 'image_preview_screen.dart';
|
||||
import 'scan_mode_provider.dart';
|
||||
import 'camera_drawer.dart';
|
||||
|
||||
class CameraScreen extends ConsumerStatefulWidget {
|
||||
const CameraScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<CameraScreen> createState() => CameraScreenState();
|
||||
}
|
||||
|
||||
class CameraScreenState extends ConsumerState<CameraScreen> {
|
||||
bool _isCapturing = false;
|
||||
Position? _currentPosition;
|
||||
StreamSubscription<Position>? _locationSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_preFetchLocation();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_locationSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _preFetchLocation() async {
|
||||
try {
|
||||
final pos = await LocationService.determinePosition();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_currentPosition = pos;
|
||||
});
|
||||
}
|
||||
|
||||
// Live location updates
|
||||
_locationSubscription = LocationService.getPositionStream().listen(
|
||||
(Position freshPos) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_currentPosition = freshPos;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> _takePicture() async {
|
||||
if (_isCapturing) return;
|
||||
|
||||
final ImagePicker picker = ImagePicker();
|
||||
try {
|
||||
final XFile? image = await picker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
maxWidth: 2048,
|
||||
maxHeight: 2048,
|
||||
imageQuality: 85,
|
||||
);
|
||||
|
||||
if (image != null) {
|
||||
setState(() {
|
||||
_isCapturing = true;
|
||||
});
|
||||
|
||||
// Use pre-fetched location if available, otherwise fetch quick position
|
||||
Position? position = _currentPosition;
|
||||
if (position == null) {
|
||||
try {
|
||||
position = await LocationService.determinePosition().timeout(const Duration(seconds: 2));
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isCapturing = false;
|
||||
});
|
||||
|
||||
navigateToPreview(image.path, latitude: position?.latitude, longitude: position?.longitude);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error taking picture: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickFromGallery() async {
|
||||
if (_isCapturing) return;
|
||||
|
||||
final ImagePicker picker = ImagePicker();
|
||||
try {
|
||||
final XFile? image = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
maxWidth: 2048,
|
||||
maxHeight: 2048,
|
||||
imageQuality: 85,
|
||||
);
|
||||
|
||||
if (image != null) {
|
||||
setState(() {
|
||||
_isCapturing = true;
|
||||
});
|
||||
|
||||
Position? position = _currentPosition;
|
||||
try {
|
||||
// Attempt to extract GPS from EXIF tags
|
||||
final exifGps = await ExifHelper.extractGps(image.path);
|
||||
if (exifGps != null) {
|
||||
position = Position(
|
||||
latitude: exifGps['latitude']!,
|
||||
longitude: exifGps['longitude']!,
|
||||
timestamp: DateTime.now(),
|
||||
accuracy: 0.0,
|
||||
altitude: 0.0,
|
||||
heading: 0.0,
|
||||
speed: 0.0,
|
||||
speedAccuracy: 0.0,
|
||||
altitudeAccuracy: 0.0,
|
||||
headingAccuracy: 0.0,
|
||||
);
|
||||
} else if (position == null) {
|
||||
position = await LocationService.determinePosition().timeout(const Duration(seconds: 2));
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
setState(() {
|
||||
_isCapturing = false;
|
||||
});
|
||||
|
||||
navigateToPreview(image.path, latitude: position?.latitude, longitude: position?.longitude);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error picking image from gallery: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void navigateToPreview(String imagePath, {double? latitude, double? longitude}) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ImagePreviewScreen(
|
||||
imagePath: imagePath,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final activeMode = ref.watch(scanModeProvider);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppConfig.backgroundColor,
|
||||
appBar: AppBar(
|
||||
title: Text(activeMode == 'DO' ? 'Pemindai Dokumen OCR' : 'Pemindai Produk PFM'),
|
||||
),
|
||||
drawer: const CameraDrawer(),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: AppConfig.screenPadding,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Upper Illustration Card
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadiusXL),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: AppConfig.paddingXL,
|
||||
child: SingleChildScrollView(
|
||||
child: activeMode == 'DO'
|
||||
? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppConfig.primaryColor.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.document_scanner,
|
||||
size: 48,
|
||||
color: AppConfig.primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
Text(
|
||||
'Pemindai DO',
|
||||
style: AppConfig.h3,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
Text(
|
||||
'Ambil foto lembar Delivery Order (DO) Prima Fresh Mart untuk mengekstrak data PO, SO, DO, dan item barang secara otomatis.',
|
||||
style: AppConfig.bodyMedium.copyWith(color: AppConfig.textSecondary),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
_buildGuidelineRow(Icons.lightbulb_outline, 'Pastikan pencahayaan cukup dan terang.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.crop_free, 'Posisikan seluruh halaman dokumen di dalam foto.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.camera_enhance_outlined, 'Pegang HP dengan stabil agar tidak buram.'),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppConfig.primaryColor.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.inventory_2_outlined,
|
||||
size: 48,
|
||||
color: AppConfig.primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
Text(
|
||||
'Pemindai Produk',
|
||||
style: AppConfig.h3,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
Text(
|
||||
'Ambil foto kemasan produk Prima Fresh Mart untuk mengidentifikasi SKU, nama item, dan mendeteksi tanggal kadaluwarsa secara otomatis.',
|
||||
style: AppConfig.bodyMedium.copyWith(color: AppConfig.textSecondary),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
_buildGuidelineRow(Icons.lightbulb_outline, 'Pastikan kode SKU atau teks kadaluwarsa terlihat jelas.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.center_focus_strong, 'Posisikan produk di tengah kamera.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.flash_on, 'Gunakan pencahayaan yang cukup agar tidak buram.'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
|
||||
// Action Buttons
|
||||
ElevatedButton.icon(
|
||||
onPressed: _isCapturing ? null : _takePicture,
|
||||
icon: const Icon(Icons.camera_alt),
|
||||
label: const Text('Ambil Foto Kamera'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18),
|
||||
textStyle: AppConfig.bodyLarge.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
OutlinedButton.icon(
|
||||
onPressed: _isCapturing ? null : _pickFromGallery,
|
||||
icon: const Icon(Icons.photo_library),
|
||||
label: const Text('Pilih Dari Galeri'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18),
|
||||
side: const BorderSide(color: AppConfig.primaryColor),
|
||||
foregroundColor: AppConfig.primaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadiusMedium),
|
||||
),
|
||||
textStyle: AppConfig.bodyLarge.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
TextButton.icon(
|
||||
onPressed: _isCapturing ? null : () => context.push('/documents'),
|
||||
icon: const Icon(Icons.history_edu),
|
||||
label: const Text('Riwayat Dokumen'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppConfig.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGuidelineRow(IconData icon, String text) {
|
||||
return Row(
|
||||
children: [
|
||||
Icon(icon, size: 18, color: AppConfig.secondaryColor),
|
||||
const SizedBox(width: AppConfig.spaceSmall),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: AppConfig.bodySmall.copyWith(color: AppConfig.textSecondary),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import '../../core/location/location_service.dart';
|
||||
import '../../core/utils/exif_helper.dart';
|
||||
import '../../config/app_config.dart';
|
||||
import 'image_preview_screen.dart';
|
||||
import 'scan_mode_provider.dart';
|
||||
import 'camera_drawer.dart';
|
||||
|
||||
class CameraScreen extends ConsumerStatefulWidget {
|
||||
const CameraScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<CameraScreen> createState() => CameraScreenState();
|
||||
}
|
||||
|
||||
class CameraScreenState extends ConsumerState<CameraScreen> {
|
||||
bool _isCapturing = false;
|
||||
Position? _currentPosition;
|
||||
StreamSubscription<Position>? _locationSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_preFetchLocation();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_locationSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _preFetchLocation() async {
|
||||
try {
|
||||
final pos = await LocationService.determinePosition();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_currentPosition = pos;
|
||||
});
|
||||
}
|
||||
|
||||
// Live location updates
|
||||
_locationSubscription = LocationService.getPositionStream().listen(
|
||||
(Position freshPos) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_currentPosition = freshPos;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> _takePicture() async {
|
||||
if (_isCapturing) return;
|
||||
|
||||
final ImagePicker picker = ImagePicker();
|
||||
try {
|
||||
final XFile? image = await picker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
maxWidth: 2048,
|
||||
maxHeight: 2048,
|
||||
imageQuality: 85,
|
||||
);
|
||||
|
||||
if (image != null) {
|
||||
setState(() {
|
||||
_isCapturing = true;
|
||||
});
|
||||
|
||||
// Use pre-fetched location if available, otherwise fetch quick position
|
||||
Position? position = _currentPosition;
|
||||
if (position == null) {
|
||||
try {
|
||||
position = await LocationService.determinePosition().timeout(const Duration(seconds: 2));
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isCapturing = false;
|
||||
});
|
||||
|
||||
navigateToPreview(image.path, latitude: position?.latitude, longitude: position?.longitude);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error taking picture: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickFromGallery() async {
|
||||
if (_isCapturing) return;
|
||||
|
||||
final ImagePicker picker = ImagePicker();
|
||||
try {
|
||||
final XFile? image = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
maxWidth: 2048,
|
||||
maxHeight: 2048,
|
||||
imageQuality: 85,
|
||||
);
|
||||
|
||||
if (image != null) {
|
||||
setState(() {
|
||||
_isCapturing = true;
|
||||
});
|
||||
|
||||
Position? position = _currentPosition;
|
||||
try {
|
||||
// Attempt to extract GPS from EXIF tags
|
||||
final exifGps = await ExifHelper.extractGps(image.path);
|
||||
if (exifGps != null) {
|
||||
position = Position(
|
||||
latitude: exifGps['latitude']!,
|
||||
longitude: exifGps['longitude']!,
|
||||
timestamp: DateTime.now(),
|
||||
accuracy: 0.0,
|
||||
altitude: 0.0,
|
||||
heading: 0.0,
|
||||
speed: 0.0,
|
||||
speedAccuracy: 0.0,
|
||||
altitudeAccuracy: 0.0,
|
||||
headingAccuracy: 0.0,
|
||||
);
|
||||
} else if (position == null) {
|
||||
position = await LocationService.determinePosition().timeout(const Duration(seconds: 2));
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
setState(() {
|
||||
_isCapturing = false;
|
||||
});
|
||||
|
||||
navigateToPreview(image.path, latitude: position?.latitude, longitude: position?.longitude);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error picking image from gallery: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void navigateToPreview(String imagePath, {double? latitude, double? longitude}) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ImagePreviewScreen(
|
||||
imagePath: imagePath,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final activeMode = ref.watch(scanModeProvider);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppConfig.backgroundColor,
|
||||
appBar: AppBar(
|
||||
title: Text(activeMode == 'DO' ? 'Pemindai Dokumen OCR' : 'Pemindai Produk PFM'),
|
||||
),
|
||||
drawer: const CameraDrawer(),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: AppConfig.screenPadding,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Upper Illustration Card
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadiusXL),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: AppConfig.paddingXL,
|
||||
child: SingleChildScrollView(
|
||||
child: activeMode == 'DO'
|
||||
? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppConfig.primaryColor.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.document_scanner,
|
||||
size: 48,
|
||||
color: AppConfig.primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
Text(
|
||||
'Pemindai DO',
|
||||
style: AppConfig.h3,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
Text(
|
||||
'Ambil foto lembar Delivery Order (DO) Prima Fresh Mart untuk mengekstrak data PO, SO, DO, dan item barang secara otomatis.',
|
||||
style: AppConfig.bodyMedium.copyWith(color: AppConfig.textSecondary),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
_buildGuidelineRow(Icons.lightbulb_outline, 'Pastikan pencahayaan cukup dan terang.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.crop_free, 'Posisikan seluruh halaman dokumen di dalam foto.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.camera_enhance_outlined, 'Pegang HP dengan stabil agar tidak buram.'),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppConfig.primaryColor.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.inventory_2_outlined,
|
||||
size: 48,
|
||||
color: AppConfig.primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
Text(
|
||||
'Pemindai Produk',
|
||||
style: AppConfig.h3,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
Text(
|
||||
'Ambil foto kemasan produk Prima Fresh Mart untuk mengidentifikasi SKU, nama item, dan mendeteksi tanggal kadaluwarsa secara otomatis.',
|
||||
style: AppConfig.bodyMedium.copyWith(color: AppConfig.textSecondary),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
_buildGuidelineRow(Icons.lightbulb_outline, 'Pastikan kode SKU atau teks kadaluwarsa terlihat jelas.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.center_focus_strong, 'Posisikan produk di tengah kamera.'),
|
||||
const SizedBox(height: AppConfig.spaceSmall),
|
||||
_buildGuidelineRow(Icons.flash_on, 'Gunakan pencahayaan yang cukup agar tidak buram.'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceXL),
|
||||
|
||||
// Action Buttons
|
||||
ElevatedButton.icon(
|
||||
onPressed: _isCapturing ? null : _takePicture,
|
||||
icon: const Icon(Icons.camera_alt),
|
||||
label: const Text('Ambil Foto Kamera'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18),
|
||||
textStyle: AppConfig.bodyLarge.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
OutlinedButton.icon(
|
||||
onPressed: _isCapturing ? null : _pickFromGallery,
|
||||
icon: const Icon(Icons.photo_library),
|
||||
label: const Text('Pilih Dari Galeri'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18),
|
||||
side: const BorderSide(color: AppConfig.primaryColor),
|
||||
foregroundColor: AppConfig.primaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadiusMedium),
|
||||
),
|
||||
textStyle: AppConfig.bodyLarge.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppConfig.spaceMedium),
|
||||
TextButton.icon(
|
||||
onPressed: _isCapturing ? null : () => context.push('/documents'),
|
||||
icon: const Icon(Icons.history_edu),
|
||||
label: const Text('Riwayat Dokumen'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppConfig.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGuidelineRow(IconData icon, String text) {
|
||||
return Row(
|
||||
children: [
|
||||
Icon(icon, size: 18, color: AppConfig.secondaryColor),
|
||||
const SizedBox(width: AppConfig.spaceSmall),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: AppConfig.bodySmall.copyWith(color: AppConfig.textSecondary),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user