Se agrego el sdk para el pda de honeywell
This commit is contained in:
@@ -3,9 +3,14 @@ import 'package:qrscanner/models/auth_session.dart';
|
||||
import 'package:qrscanner/screens/login_screen.dart';
|
||||
import 'package:qrscanner/screens/scanner_screen.dart';
|
||||
import 'package:qrscanner/services/auth_service.dart';
|
||||
import 'package:qrscanner/services/hardware_scanner_service.dart';
|
||||
import 'package:qrscanner/services/scan_launch_service.dart';
|
||||
import 'package:qrscanner/theme/app_theme.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
final RouteObserver<ModalRoute<void>> homeRouteObserver =
|
||||
RouteObserver<ModalRoute<void>>();
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({
|
||||
super.key,
|
||||
required this.session,
|
||||
@@ -13,7 +18,29 @@ class HomeScreen extends StatelessWidget {
|
||||
|
||||
final AuthSession session;
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreenState();
|
||||
}
|
||||
|
||||
class _HomeScreenState extends State<HomeScreen> {
|
||||
bool _hardwareAvailable = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkHardware();
|
||||
}
|
||||
|
||||
Future<void> _checkHardware() async {
|
||||
final available = await ScanLaunchService.instance.isSupported();
|
||||
if (mounted) {
|
||||
setState(() => _hardwareAvailable = available);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _logout(BuildContext context) async {
|
||||
await HardwareScannerService.instance.ensureStopped();
|
||||
await ScanLaunchService.instance.releaseScanner();
|
||||
await AuthService().clearSession();
|
||||
|
||||
if (!context.mounted) return;
|
||||
@@ -30,11 +57,12 @@ class HomeScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _openScanner(BuildContext context) {
|
||||
Navigator.of(context).push(
|
||||
Future<void> _openScanner(BuildContext context) async {
|
||||
await Navigator.of(context).push(
|
||||
PageRouteBuilder<void>(
|
||||
settings: const RouteSettings(name: '/scanner'),
|
||||
pageBuilder: (context, animation, secondaryAnimation) =>
|
||||
ScannerScreen(session: session),
|
||||
ScannerScreen(session: widget.session),
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
return FadeTransition(
|
||||
opacity: animation,
|
||||
@@ -57,7 +85,7 @@ class HomeScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final expiresHours = (session.expiresIn / 3600).round();
|
||||
final expiresHours = (widget.session.expiresIn / 3600).round();
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
@@ -140,7 +168,7 @@ class HomeScreen extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
Text(
|
||||
'Hola, ${session.username}',
|
||||
'Hola, ${widget.session.username}',
|
||||
style:
|
||||
Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -175,6 +203,40 @@ class HomeScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_hardwareAvailable) ...[
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.primary.withValues(alpha: 0.12),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: AppTheme.primary.withValues(alpha: 0.25),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.barcode_reader,
|
||||
size: 16,
|
||||
color: AppTheme.primary.withValues(alpha: 0.9),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Escáner activo · abre la app al escanear',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: 0.7),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 48),
|
||||
_ScanButton(onPressed: () => _openScanner(context)),
|
||||
],
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qrscanner/config/api_config.dart';
|
||||
import 'package:qrscanner/models/auth_session.dart';
|
||||
import 'package:qrscanner/screens/home_screen.dart';
|
||||
import 'package:qrscanner/services/auth_service.dart';
|
||||
import 'package:qrscanner/services/scan_launch_service.dart';
|
||||
import 'package:qrscanner/services/scan_navigation_service.dart';
|
||||
import 'package:qrscanner/theme/app_theme.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
@@ -47,6 +48,7 @@ class _LoginScreenState extends State<LoginScreen>
|
||||
curve: Curves.easeOutCubic,
|
||||
));
|
||||
_animationController.forward();
|
||||
ScanLaunchService.instance.claimScanner();
|
||||
_checkStoredSession();
|
||||
}
|
||||
|
||||
@@ -54,7 +56,7 @@ class _LoginScreenState extends State<LoginScreen>
|
||||
final session = await _authService.loadStoredSession();
|
||||
if (!mounted || session == null) return;
|
||||
|
||||
_goToHome(session);
|
||||
await ScanNavigationService.instance.navigateToHome(session);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -83,7 +85,7 @@ class _LoginScreenState extends State<LoginScreen>
|
||||
if (!mounted) return;
|
||||
|
||||
if (response.success && response.accessToken != null) {
|
||||
_goToHome(
|
||||
await ScanNavigationService.instance.navigateToHome(
|
||||
AuthSession.fromLoginResponse(
|
||||
response.copyWith(
|
||||
username: response.username ?? _usernameController.text.trim(),
|
||||
@@ -109,19 +111,6 @@ class _LoginScreenState extends State<LoginScreen>
|
||||
}
|
||||
}
|
||||
|
||||
void _goToHome(AuthSession session) {
|
||||
Navigator.of(context).pushReplacement(
|
||||
PageRouteBuilder<void>(
|
||||
pageBuilder: (context, animation, secondaryAnimation) =>
|
||||
HomeScreen(session: session),
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
return FadeTransition(opacity: animation, child: child);
|
||||
},
|
||||
transitionDuration: const Duration(milliseconds: 400),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
+333
-20
@@ -1,25 +1,33 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
import 'package:qrscanner/models/auth_session.dart';
|
||||
import 'package:qrscanner/models/pass_code.dart';
|
||||
import 'package:qrscanner/models/scan_response.dart';
|
||||
import 'package:qrscanner/services/feedback_service.dart';
|
||||
import 'package:qrscanner/services/hardware_scanner_service.dart';
|
||||
import 'package:qrscanner/services/pass_code_service.dart';
|
||||
import 'package:qrscanner/theme/app_theme.dart';
|
||||
|
||||
enum _ScanPhase { scanning, processing, success, warning }
|
||||
|
||||
enum _ScannerInput { loading, camera, hardware }
|
||||
|
||||
const double _scanWindowSize = 260;
|
||||
const double _scanWindowRadius = 28;
|
||||
const Alignment _scanWindowAlignment = Alignment(0, -0.12);
|
||||
const Duration _resultDisplayDuration = Duration(seconds: 5);
|
||||
|
||||
class ScannerScreen extends StatefulWidget {
|
||||
const ScannerScreen({
|
||||
super.key,
|
||||
required this.session,
|
||||
this.initialCode,
|
||||
});
|
||||
|
||||
final AuthSession session;
|
||||
final String? initialCode;
|
||||
|
||||
@override
|
||||
State<ScannerScreen> createState() => _ScannerScreenState();
|
||||
@@ -27,21 +35,21 @@ class ScannerScreen extends StatefulWidget {
|
||||
|
||||
class _ScannerScreenState extends State<ScannerScreen>
|
||||
with TickerProviderStateMixin {
|
||||
final _scannerController = MobileScannerController(
|
||||
detectionSpeed: DetectionSpeed.normal,
|
||||
detectionTimeoutMs: 500,
|
||||
facing: CameraFacing.back,
|
||||
formats: const [BarcodeFormat.qrCode],
|
||||
autoZoom: true,
|
||||
);
|
||||
MobileScannerController? _cameraController;
|
||||
final _hardwareScanner = HardwareScannerService.instance;
|
||||
final _passCodeService = PassCodeService();
|
||||
final _feedbackService = FeedbackService();
|
||||
|
||||
_ScannerInput _inputMode = _ScannerInput.loading;
|
||||
bool _hardwareAvailable = false;
|
||||
bool _isInitialSetup = true;
|
||||
bool _isSwitchingInput = false;
|
||||
_ScanPhase _phase = _ScanPhase.scanning;
|
||||
bool _isHandlingDetection = false;
|
||||
String? _lastScannedCode;
|
||||
String? _message;
|
||||
PassCode? _passCode;
|
||||
Timer? _resultAutoDismissTimer;
|
||||
|
||||
late final AnimationController _scanLineController;
|
||||
late final AnimationController _resultController;
|
||||
@@ -76,8 +84,158 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
parent: _resultController,
|
||||
curve: Curves.easeOutBack,
|
||||
));
|
||||
_initializeScannerInput();
|
||||
}
|
||||
|
||||
Future<void> _initializeScannerInput() async {
|
||||
_hardwareAvailable = await HardwareScannerService.isAvailable();
|
||||
if (!mounted) return;
|
||||
|
||||
if (_hardwareAvailable) {
|
||||
await _activateHardwareMode(showLoading: true);
|
||||
} else {
|
||||
await _activateCameraMode(showLoading: true);
|
||||
}
|
||||
|
||||
final pendingCode = widget.initialCode?.trim();
|
||||
if (!mounted || pendingCode == null || pendingCode.isEmpty) return;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) _processScan(pendingCode);
|
||||
});
|
||||
}
|
||||
|
||||
MobileScannerController _createCameraController() {
|
||||
return MobileScannerController(
|
||||
autoStart: false,
|
||||
detectionSpeed: DetectionSpeed.normal,
|
||||
detectionTimeoutMs: 500,
|
||||
facing: CameraFacing.back,
|
||||
formats: const [BarcodeFormat.qrCode],
|
||||
autoZoom: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _activateHardwareMode({bool showLoading = false}) async {
|
||||
if (!mounted) return;
|
||||
if (showLoading) {
|
||||
setState(() => _inputMode = _ScannerInput.loading);
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
await _cameraController?.stop();
|
||||
} catch (_) {}
|
||||
await _cameraController?.dispose();
|
||||
_cameraController = null;
|
||||
_scanLineController.stop();
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
final started = await _hardwareScanner.activate(onScan: _onRawScan);
|
||||
if (!mounted) return;
|
||||
|
||||
if (started) {
|
||||
setState(() {
|
||||
_inputMode = _ScannerInput.hardware;
|
||||
_isInitialSetup = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await _activateCameraMode(
|
||||
showLoading: showLoading,
|
||||
fallbackFromHardware: true,
|
||||
);
|
||||
} finally {
|
||||
if (mounted && _inputMode == _ScannerInput.loading) {
|
||||
setState(() {
|
||||
_inputMode = _ScannerInput.hardware;
|
||||
_isInitialSetup = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _activateCameraMode({
|
||||
bool showLoading = false,
|
||||
bool fallbackFromHardware = false,
|
||||
}) async {
|
||||
if (!mounted) return;
|
||||
if (showLoading) {
|
||||
setState(() => _inputMode = _ScannerInput.loading);
|
||||
}
|
||||
|
||||
try {
|
||||
await _hardwareScanner.pause();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
try {
|
||||
await _cameraController?.stop();
|
||||
} catch (_) {}
|
||||
await _cameraController?.dispose();
|
||||
_cameraController = _createCameraController();
|
||||
|
||||
if (!_scanLineController.isAnimating) {
|
||||
_scanLineController.repeat();
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_inputMode = _ScannerInput.camera;
|
||||
_isInitialSetup = false;
|
||||
});
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
try {
|
||||
await _cameraController!.start();
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
if (!fallbackFromHardware) {
|
||||
await _activateHardwareMode();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (mounted && _inputMode == _ScannerInput.loading) {
|
||||
setState(() {
|
||||
_inputMode = _ScannerInput.camera;
|
||||
_isInitialSetup = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _toggleScannerInput() async {
|
||||
if (!_hardwareAvailable ||
|
||||
_phase != _ScanPhase.scanning ||
|
||||
_isSwitchingInput ||
|
||||
_inputMode == _ScannerInput.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSwitchingInput = true);
|
||||
try {
|
||||
if (_inputMode == _ScannerInput.hardware) {
|
||||
await _activateCameraMode();
|
||||
} else if (_inputMode == _ScannerInput.camera) {
|
||||
await _activateHardwareMode();
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _isSwitchingInput = false);
|
||||
} else {
|
||||
_isSwitchingInput = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get _showsScannerInput =>
|
||||
_phase == _ScanPhase.scanning ||
|
||||
_phase == _ScanPhase.processing ||
|
||||
_phase == _ScanPhase.success ||
|
||||
_phase == _ScanPhase.warning;
|
||||
|
||||
Rect _scanWindowRect(Size size) {
|
||||
final center = _scanWindowAlignment.alongSize(size);
|
||||
return Rect.fromCenter(
|
||||
@@ -87,23 +245,58 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
);
|
||||
}
|
||||
|
||||
void _cancelAutoDismiss() {
|
||||
_resultAutoDismissTimer?.cancel();
|
||||
_resultAutoDismissTimer = null;
|
||||
}
|
||||
|
||||
void _scheduleAutoDismiss() {
|
||||
_cancelAutoDismiss();
|
||||
_resultAutoDismissTimer = Timer(_resultDisplayDuration, () {
|
||||
if (!mounted) return;
|
||||
if (_phase == _ScanPhase.success || _phase == _ScanPhase.warning) {
|
||||
unawaited(_scanAgain());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cancelAutoDismiss();
|
||||
_scanLineController.dispose();
|
||||
_resultController.dispose();
|
||||
_scannerController.dispose();
|
||||
_cameraController?.dispose();
|
||||
_hardwareScanner.deactivate();
|
||||
_feedbackService.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _onDetect(BarcodeCapture capture) async {
|
||||
if (_phase != _ScanPhase.scanning || _isHandlingDetection) return;
|
||||
void _onRawScan(String rawValue) {
|
||||
_processScan(rawValue);
|
||||
}
|
||||
|
||||
Future<void> _onDetect(BarcodeCapture capture) async {
|
||||
final rawValue = capture.barcodes.firstOrNull?.rawValue?.trim();
|
||||
if (rawValue == null || rawValue.isEmpty) return;
|
||||
await _processScan(rawValue);
|
||||
}
|
||||
|
||||
Future<void> _processScan(String rawValue) async {
|
||||
if (_isHandlingDetection || _phase == _ScanPhase.processing) return;
|
||||
|
||||
final code = _extractCode(rawValue);
|
||||
if (code.isEmpty || code == _lastScannedCode) return;
|
||||
if (code.isEmpty) return;
|
||||
|
||||
final showingResult =
|
||||
_phase == _ScanPhase.success || _phase == _ScanPhase.warning;
|
||||
|
||||
if (showingResult) {
|
||||
_cancelAutoDismiss();
|
||||
_resultController.reset();
|
||||
} else {
|
||||
if (_phase != _ScanPhase.scanning) return;
|
||||
if (code == _lastScannedCode) return;
|
||||
}
|
||||
|
||||
_isHandlingDetection = true;
|
||||
setState(() {
|
||||
@@ -114,7 +307,9 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
});
|
||||
|
||||
_scanLineController.stop();
|
||||
await _scannerController.stop();
|
||||
if (_inputMode == _ScannerInput.camera) {
|
||||
await _cameraController?.stop();
|
||||
}
|
||||
await _feedbackService.onCodeDetected();
|
||||
|
||||
try {
|
||||
@@ -166,9 +361,15 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
}
|
||||
|
||||
await _resultController.forward();
|
||||
_scheduleAutoDismiss();
|
||||
|
||||
if (_inputMode == _ScannerInput.camera) {
|
||||
await _cameraController?.start();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _scanAgain() async {
|
||||
_cancelAutoDismiss();
|
||||
_resultController.reset();
|
||||
_isHandlingDetection = false;
|
||||
setState(() {
|
||||
@@ -177,8 +378,10 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
_message = null;
|
||||
_passCode = null;
|
||||
});
|
||||
_scanLineController.repeat();
|
||||
await _scannerController.start();
|
||||
if (_inputMode == _ScannerInput.camera) {
|
||||
_scanLineController.repeat();
|
||||
await _cameraController?.start();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -203,16 +406,45 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
),
|
||||
title: const Text('Escanear pase'),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
if (_hardwareAvailable && _phase == _ScanPhase.scanning)
|
||||
IconButton(
|
||||
tooltip: _inputMode == _ScannerInput.hardware
|
||||
? 'Usar cámara'
|
||||
: 'Usar escáner Honeywell',
|
||||
icon: _isSwitchingInput
|
||||
? const SizedBox(
|
||||
width: 22,
|
||||
height: 22,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
_inputMode == _ScannerInput.hardware
|
||||
? Icons.camera_alt_outlined
|
||||
: Icons.barcode_reader,
|
||||
),
|
||||
onPressed: _isSwitchingInput ? null : _toggleScannerInput,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
if (_phase == _ScanPhase.scanning || _phase == _ScanPhase.processing)
|
||||
if (_inputMode == _ScannerInput.loading && _isInitialSetup)
|
||||
const Center(
|
||||
child: CircularProgressIndicator(color: AppTheme.primary),
|
||||
),
|
||||
if (_showsScannerInput &&
|
||||
_inputMode == _ScannerInput.camera &&
|
||||
_cameraController != null)
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final scanRect = _scanWindowRect(constraints.biggest);
|
||||
return MobileScanner(
|
||||
controller: _scannerController,
|
||||
controller: _cameraController!,
|
||||
onDetect: _onDetect,
|
||||
scanWindow: scanRect,
|
||||
overlayBuilder: (context, constraints) {
|
||||
@@ -236,8 +468,8 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
);
|
||||
},
|
||||
),
|
||||
if (_phase != _ScanPhase.scanning && _phase != _ScanPhase.processing)
|
||||
Container(color: AppTheme.backgroundDark),
|
||||
if (_showsScannerInput && _inputMode == _ScannerInput.hardware)
|
||||
_buildHardwareScannerView(),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 450),
|
||||
curve: Curves.easeOutCubic,
|
||||
@@ -256,7 +488,7 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_phase == _ScanPhase.scanning)
|
||||
if (_phase == _ScanPhase.scanning && _inputMode != _ScannerInput.loading)
|
||||
SafeArea(
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
@@ -452,11 +684,92 @@ class _ScannerScreenState extends State<ScannerScreen>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHardwareScannerView() {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
AppTheme.backgroundDark,
|
||||
Color(0xFF0D1B2A),
|
||||
Color(0xFF12182B),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 36),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 120,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
color: AppTheme.primary.withValues(alpha: 0.12),
|
||||
border: Border.all(
|
||||
color: AppTheme.primary.withValues(alpha: 0.35),
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.barcode_reader,
|
||||
size: 56,
|
||||
color: AppTheme.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 28),
|
||||
Text(
|
||||
'Escáner Honeywell',
|
||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Presiona el botón lateral del EDA51 para leer el código QR del pase.',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: Colors.white.withValues(alpha: 0.6),
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.06),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: Colors.white.withValues(alpha: 0.08),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Sensor dedicado · Honeywell ScanPal EDA51',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: 0.45),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHint() {
|
||||
final message = _inputMode == _ScannerInput.hardware
|
||||
? 'Presiona el botón lateral para escanear'
|
||||
: 'Apunta la cámara al código QR del pase';
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Text(
|
||||
'Apunta la cámara al código QR del pase',
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: 0.65),
|
||||
|
||||
Reference in New Issue
Block a user