Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
mobile_scanner
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
Julian Steenbakker
2022-12-08 11:44:02 +0100
Browse Files
Options
Browse Files
Download
Plain Diff
Committed by
GitHub
2022-12-08 11:44:02 +0100
Commit
8428bdb162751d2aca743efe1ff1c37cc65aeb17
8428bdb1
2 parents
6b0d799f
944a3920
Merge branch 'master' into feature/zoom
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
374 additions
and
206 deletions
CHANGELOG.md
android/build.gradle
android/gradle/wrapper/gradle-wrapper.properties
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerPlugin.kt
example/lib/barcode_list_scanner_controller.dart
example/lib/barcode_scanner_controller.dart
example/lib/barcode_scanner_returning_image.dart
ios/Classes/SwiftMobileScannerPlugin.swift
lib/mobile_scanner.dart
lib/src/enums/mobile_scanner_error_code.dart
lib/src/enums/mobile_scanner_state.dart
lib/src/mobile_scanner.dart
lib/src/mobile_scanner_controller.dart
lib/src/mobile_scanner_exception.dart
macos/Classes/MobileScannerPlugin.swift
pubspec.yaml
CHANGELOG.md
View file @
8428bdb
## 3.0.0-beta.3
Deprecated:
*
The
`onStart` method has been renamed to `onScannerStarted`
.
*
The
`onPermissionSet` argument of the `MobileScannerController`
is now deprecated.
Breaking changes:
*
`MobileScannerException` now uses an `errorCode` instead of a `message`
.
*
`MobileScannerException`
now contains additional details from the original error.
*
Refactored
`MobileScannerController.start()` to throw `MobileScannerException`
s
with consistent error codes, rather than string messages.
To handle permission errors, consider catching the result of
`MobileScannerController.start()`
.
*
The
`autoResume` attribute has been removed from the `MobileScanner`
widget.
The controller already automatically resumes, so it had no effect.
*
Removed
`MobileScannerCallback` and `MobileScannerArgumentsCallback`
typedef.
Improvements:
*
Toggling the device torch now does nothing if the device has no torch, rather than throwing an error.
*
Removed
`called stop while already stopped`
messages.
Features:
*
Added a new
`placeholderBuilder` function to the `MobileScanner`
widget to customize the preview placeholder.
*
Added
`autoStart`
parameter to MobileScannerController(). If set to false, controller won't start automatically.
*
Added
`hasTorch`
function on MobileScannerController(). After starting the controller, you can check if the device has a torch.
Fixes:
*
Fixes the missing gradle setup for the Android project, which prevented gradle sync from working.
*
Fixes
`MobileScannerController.stop()`
throwing when already stopped.
*
Fixes
`MobileScannerController.toggleTorch()`
throwing if the device has no torch.
Now it does nothing if the torch is not available.
*
Fixes a memory leak where the
`MobileScanner`
would keep listening to the barcode events.
*
Fixes the
`MobileScanner` preview depending on all attributes of `MediaQueryData`
.
Now it only depends on its layout constraints.
*
Fixed a potential crash when the scanner is restarted due to the app being resumed.
## 3.0.0-beta.2
Breaking changes:
*
The arguments parameter of onDetect is removed. The data is now returned by the onStart callback
...
...
android/build.gradle
View file @
8428bdb
...
...
@@ -10,6 +10,7 @@ buildscript {
dependencies
{
classpath
'com.android.tools.build:gradle:7.3.1'
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
...
...
android/gradle/wrapper/gradle-wrapper.properties
0 → 100644
View file @
8428bdb
#Fri Jun 23 08:50:38 CEST 2017
distributionBase
=
GRADLE_USER_HOME
distributionPath
=
wrapper/dists
zipStoreBase
=
GRADLE_USER_HOME
zipStorePath
=
wrapper/dists
distributionUrl
=
https
\:
//services.gradle.org/distributions/gradle-7.4-all.zip
...
...
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
View file @
8428bdb
...
...
@@ -17,10 +17,9 @@ import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
import dev.steenbakker.mobile_scanner.objects.DetectionSpeed
import dev.steenbakker.mobile_scanner.objects.MobileScannerStartParameters
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
import io.flutter.view.TextureRegistry
typealias PermissionCallback = (permissionGranted: Boolean) -> Unit
typealias MobileScannerCallback = (barcodes: List<Map<String, Any?>>, image: ByteArray?) -> Unit
typealias AnalyzerCallback = (barcodes: List<Map<String, Any?>>?) -> Unit
typealias MobileScannerErrorCallback = (error: String) -> Unit
...
...
@@ -51,10 +50,9 @@ class MobileScanner(
private const val REQUEST_CODE = 0x0786
}
private var listener: PluginRegistry.RequestPermissionsResultListener? = null
private var cameraProvider: ProcessCameraProvider? = null
private var camera: Camera? = null
private var pendingPermissionResult: MethodChannel.Result? = null
private var preview: Preview? = null
private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null
...
...
@@ -88,16 +86,12 @@ class MobileScanner(
/**
* Request camera permissions.
*/
fun requestPermission(permissionCallback: PermissionCallback) {
listener = PluginRegistry.RequestPermissionsResultListener { requestCode, _, grantResults ->
if (requestCode != REQUEST_CODE) {
false
} else {
val authorized = grantResults[0] == PackageManager.PERMISSION_GRANTED
permissionCallback(authorized)
true
}
}
fun requestPermission(result: MethodChannel.Result) {
if(pendingPermissionResult != null) {
return
}
pendingPermissionResult = result
val permissions = arrayOf(Manifest.permission.CAMERA)
ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE)
}
...
...
@@ -110,7 +104,14 @@ class MobileScanner(
permissions: Array<out String>,
grantResults: IntArray
): Boolean {
return listener?.onRequestPermissionsResult(requestCode, permissions, grantResults) ?: false
if (requestCode != REQUEST_CODE) {
return false
}
pendingPermissionResult?.success(grantResults[0] == PackageManager.PERMISSION_GRANTED)
pendingPermissionResult = null
return true
}
/**
...
...
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerPlugin.kt
View file @
8428bdb
...
...
@@ -23,14 +23,8 @@ class MobileScannerPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCa
private lateinit var barcodeHandler: BarcodeHandler
private var permissionResult: MethodChannel.Result? = null
private var analyzerResult: MethodChannel.Result? = null
private val permissionCallback: PermissionCallback = {hasPermission: Boolean ->
permissionResult?.success(hasPermission)
permissionResult = null
}
private val callback: MobileScannerCallback = { barcodes: List<Map<String, Any?>>, image: ByteArray? ->
if (image != null) {
barcodeHandler.publishEvent(mapOf(
...
...
@@ -78,7 +72,7 @@ class MobileScannerPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCa
}
when (call.method) {
"state" -> result.success(handler!!.hasCameraPermission())
"request" -> requestPermission(result)
"request" ->
handler!!.
requestPermission(result)
"start" -> start(call, result)
"torch" -> toggleTorch(call, result)
"stop" -> stop(result)
...
...
@@ -125,11 +119,6 @@ class MobileScannerPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCa
onDetachedFromActivity()
}
private fun requestPermission(result: MethodChannel.Result) {
permissionResult = result
handler!!.requestPermission(permissionCallback)
}
@ExperimentalGetImage
private fun start(call: MethodCall, result: MethodChannel.Result) {
val torch: Boolean = call.argument<Boolean>("torch") ?: false
...
...
@@ -209,7 +198,7 @@ class MobileScannerPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCa
handler!!.stop()
result.success(null)
} catch (e: AlreadyStopped) {
result.
error("MobileScanner", "Called stop() while already stopped!",
null)
result.
success(
null)
}
}
...
...
example/lib/barcode_list_scanner_controller.dart
View file @
8428bdb
...
...
@@ -15,13 +15,10 @@ class _BarcodeListScannerWithControllerState
with
SingleTickerProviderStateMixin
{
BarcodeCapture
?
barcodeCapture
;
MobileScannerController
controller
=
MobileScannerController
(
final
MobileScannerController
controller
=
MobileScannerController
(
torchEnabled:
true
,
// formats: [BarcodeFormat.qrCode]
// facing: CameraFacing.front,
onPermissionSet:
(
hasPermission
)
{
// Do something with permission callback
},
// detectionSpeed: DetectionSpeed.normal
// detectionTimeoutMs: 1000,
// returnImage: false,
...
...
@@ -29,6 +26,31 @@ class _BarcodeListScannerWithControllerState
bool
isStarted
=
true
;
void
_startOrStop
()
{
if
(
isStarted
)
{
controller
.
stop
();
}
else
{
controller
.
start
().
catchError
((
error
)
{
final
exception
=
error
as
MobileScannerException
;
switch
(
exception
.
errorCode
)
{
case
MobileScannerErrorCode
.
controllerUninitialized
:
break
;
// This error code is not used by `start()`.
case
MobileScannerErrorCode
.
genericError
:
debugPrint
(
'Scanner failed to start'
);
break
;
case
MobileScannerErrorCode
.
permissionDenied
:
debugPrint
(
'Camera permission denied'
);
break
;
}
});
}
setState
(()
{
isStarted
=
!
isStarted
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
...
...
@@ -40,17 +62,13 @@ class _BarcodeListScannerWithControllerState
MobileScanner
(
controller:
controller
,
fit:
BoxFit
.
contain
,
// controller: MobileScannerController(
// torchEnabled: true,
// facing: CameraFacing.front,
// ),
onDetect:
(
barcodeCapture
)
{
setState
(()
{
this
.
barcodeCapture
=
barcodeCapture
;
});
},
onStart:
(
arguments
)
{
// Do something with start arguments
onScannerStarted:
(
arguments
)
{
// Do something with arguments.
},
),
Align
(
...
...
@@ -96,10 +114,7 @@ class _BarcodeListScannerWithControllerState
?
const
Icon
(
Icons
.
stop
)
:
const
Icon
(
Icons
.
play_arrow
),
iconSize:
32.0
,
onPressed:
()
=>
setState
(()
{
isStarted
?
controller
.
stop
()
:
controller
.
start
();
isStarted
=
!
isStarted
;
}),
onPressed:
_startOrStop
,
),
Center
(
child:
SizedBox
(
...
...
@@ -177,4 +192,10 @@ class _BarcodeListScannerWithControllerState
),
);
}
@override
void
dispose
()
{
controller
.
dispose
();
super
.
dispose
();
}
}
...
...
example/lib/barcode_scanner_controller.dart
View file @
8428bdb
...
...
@@ -15,13 +15,10 @@ class _BarcodeScannerWithControllerState
with
SingleTickerProviderStateMixin
{
BarcodeCapture
?
barcode
;
MobileScannerController
controller
=
MobileScannerController
(
final
MobileScannerController
controller
=
MobileScannerController
(
torchEnabled:
true
,
// formats: [BarcodeFormat.qrCode]
// facing: CameraFacing.front,
onPermissionSet:
(
hasPermission
)
{
// Do something with permission callback
},
// detectionSpeed: DetectionSpeed.normal
// detectionTimeoutMs: 1000,
// returnImage: false,
...
...
@@ -29,6 +26,31 @@ class _BarcodeScannerWithControllerState
bool
isStarted
=
true
;
void
_startOrStop
()
{
if
(
isStarted
)
{
controller
.
stop
();
}
else
{
controller
.
start
().
catchError
((
error
)
{
final
exception
=
error
as
MobileScannerException
;
switch
(
exception
.
errorCode
)
{
case
MobileScannerErrorCode
.
controllerUninitialized
:
break
;
// This error code is not used by `start()`.
case
MobileScannerErrorCode
.
genericError
:
debugPrint
(
'Scanner failed to start'
);
break
;
case
MobileScannerErrorCode
.
permissionDenied
:
debugPrint
(
'Camera permission denied'
);
break
;
}
});
}
setState
(()
{
isStarted
=
!
isStarted
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
...
...
@@ -40,10 +62,6 @@ class _BarcodeScannerWithControllerState
MobileScanner
(
controller:
controller
,
fit:
BoxFit
.
contain
,
// controller: MobileScannerController(
// torchEnabled: true,
// facing: CameraFacing.front,
// ),
onDetect:
(
barcode
)
{
setState
(()
{
this
.
barcode
=
barcode
;
...
...
@@ -93,10 +111,7 @@ class _BarcodeScannerWithControllerState
?
const
Icon
(
Icons
.
stop
)
:
const
Icon
(
Icons
.
play_arrow
),
iconSize:
32.0
,
onPressed:
()
=>
setState
(()
{
isStarted
?
controller
.
stop
()
:
controller
.
start
();
isStarted
=
!
isStarted
;
}),
onPressed:
_startOrStop
,
),
Center
(
child:
SizedBox
(
...
...
@@ -175,4 +190,10 @@ class _BarcodeScannerWithControllerState
),
);
}
@override
void
dispose
()
{
controller
.
dispose
();
super
.
dispose
();
}
}
...
...
example/lib/barcode_scanner_returning_image.dart
View file @
8428bdb
...
...
@@ -17,13 +17,10 @@ class _BarcodeScannerReturningImageState
BarcodeCapture
?
barcode
;
MobileScannerArguments
?
arguments
;
MobileScannerController
controller
=
MobileScannerController
(
final
MobileScannerController
controller
=
MobileScannerController
(
torchEnabled:
true
,
// formats: [BarcodeFormat.qrCode]
// facing: CameraFacing.front,
onPermissionSet:
(
hasPermission
)
{
// Do something with permission callback
},
// detectionSpeed: DetectionSpeed.normal
// detectionTimeoutMs: 1000,
returnImage:
true
,
...
...
@@ -31,6 +28,31 @@ class _BarcodeScannerReturningImageState
bool
isStarted
=
true
;
void
_startOrStop
()
{
if
(
isStarted
)
{
controller
.
stop
();
}
else
{
controller
.
start
().
catchError
((
error
)
{
final
exception
=
error
as
MobileScannerException
;
switch
(
exception
.
errorCode
)
{
case
MobileScannerErrorCode
.
controllerUninitialized
:
break
;
// This error code is not used by `start()`.
case
MobileScannerErrorCode
.
genericError
:
debugPrint
(
'Scanner failed to start'
);
break
;
case
MobileScannerErrorCode
.
permissionDenied
:
debugPrint
(
'Camera permission denied'
);
break
;
}
});
}
setState
(()
{
isStarted
=
!
isStarted
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
...
...
@@ -62,10 +84,6 @@ class _BarcodeScannerReturningImageState
MobileScanner
(
controller:
controller
,
fit:
BoxFit
.
contain
,
// controller: MobileScannerController(
// torchEnabled: true,
// facing: CameraFacing.front,
// ),
onDetect:
(
barcode
)
{
setState
(()
{
this
.
barcode
=
barcode
;
...
...
@@ -115,12 +133,7 @@ class _BarcodeScannerReturningImageState
?
const
Icon
(
Icons
.
stop
)
:
const
Icon
(
Icons
.
play_arrow
),
iconSize:
32.0
,
onPressed:
()
=>
setState
(()
{
isStarted
?
controller
.
stop
()
:
controller
.
start
();
isStarted
=
!
isStarted
;
}),
onPressed:
_startOrStop
,
),
Center
(
child:
SizedBox
(
...
...
@@ -171,4 +184,10 @@ class _BarcodeScannerReturningImageState
),
);
}
@override
void
dispose
()
{
controller
.
dispose
();
super
.
dispose
();
}
}
...
...
ios/Classes/SwiftMobileScannerPlugin.swift
View file @
8428bdb
...
...
@@ -109,11 +109,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin {
private
func
stop
(
_
result
:
@escaping
FlutterResult
)
{
do
{
try
mobileScanner
.
stop
()
}
catch
{
result
(
FlutterError
(
code
:
"MobileScanner"
,
message
:
"Called stop() while already stopped!"
,
details
:
nil
))
}
}
catch
{}
result
(
nil
)
}
...
...
lib/mobile_scanner.dart
View file @
8428bdb
...
...
@@ -2,11 +2,13 @@ library mobile_scanner;
export
'src/enums/camera_facing.dart'
;
export
'src/enums/detection_speed.dart'
;
export
'src/enums/mobile_scanner_error_code.dart'
;
export
'src/enums/mobile_scanner_state.dart'
;
export
'src/enums/ratio.dart'
;
export
'src/enums/torch_state.dart'
;
export
'src/mobile_scanner.dart'
;
export
'src/mobile_scanner_controller.dart'
;
export
'src/mobile_scanner_exception.dart'
;
export
'src/objects/barcode.dart'
;
export
'src/objects/barcode_capture.dart'
;
export
'src/objects/mobile_scanner_arguments.dart'
;
...
...
lib/src/enums/mobile_scanner_error_code.dart
0 → 100644
View file @
8428bdb
/// This enum defines the different error codes for the mobile scanner.
enum
MobileScannerErrorCode
{
/// The controller was used
/// while it was not yet initialized using [MobileScannerController.start].
controllerUninitialized
,
/// A generic error occurred.
///
/// This error code is used for all errors that do not have a specific error code.
genericError
,
/// The permission to use the camera was denied.
permissionDenied
,
}
...
...
lib/src/enums/mobile_scanner_state.dart
View file @
8428bdb
/// The authorization state of the scanner.
enum
MobileScannerState
{
/// The scanner has
yet to request weather it is [authorized] or [denied]
/// The scanner has
not yet requested the required permissions.
undetermined
,
/// The scanner has the required permissions.
...
...
lib/src/mobile_scanner.dart
View file @
8428bdb
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:mobile_scanner/src/mobile_scanner_controller.dart'
;
import
'package:mobile_scanner/src/objects/barcode_capture.dart'
;
import
'package:mobile_scanner/src/objects/mobile_scanner_arguments.dart'
;
typedef
MobileScannerCallback
=
void
Function
(
BarcodeCapture
barcodes
);
typedef
MobileScannerArgumentsCallback
=
void
Function
(
MobileScannerArguments
?
arguments
,
);
/// A widget showing a live camera preview.
/// The [MobileScanner] widget displays a live camera preview.
class
MobileScanner
extends
StatefulWidget
{
/// The controller of the camera.
/// The controller that manages the barcode scanner.
///
/// If this is null, the scanner will manage its own controller.
final
MobileScannerController
?
controller
;
/// Calls the provided [onPermissionSet] callback when the permission is set.
// @Deprecated('Use the [onPermissionSet] paremeter in the [MobileScannerController] instead.')
// ignore: deprecated_consistency
final
Function
(
bool
permissionGranted
)?
onPermissionSet
;
/// Function that gets called when a Barcode is detected.
/// The [BoxFit] for the camera preview.
///
/// [barcode] The barcode object with all information about the scanned code.
/// [startInternalArguments] Information about the state of the MobileScanner widget
final
MobileScannerCallback
onDetect
;
/// Defaults to [BoxFit.cover].
final
BoxFit
fit
;
/// Function that gets called when the scanner is started.
///
/// [arguments] The start arguments of the scanner. This contains the size of
/// the scanner which can be used to draw a box over the scanner.
final
MobileScannerArgumentsCallback
?
onStart
;
/// The function that signals when new codes were detected by the [controller].
final
void
Function
(
BarcodeCapture
barcodes
)
onDetect
;
/// Handles how the widget should fit the screen.
final
BoxFit
fit
;
/// The function that signals when the barcode scanner is started.
@Deprecated
(
'Use onScannerStarted() instead.'
)
final
void
Function
(
MobileScannerArguments
?
arguments
)?
onStart
;
/// Whether to automatically resume the camera when the application is resumed
final
bool
autoResume
;
/// The function that signals when the barcode scanner is started.
final
void
Function
(
MobileScannerArguments
?
arguments
)?
onScannerStarted
;
/// Create a [MobileScanner] with a [controller], the [controller] must has been initialized.
/// The function that builds a placeholder widget when the scanner
/// is not yet displaying its camera preview.
///
/// If this is null, a black [ColoredBox] is used as placeholder.
final
Widget
Function
(
BuildContext
,
Widget
?)?
placeholderBuilder
;
/// Create a new [MobileScanner] using the provided [controller]
/// and [onBarcodeDetected] callback.
const
MobileScanner
({
super
.
key
,
required
this
.
onDetect
,
this
.
onStart
,
this
.
controller
,
this
.
autoResume
=
true
,
this
.
fit
=
BoxFit
.
cover
,
@Deprecated
(
'Use the [onPermissionSet] paremeter in the [MobileScannerController] instead.'
)
this
.
onPermissionSet
,
required
this
.
onDetect
,
@Deprecated
(
'Use onScannerStarted() instead.'
)
this
.
onStart
,
this
.
onScannerStarted
,
this
.
placeholderBuilder
,
super
.
key
,
});
@override
...
...
@@ -55,104 +52,109 @@ class MobileScanner extends StatefulWidget {
class
_MobileScannerState
extends
State
<
MobileScanner
>
with
WidgetsBindingObserver
{
late
MobileScannerController
controller
;
/// The subscription that listens to barcode detection.
StreamSubscription
<
BarcodeCapture
>?
_barcodesSubscription
;
/// The internally managed controller.
late
MobileScannerController
_controller
;
/// Whether the controller should resume
/// when the application comes back to the foreground.
bool
_resumeFromBackground
=
false
;
/// Start the given [scanner].
void
_startScanner
(
MobileScannerController
scanner
)
{
if
(!
_controller
.
autoStart
)
{
debugPrint
(
'mobile_scanner: not starting automatically because autoStart is set to false in the controller.'
,
);
return
;
}
scanner
.
start
().
then
((
arguments
)
{
// ignore: deprecated_member_use_from_same_package
widget
.
onStart
?.
call
(
arguments
);
widget
.
onScannerStarted
?.
call
(
arguments
);
});
}
@override
void
initState
()
{
super
.
initState
();
WidgetsBinding
.
instance
.
addObserver
(
this
);
controller
=
widget
.
controller
??
MobileScannerController
(
onPermissionSet:
widget
.
onPermissionSet
);
if
(!
controller
.
isStarting
)
{
_startScanner
();
}
}
_controller
=
widget
.
controller
??
MobileScannerController
();
Future
<
void
>
_startScanner
()
async
{
final
arguments
=
await
controller
.
start
();
widget
.
onStart
?.
call
(
arguments
);
}
_barcodesSubscription
=
_controller
.
barcodes
.
listen
(
widget
.
onDetect
,
);
bool
resumeFromBackground
=
false
;
if
(!
_controller
.
isStarting
)
{
_startScanner
(
_controller
);
}
}
@override
void
didChangeAppLifecycleState
(
AppLifecycleState
state
)
{
// App state changed before it is initialized.
if
(
controller
.
isStarting
)
{
// App state changed before the controller was initialized.
if
(
_controller
.
isStarting
)
{
return
;
}
switch
(
state
)
{
case
AppLifecycleState
.
resumed
:
resumeFromBackground
=
false
;
_startScanner
();
_resumeFromBackground
=
false
;
_startScanner
(
_controller
);
break
;
case
AppLifecycleState
.
paused
:
resumeFromBackground
=
true
;
_
resumeFromBackground
=
true
;
break
;
case
AppLifecycleState
.
inactive
:
if
(!
resumeFromBackground
)
controller
.
stop
();
if
(!
_resumeFromBackground
)
{
_controller
.
stop
();
}
break
;
default
:
case
AppLifecycleState
.
detached
:
break
;
}
}
@override
Widget
build
(
BuildContext
context
)
{
return
ValueListenableBuilder
(
valueListenable:
controller
.
startArguments
,
return
ValueListenableBuilder
<
MobileScannerArguments
?>(
valueListenable:
_controller
.
startArguments
,
builder:
(
context
,
value
,
child
)
{
value
=
value
as
MobileScannerArguments
?;
if
(
value
==
null
)
{
return
const
ColoredBox
(
color:
Colors
.
black
);
}
else
{
controller
.
barcodes
.
listen
((
barcode
)
{
widget
.
onDetect
(
barcode
);
});
return
ClipRect
(
child:
SizedBox
(
width:
MediaQuery
.
of
(
context
).
size
.
width
,
height:
MediaQuery
.
of
(
context
).
size
.
height
,
child:
FittedBox
(
fit:
widget
.
fit
,
child:
SizedBox
(
width:
value
.
size
.
width
,
height:
value
.
size
.
height
,
child:
kIsWeb
?
HtmlElementView
(
viewType:
value
.
webId
!)
:
Texture
(
textureId:
value
.
textureId
!),
),
),
),
);
return
widget
.
placeholderBuilder
?.
call
(
context
,
child
)
??
const
ColoredBox
(
color:
Colors
.
black
);
}
return
ClipRect
(
child:
LayoutBuilder
(
builder:
(
_
,
constraints
)
{
return
SizedBox
.
fromSize
(
size:
constraints
.
biggest
,
child:
FittedBox
(
fit:
widget
.
fit
,
child:
SizedBox
(
width:
value
.
size
.
width
,
height:
value
.
size
.
height
,
child:
kIsWeb
?
HtmlElementView
(
viewType:
value
.
webId
!)
:
Texture
(
textureId:
value
.
textureId
!),
),
),
);
},
),
);
},
);
}
@override
void
didUpdateWidget
(
covariant
MobileScanner
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
oldWidget
.
controller
==
null
)
{
if
(
widget
.
controller
!=
null
)
{
controller
.
dispose
();
controller
=
widget
.
controller
!;
}
}
else
{
if
(
widget
.
controller
==
null
)
{
controller
=
MobileScannerController
(
onPermissionSet:
widget
.
onPermissionSet
);
}
else
if
(
oldWidget
.
controller
!=
widget
.
controller
)
{
controller
=
widget
.
controller
!;
}
}
}
@override
void
dispose
()
{
controller
.
dispose
();
WidgetsBinding
.
instance
.
removeObserver
(
this
);
_barcodesSubscription
?.
cancel
();
_controller
.
dispose
();
super
.
dispose
();
}
}
...
...
lib/src/mobile_scanner_controller.dart
View file @
8428bdb
...
...
@@ -6,7 +6,6 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/services.dart'
;
import
'package:mobile_scanner/mobile_scanner.dart'
;
import
'package:mobile_scanner/src/barcode_utility.dart'
;
import
'package:mobile_scanner/src/mobile_scanner_exception.dart'
;
/// The [MobileScannerController] holds all the logic of this plugin,
/// where as the [MobileScanner] class is the frontend of this plugin.
...
...
@@ -18,7 +17,9 @@ class MobileScannerController {
this
.
torchEnabled
=
false
,
this
.
formats
,
this
.
returnImage
=
false
,
this
.
onPermissionSet
,
@Deprecated
(
'Instead, use the result of calling `start()` to determine if permissions were granted.'
)
this
.
onPermissionSet
,
this
.
autoStart
=
true
,
})
{
// In case a new instance is created before calling dispose()
if
(
controllerHashcode
!=
null
)
{
...
...
@@ -64,6 +65,9 @@ class MobileScannerController {
/// [DetectionSpeed.normal] (which is the default value).
final
int
detectionTimeoutMs
;
/// Automatically start the mobileScanner on initialization.
final
bool
autoStart
;
/// Sets the barcode stream
final
StreamController
<
BarcodeCapture
>
_barcodesController
=
StreamController
.
broadcast
();
...
...
@@ -74,6 +78,9 @@ class MobileScannerController {
static
const
EventChannel
_eventChannel
=
EventChannel
(
'dev.steenbakker.mobile_scanner/scanner/event'
);
@Deprecated
(
'Instead, use the result of calling `start()` to determine if permissions were granted.'
,
)
Function
(
bool
permissionGranted
)?
onPermissionSet
;
/// Listen to events from the platform specific code
...
...
@@ -94,6 +101,19 @@ class MobileScannerController {
bool
?
_hasTorch
;
/// Returns whether the device has a torch.
///
/// Throws an error if the controller is not initialized.
bool
get
hasTorch
{
if
(
_hasTorch
==
null
)
{
throw
const
MobileScannerException
(
errorCode:
MobileScannerErrorCode
.
controllerUninitialized
,
);
}
return
_hasTorch
!;
}
/// Set the starting arguments for the camera
Map
<
String
,
dynamic
>
_argumentsToMap
({
CameraFacing
?
cameraFacingOverride
})
{
final
Map
<
String
,
dynamic
>
arguments
=
{};
...
...
@@ -115,8 +135,14 @@ class MobileScannerController {
return
arguments
;
}
/// Start barcode scanning. This will first check if the required permissions
/// are set.
/// Start scanning for barcodes.
/// Upon calling this method, the necessary camera permission will be requested.
///
/// Returns an instance of [MobileScannerArguments]
/// when the scanner was successfully started.
/// Returns null if the scanner is currently starting.
///
/// Throws a [MobileScannerException] if starting the scanner failed.
Future
<
MobileScannerArguments
?>
start
({
CameraFacing
?
cameraFacingOverride
,
})
async
{
...
...
@@ -124,6 +150,7 @@ class MobileScannerController {
debugPrint
(
"Called start() while starting."
);
return
null
;
}
isStarting
=
true
;
// Check authorization status
...
...
@@ -136,16 +163,17 @@ class MobileScannerController {
await
_methodChannel
.
invokeMethod
(
'request'
)
as
bool
?
??
false
;
if
(!
result
)
{
isStarting
=
false
;
onPermissionSet
?.
call
(
result
);
throw
MobileScannerException
(
'User declined camera permission.'
);
throw
const
MobileScannerException
(
errorCode:
MobileScannerErrorCode
.
permissionDenied
,
);
}
break
;
case
MobileScannerState
.
denied
:
isStarting
=
false
;
onPermissionSet
?.
call
(
false
);
throw
MobileScannerException
(
'User declined camera permission.'
);
throw
const
MobileScannerException
(
errorCode:
MobileScannerErrorCode
.
permissionDenied
,
);
case
MobileScannerState
.
authorized
:
onPermissionSet
?.
call
(
true
);
break
;
}
}
...
...
@@ -158,18 +186,27 @@ class MobileScannerController {
_argumentsToMap
(
cameraFacingOverride:
cameraFacingOverride
),
);
}
on
PlatformException
catch
(
error
)
{
debugPrint
(
'
${error.code}
:
${error.message}
'
);
MobileScannerErrorCode
errorCode
=
MobileScannerErrorCode
.
genericError
;
if
(
error
.
code
==
"MobileScannerWeb"
)
{
onPermissionSet
?.
call
(
false
)
;
errorCode
=
MobileScannerErrorCode
.
permissionDenied
;
}
isStarting
=
false
;
return
null
;
throw
MobileScannerException
(
errorCode:
errorCode
,
errorDetails:
MobileScannerErrorDetails
(
code:
error
.
code
,
details:
error
.
details
as
Object
?,
message:
error
.
message
,
),
);
}
if
(
startResult
==
null
)
{
isStarting
=
false
;
throw
MobileScannerException
(
'Failed to start mobileScanner, no response from platform side'
,
throw
const
MobileScannerException
(
errorCode:
MobileScannerErrorCode
.
genericError
,
);
}
...
...
@@ -178,13 +215,6 @@ class MobileScannerController {
torchState
.
value
=
TorchState
.
on
;
}
if
(
kIsWeb
)
{
// If we reach this line, it means camera permission has been granted
onPermissionSet
?.
call
(
true
,
);
}
isStarting
=
false
;
return
startArguments
.
value
=
MobileScannerArguments
(
size:
kIsWeb
...
...
@@ -210,14 +240,18 @@ class MobileScannerController {
/// Switches the torch on or off.
///
/// Only works if torch is available.
/// Does nothing if the device has no torch.
///
/// Throws if the controller was not initialized.
Future
<
void
>
toggleTorch
()
async
{
if
(
_hasTorch
==
null
)
{
throw
MobileScannerException
(
'Cannot toggle torch if start() has never been called'
,
final
hasTorch
=
_hasTorch
;
if
(
hasTorch
==
null
)
{
throw
const
MobileScannerException
(
errorCode:
MobileScannerErrorCode
.
controllerUninitialized
,
);
}
else
if
(!
_hasTorch
!)
{
throw
MobileScannerException
(
'Device has no torch'
);
}
else
if
(!
hasTorch
)
{
return
;
}
torchState
.
value
=
...
...
@@ -271,7 +305,6 @@ class MobileScannerController {
_barcodesController
.
close
();
if
(
hashCode
==
controllerHashcode
)
{
controllerHashcode
=
null
;
onPermissionSet
=
null
;
}
}
...
...
@@ -320,7 +353,10 @@ class MobileScannerController {
);
break
;
case
'error'
:
throw
MobileScannerException
(
data
as
String
);
throw
MobileScannerException
(
errorCode:
MobileScannerErrorCode
.
genericError
,
errorDetails:
MobileScannerErrorDetails
(
message:
data
as
String
?),
);
default
:
throw
UnimplementedError
(
name
as
String
?);
}
...
...
lib/src/mobile_scanner_exception.dart
View file @
8428bdb
import
'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'
;
/// This class represents an exception thrown by the mobile scanner.
class
MobileScannerException
implements
Exception
{
String
message
;
MobileScannerException
(
this
.
message
);
const
MobileScannerException
({
required
this
.
errorCode
,
this
.
errorDetails
,
});
/// The error code of the exception.
final
MobileScannerErrorCode
errorCode
;
/// The additional error details that came with the [errorCode].
final
MobileScannerErrorDetails
?
errorDetails
;
}
/// The raw error details for a [MobileScannerException].
class
MobileScannerErrorDetails
{
const
MobileScannerErrorDetails
({
this
.
code
,
this
.
details
,
this
.
message
,
});
/// The error code from the [PlatformException].
final
String
?
code
;
/// The details from the [PlatformException].
final
Object
?
details
;
/// The error message from the [PlatformException].
final
String
?
message
;
}
...
...
macos/Classes/MobileScannerPlugin.swift
View file @
8428bdb
...
...
@@ -238,9 +238,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
func
toggleTorch
(
_
call
:
FlutterMethodCall
,
_
result
:
@escaping
FlutterResult
)
{
if
(
device
==
nil
)
{
result
(
FlutterError
(
code
:
"MobileScanner"
,
message
:
"Called toggleTorch() while stopped!"
,
details
:
nil
))
result
(
nil
)
return
}
do
{
...
...
@@ -260,9 +258,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
func
stop
(
_
result
:
FlutterResult
)
{
if
(
device
==
nil
)
{
result
(
FlutterError
(
code
:
"MobileScanner"
,
message
:
"Called stop() while already stopped!"
,
details
:
nil
))
result
(
nil
)
return
}
captureSession
.
stopRunning
()
...
...
pubspec.yaml
View file @
8428bdb
name
:
mobile_scanner
description
:
A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS.
version
:
3.0.0-beta.
2
version
:
3.0.0-beta.
3
repository
:
https://github.com/juliansteenbakker/mobile_scanner
environment
:
...
...
Please
register
or
login
to post a comment