5th commit

This commit is contained in:
2025-11-10 12:40:46 +05:30
parent 4946d6b8c0
commit dd72f058a9
64 changed files with 422 additions and 115 deletions

View File

@@ -26,7 +26,7 @@ class MyApp extends StatelessWidget {
Theme.of(context).textTheme,
)
),
initialRoute: AppRouter.bookingPage,
initialRoute: AppRouter.splashScreen,
onGenerateRoute: AppRouter.generateRoute,
);
}

Binary file not shown.

View File

@@ -1,7 +1,7 @@
import 'dart:io';
import 'package:citycards_partner_flutter/core/app_router.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import '../bloc/qr_scan_bloc.dart';
import '../bloc/qr_scan_event.dart';
@@ -19,6 +19,7 @@ class _QrScanScreenState extends State<QrScanScreen>
with WidgetsBindingObserver {
late MobileScannerController _cameraController;
final ValueNotifier<bool> _isTorchOn = ValueNotifier(false);
bool _cameraAvailable = true;
final collapsedKey = GlobalKey();
final ValueNotifier<double> sheetExtent = ValueNotifier(0.5);
@@ -30,7 +31,17 @@ class _QrScanScreenState extends State<QrScanScreen>
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_cameraController = MobileScannerController();
// 🔍 Detect if running in iOS Simulator
if (Platform.isIOS && !Platform.isMacOS) {
_cameraAvailable = !Platform.environment.containsKey(
'SIMULATOR_DEVICE_NAME',
);
}
// 🔦 Update torch state
_cameraController.addListener(() {
final torchState = _cameraController.value.torchState;
_isTorchOn.value = (torchState == TorchState.on);
@@ -48,6 +59,8 @@ class _QrScanScreenState extends State<QrScanScreen>
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (!mounted) return;
if (!_cameraAvailable) return;
if (state == AppLifecycleState.paused) {
_cameraController.stop();
} else if (state == AppLifecycleState.resumed) {
@@ -55,14 +68,17 @@ class _QrScanScreenState extends State<QrScanScreen>
}
}
void _safeStartCamera() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (mounted) {
try {
await _cameraController.start();
} catch (_) {}
/// ✅ Start camera safely (prevent multiple starts)
void _safeStartCamera() async {
if (!mounted || !_cameraAvailable) return;
try {
if (!_cameraController.value.isStarting) {
await _cameraController.start();
}
});
} catch (e) {
debugPrint("⚠️ Camera already running or failed to start: $e");
}
}
@override
@@ -101,37 +117,47 @@ class _QrScanScreenState extends State<QrScanScreen>
backgroundColor: Colors.black,
body: Stack(
children: [
/// 📷 QR Scanner
MobileScanner(
controller: _cameraController,
fit: BoxFit.cover,
onDetect: (capture) {
final barcode =
capture.barcodes.first.rawValue ?? '';
if (barcode.isNotEmpty && !state.isLocked) {
viewModel.onQrDetected(barcode);
}
},
),
/// 🚫 Show this if no camera available (iOS simulator)
if (!_cameraAvailable)
const Center(
child: Text(
"Camera not available on iOS Simulator.\nPlease test on a real device.",
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
textAlign: TextAlign.center,
),
)
else
/// 📷 QR Scanner
MobileScanner(
controller: _cameraController,
fit: BoxFit.cover,
onDetect: (capture) {
final barcode =
capture.barcodes.first.rawValue ?? '';
if (barcode.isNotEmpty && !state.isLocked) {
viewModel.onQrDetected(barcode);
}
},
),
/// 🔲 Scanner Frame
Positioned(
left: scanCenter.dx - 125,
top: scanCenter.dy - 200,
child: CustomPaint(
size: const Size(250, 250),
painter: CornerBorderPainter(
const LinearGradient(
colors: [
Colors.white,
Colors.white,
Colors.white,
],
if (_cameraAvailable)
Positioned(
left: scanCenter.dx - 125,
top: scanCenter.dy - 200,
child: CustomPaint(
size: const Size(250, 250),
painter: CornerBorderPainter(
const LinearGradient(
colors: [Colors.white, Colors.white],
),
strokeWidth: 4,
),
strokeWidth: 4,
),
),
),
/// 🧾 Draggable Bottom Sheet
SafeArea(
@@ -250,14 +276,15 @@ class _QrScanScreenState extends State<QrScanScreen>
),
);
}
Widget _headerIcons(
BuildContext context,
bool isExpanded,
QrScanStatus status,
MobileScannerController controller,
DraggableScrollableController sheetController,
double initFraction,
) {
BuildContext context,
bool isExpanded,
QrScanStatus status,
MobileScannerController controller,
DraggableScrollableController sheetController,
double initFraction,
) {
final icons = [
{'img': 'assets/scan/flash.png', 'route': '/flash'},
{'img': 'assets/scan/menu.png', 'route': '/menu'},
@@ -342,7 +369,6 @@ class _QrScanScreenState extends State<QrScanScreen>
);
}
/// ✅ Success Layout
Widget _successLayout() => Card(
color: Colors.green[50],

View File

@@ -81,7 +81,7 @@ class _SupportFormView extends StatelessWidget {
body: BlocBuilder<TicketBloc, TicketState>(
builder: (context, state) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [