Julian Steenbakker

imp: improve error handling, add scanner_error_widget.dart to lib and show error…

… messages when kdebugmode is true
... ... @@ -8,19 +8,6 @@ class ScannerErrorWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
String errorMessage;
switch (error.errorCode) {
case MobileScannerErrorCode.controllerUninitialized:
errorMessage = 'Controller not ready.';
case MobileScannerErrorCode.permissionDenied:
errorMessage = 'Permission denied';
case MobileScannerErrorCode.unsupported:
errorMessage = 'Scanning is unsupported on this device';
default:
errorMessage = 'Generic Error';
}
return ColoredBox(
color: Colors.black,
child: Center(
... ... @@ -32,11 +19,11 @@ class ScannerErrorWidget extends StatelessWidget {
child: Icon(Icons.error, color: Colors.white),
),
Text(
errorMessage,
error.errorCode.message,
style: const TextStyle(color: Colors.white),
),
Text(
error.errorDetails?.message ?? '',
if (error.errorDetails != null) Text(
error.errorDetails!.message ?? '',
style: const TextStyle(color: Colors.white),
),
],
... ...
... ... @@ -27,6 +27,23 @@ enum MobileScannerErrorCode {
/// Scanning is unsupported on the current device.
unsupported;
String get message {
switch (this) {
case MobileScannerErrorCode.controllerUninitialized:
return 'The MobileScannerController has not been initialized. Call start() before using it.';
case MobileScannerErrorCode.permissionDenied:
return 'Camera permission denied.';
case MobileScannerErrorCode.unsupported:
return 'Scanning is not supported on this device.';
case MobileScannerErrorCode.controllerAlreadyInitialized:
return 'The MobileScannerController is already running. Stop it before starting again.';
case MobileScannerErrorCode.controllerDisposed:
return 'The MobileScannerController was used after it was disposed.';
case MobileScannerErrorCode.genericError:
return 'An unexpected error occurred.';
}
}
/// Convert the given [PlatformException.code] to a [MobileScannerErrorCode].
factory MobileScannerErrorCode.fromPlatformException(
PlatformException exception,
... ...
... ... @@ -80,10 +80,10 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
);
}
throw const MobileScannerException(
errorCode: MobileScannerErrorCode.genericError,
throw MobileScannerException(
errorCode: MobileScannerErrorCode.unsupported,
errorDetails: MobileScannerErrorDetails(
message: 'Only Android, iOS and macOS are supported.',
message:MobileScannerErrorCode.unsupported.message,
),
);
}
... ... @@ -218,10 +218,10 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
@override
Future<MobileScannerViewAttributes> start(StartOptions startOptions) async {
if (!_pausing && _textureId != null) {
throw const MobileScannerException(
throw MobileScannerException(
errorCode: MobileScannerErrorCode.controllerAlreadyInitialized,
errorDetails: MobileScannerErrorDetails(
message: 'The scanner was already started.',
message: MobileScannerErrorCode.controllerAlreadyInitialized.message,
),
);
}
... ...
... ... @@ -6,6 +6,7 @@ import 'package:mobile_scanner/src/mobile_scanner_exception.dart';
import 'package:mobile_scanner/src/mobile_scanner_platform_interface.dart';
import 'package:mobile_scanner/src/objects/barcode_capture.dart';
import 'package:mobile_scanner/src/objects/mobile_scanner_state.dart';
import 'package:mobile_scanner/src/objects/scanner_error_widget.dart';
import 'package:mobile_scanner/src/scan_window_calculation.dart';
/// The function signature for the error builder.
... ... @@ -199,6 +200,8 @@ class _MobileScannerState extends State<MobileScanner>
return ValueListenableBuilder<MobileScannerState>(
valueListenable: controller,
builder: (BuildContext context, MobileScannerState value, Widget? child) {
// If the controller is still initializing, show a black screen, or user provided placeholder
if (!value.isInitialized) {
const Widget defaultPlaceholder = ColoredBox(color: Colors.black);
... ... @@ -207,12 +210,9 @@ class _MobileScannerState extends State<MobileScanner>
}
final MobileScannerException? error = value.error;
// If the controller encountered, show an error screen, or user provided placeholder
if (error != null) {
const Widget defaultError = ColoredBox(
color: Colors.black,
child: Center(child: Icon(Icons.error, color: Colors.white)),
);
final Widget defaultError = ScannerErrorWidget(error: error);
return widget.errorBuilder?.call(context, error, child) ??
defaultError;
... ...
... ... @@ -164,20 +164,20 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
void _throwIfNotInitialized() {
if (!value.isInitialized) {
throw const MobileScannerException(
throw MobileScannerException(
errorCode: MobileScannerErrorCode.controllerUninitialized,
errorDetails: MobileScannerErrorDetails(
message: 'The MobileScannerController has not been initialized.',
message: MobileScannerErrorCode.controllerUninitialized.message,
),
);
}
if (_isDisposed) {
throw const MobileScannerException(
throw MobileScannerException(
errorCode: MobileScannerErrorCode.controllerDisposed,
errorDetails: MobileScannerErrorDetails(
message:
'The MobileScannerController was used after it has been disposed.',
MobileScannerErrorCode.controllerDisposed.message,
),
);
}
... ... @@ -284,11 +284,10 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
/// If the permission is denied on iOS, MacOS or Web, there is no way to request it again.
Future<void> start({CameraFacing? cameraDirection}) async {
if (_isDisposed) {
throw const MobileScannerException(
throw MobileScannerException(
errorCode: MobileScannerErrorCode.controllerDisposed,
errorDetails: MobileScannerErrorDetails(
message:
'The MobileScannerController was used after it has been disposed.',
message: MobileScannerErrorCode.controllerDisposed.message,
),
);
}
... ...
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class ScannerErrorWidget extends StatelessWidget {
const ScannerErrorWidget({super.key, required this.error});
final MobileScannerException error;
@override
Widget build(BuildContext context) {
return ColoredBox(
color: Colors.black,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Padding(
padding: EdgeInsets.only(bottom: 16),
child: Icon(Icons.error, color: Colors.white),
),
if (kDebugMode) ...[
Text(
error.errorCode.message,
style: const TextStyle(color: Colors.white),
),
if (error.errorDetails != null) Text(
error.errorDetails!.message ?? '',
style: const TextStyle(color: Colors.white),
),
] else Text(
MobileScannerErrorCode.genericError.message,
style: const TextStyle(color: Colors.white),
),
],
),
),
);
}
}
... ...
... ... @@ -273,10 +273,10 @@ class MobileScannerWeb extends MobileScannerPlatform {
);
}
throw const MobileScannerException(
throw MobileScannerException(
errorCode: MobileScannerErrorCode.controllerAlreadyInitialized,
errorDetails: MobileScannerErrorDetails(
message: 'The scanner was already started.',
message: MobileScannerErrorCode.controllerAlreadyInitialized.message,
),
);
}
... ...