Files
be_happy_public/lib/presentation/screens/qr_scan_screen.dart

221 lines
6.7 KiB
Dart

import 'dart:ui' as ui;
import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/scooter.dart';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:go_router/go_router.dart';
import 'package:be_happy/di/service_locator.dart';
import 'package:be_happy/domain/usecase/get_scooter_by_title_usecase.dart';
import '../components/custom_app_bar.dart';
import '../components/gradient_button.dart';
class QrScanScreen extends StatefulWidget {
const QrScanScreen({super.key});
@override
State<QrScanScreen> createState() => _QrScanScreenState();
}
class _QrScanScreenState extends State<QrScanScreen> {
final MobileScannerController _controller = MobileScannerController();
String? _scannedData;
String? _scooterTitle;
bool _torchOn = false;
bool _isLoading = false;
@override
void dispose() {
_controller.dispose();
super.dispose();
}
bool _isProcessing = false;
void _handleScannedData(String rawValue) async {
if (_isProcessing || _isLoading) return;
final uri = Uri.tryParse(rawValue);
if (uri == null || uri.host != 'behappybel.by') return;
final title = uri.pathSegments.last;
print("TITLE IS: $title");
if (title.isEmpty) return;
setState(() {
_isProcessing = true;
_isLoading = true;
});
await _controller.stop();
try {
final getScooterByTitleUsecase = getIt<GetScooterByTitleUsecase>();
print("UseCase успешно получен из DI");
final result = await getScooterByTitleUsecase(title);
print("UseCase успешно выполнен");
if (mounted) {
setState(() => _isLoading = false);
switch (result) {
case Success<Scooter?>():
final scooter = result.data;
if (scooter != null) {
context.pop();
context.push('/home/scooter/${scooter.id}');
} else {
_showErrorAndRestart('Самокат не найден');
}
case Failure<Scooter?>():
_showErrorAndRestart('Ошибка при поиске самоката');
}
}
} catch (e) {
print("Ошибка DI: $e");
}
}
void _showErrorAndRestart(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
Future.delayed(const Duration(seconds: 2), () {
if (mounted) {
setState(() {
_isProcessing = false;
_scannedData = null;
});
_controller.start();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
MobileScanner(
controller: _controller,
onDetect: (capture) {
if (_isProcessing) return;
for (final barcode in capture.barcodes) {
final String? rawValue = barcode.rawValue;
if (rawValue != null) {
_handleScannedData(rawValue);
break;
}
}
},
),
ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.7),
BlendMode.srcOut,
),
child: Stack(
children: [
Container(
decoration: const BoxDecoration(
color: Colors.black,
backgroundBlendMode: BlendMode.dstOut,
),
),
Center(
child: Container(
width: 280,
height: 280,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(0),
),
),
),
],
),
),
Center(
child: Container(
width: 280,
height: 280,
decoration: BoxDecoration(
border: Border.all(color: const Color(0xFF6EE7B7), width: 3),
),
),
),
// ✅ ИЗМЕНЕНО: прижимаем аппбар к левому краю
SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // ✅ Выравнивание по левому краю
children: [
const SizedBox(height: 16),
CustomAppBar(title: "Сканирование QR-кода"),
const SizedBox(height: 60),
const Text(
'Наведите рамку на QR-код — номер будет распознан автоматически',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
shadows: [
Shadow(blurRadius: 4, color: Colors.black),
],
),
),
],
),
),
SafeArea(
child: Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Expanded(
child: GradientButton(
text: 'Ввести номер вручную',
onTap: () {
context.push('/home/qr-info/qr-input');
},
width: double.infinity,
height: 56,
fontSize: 16,
showArrows: true,
),
),
const SizedBox(width: 12),
CircleAvatar(
backgroundColor: Colors.black.withOpacity(0.5),
child: IconButton(
onPressed: () async {
final newState = !_torchOn;
await _controller.toggleTorch();
setState(() => _torchOn = newState);
},
icon: Icon(
_torchOn ? Icons.flashlight_on : Icons.flashlight_off,
color: Colors.white,
size: 24,
),
),
),
],
),
),
),
),
],
),
);
}
}