p-mazhnik

feat: add hasTorch notifier

@@ -77,34 +77,41 @@ class _BarcodeScannerWithControllerState @@ -77,34 +77,41 @@ class _BarcodeScannerWithControllerState
77 child: Row( 77 child: Row(
78 mainAxisAlignment: MainAxisAlignment.spaceEvenly, 78 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
79 children: [ 79 children: [
80 - IconButton(  
81 - color: Colors.white,  
82 - icon: ValueListenableBuilder(  
83 - valueListenable: controller.torchState, 80 + ValueListenableBuilder(
  81 + valueListenable: controller.hasTorchState,
84 builder: (context, state, child) { 82 builder: (context, state, child) {
85 - if (state == null) {  
86 - return const Icon(  
87 - Icons.flash_off,  
88 - color: Colors.grey,  
89 - ); 83 + if (state != true) {
  84 + return const SizedBox.shrink();
90 } 85 }
91 - switch (state as TorchState) {  
92 - case TorchState.off:  
93 - return const Icon(  
94 - Icons.flash_off,  
95 - color: Colors.grey,  
96 - );  
97 - case TorchState.on:  
98 - return const Icon(  
99 - Icons.flash_on,  
100 - color: Colors.yellow,  
101 - );  
102 - }  
103 - },  
104 - ),  
105 - iconSize: 32.0,  
106 - onPressed: () => controller.toggleTorch(),  
107 - ), 86 + return IconButton(
  87 + color: Colors.white,
  88 + icon: ValueListenableBuilder(
  89 + valueListenable: controller.torchState,
  90 + builder: (context, state, child) {
  91 + if (state == null) {
  92 + return const Icon(
  93 + Icons.flash_off,
  94 + color: Colors.grey,
  95 + );
  96 + }
  97 + switch (state as TorchState) {
  98 + case TorchState.off:
  99 + return const Icon(
  100 + Icons.flash_off,
  101 + color: Colors.grey,
  102 + );
  103 + case TorchState.on:
  104 + return const Icon(
  105 + Icons.flash_on,
  106 + color: Colors.yellow,
  107 + );
  108 + }
  109 + },
  110 + ),
  111 + iconSize: 32.0,
  112 + onPressed: () => controller.toggleTorch(),
  113 + );
  114 + }),
108 IconButton( 115 IconButton(
109 color: Colors.white, 116 color: Colors.white,
110 icon: isStarted 117 icon: isStarted
@@ -99,7 +99,8 @@ class MobileScannerController { @@ -99,7 +99,8 @@ class MobileScannerController {
99 99
100 bool isStarting = false; 100 bool isStarting = false;
101 101
102 - bool? _hasTorch; 102 + /// A notifier that provides availability of the Torch (Flash)
  103 + final ValueNotifier<bool?> hasTorchState = ValueNotifier(false);
103 104
104 /// Returns whether the device has a torch. 105 /// Returns whether the device has a torch.
105 /// 106 ///
@@ -210,8 +211,9 @@ class MobileScannerController { @@ -210,8 +211,9 @@ class MobileScannerController {
210 ); 211 );
211 } 212 }
212 213
213 - _hasTorch = startResult['torchable'] as bool? ?? false;  
214 - if (_hasTorch! && torchEnabled) { 214 + final hasTorch = startResult['torchable'] as bool? ?? false;
  215 + hasTorchState.value = hasTorch;
  216 + if (hasTorch && torchEnabled) {
215 torchState.value = TorchState.on; 217 torchState.value = TorchState.on;
216 } 218 }
217 219
@@ -223,7 +225,7 @@ class MobileScannerController { @@ -223,7 +225,7 @@ class MobileScannerController {
223 startResult['videoHeight'] as double? ?? 0, 225 startResult['videoHeight'] as double? ?? 0,
224 ) 226 )
225 : toSize(startResult['size'] as Map? ?? {}), 227 : toSize(startResult['size'] as Map? ?? {}),
226 - hasTorch: _hasTorch!, 228 + hasTorch: hasTorch,
227 textureId: kIsWeb ? null : startResult['textureId'] as int?, 229 textureId: kIsWeb ? null : startResult['textureId'] as int?,
228 webId: kIsWeb ? startResult['ViewID'] as String? : null, 230 webId: kIsWeb ? startResult['ViewID'] as String? : null,
229 ); 231 );
@@ -244,7 +246,7 @@ class MobileScannerController { @@ -244,7 +246,7 @@ class MobileScannerController {
244 /// 246 ///
245 /// Throws if the controller was not initialized. 247 /// Throws if the controller was not initialized.
246 Future<void> toggleTorch() async { 248 Future<void> toggleTorch() async {
247 - final hasTorch = _hasTorch; 249 + final hasTorch = hasTorchState.value;
248 250
249 if (hasTorch == null) { 251 if (hasTorch == null) {
250 throw const MobileScannerException( 252 throw const MobileScannerException(
@@ -98,8 +98,7 @@ mixin InternalStreamCreation on WebBarcodeReaderBase { @@ -98,8 +98,7 @@ mixin InternalStreamCreation on WebBarcodeReaderBase {
98 98
99 /// Mixin for libraries that don't have built-in torch support 99 /// Mixin for libraries that don't have built-in torch support
100 mixin InternalTorchDetection on InternalStreamCreation { 100 mixin InternalTorchDetection on InternalStreamCreation {
101 - @override  
102 - Future<bool> hasTorch() async { 101 + Future<List<String>> getSupportedTorchStates() async {
103 try { 102 try {
104 final track = localMediaStream?.getVideoTracks(); 103 final track = localMediaStream?.getVideoTracks();
105 if (track != null) { 104 if (track != null) {
@@ -107,13 +106,21 @@ mixin InternalTorchDetection on InternalStreamCreation { @@ -107,13 +106,21 @@ mixin InternalTorchDetection on InternalStreamCreation {
107 final photoCapabilities = await promiseToFuture<PhotoCapabilities>( 106 final photoCapabilities = await promiseToFuture<PhotoCapabilities>(
108 imageCapture.getPhotoCapabilities(), 107 imageCapture.getPhotoCapabilities(),
109 ); 108 );
110 - return photoCapabilities.fillLightMode != null; 109 + final fillLightMode = photoCapabilities.fillLightMode;
  110 + if (fillLightMode != null) {
  111 + return fillLightMode;
  112 + }
111 } 113 }
112 } catch (e) { 114 } catch (e) {
113 // ImageCapture is not supported by some browsers: 115 // ImageCapture is not supported by some browsers:
114 // https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture#browser_compatibility 116 // https://developer.mozilla.org/en-US/docs/Web/API/ImageCapture#browser_compatibility
115 } 117 }
116 - return false; 118 + return [];
  119 + }
  120 +
  121 + @override
  122 + Future<bool> hasTorch() async {
  123 + return (await getSupportedTorchStates()).isNotEmpty;
117 } 124 }
118 125
119 @override 126 @override