• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

inventree / inventree-app / 12326848637

14 Dec 2024 04:24AM UTC coverage: 8.386% (-0.1%) from 8.518%
12326848637

push

github

web-flow
[refactor] Scan improvements (#577)

* Handle error on unexpected barcode response

* Add ManufacturerPart detail view

* Support barcode scanning for manufacturer part

* Refactoring for null checks

* Ignore selected errors in sentry

* Fix API implementation for ManufacturerPart

* Update release notes

* More error handling

* Decode quantity betterer

* Refactoring

* Add option to confirm checkin details

* Improve response handlign

* Cleanup

* Remove unused imports

* Fix async function

* Fix for assigning custom barcode

* Handle barcode scan result for company

* Fix

* Adjust scan priority

* Refactoring MODEL_TYPE

- Use instead of duplicated const strings

* @override fix

4 of 241 new or added lines in 17 files covered. (1.66%)

12 existing lines in 9 files now uncovered.

723 of 8621 relevant lines covered (8.39%)

0.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lib/barcode/camera_controller.dart
1
import "dart:math";
2
import "dart:typed_data";
3

4
import "package:camera/camera.dart";
5
import "package:flutter/material.dart";
6
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
7
import "package:inventree/app_colors.dart";
8
import "package:inventree/inventree/sentry.dart";
9
import "package:inventree/preferences.dart";
10
import "package:inventree/widget/snacks.dart";
11
import "package:one_context/one_context.dart";
12
import "package:wakelock_plus/wakelock_plus.dart";
13
import "package:flutter_zxing/flutter_zxing.dart";
14

15
import "package:inventree/l10.dart";
16

17
import "package:inventree/barcode/handler.dart";
18
import "package:inventree/barcode/controller.dart";
19

20
/*
21
 * Barcode controller which uses the device's camera to scan barcodes.
22
 * Under the hood it uses the qr_code_scanner package.
23
 */
24
class CameraBarcodeController extends InvenTreeBarcodeController {
25
  const CameraBarcodeController(BarcodeHandler handler, {Key? key})
×
26
      : super(handler, key: key);
×
27

28
  @override
×
29
  State<StatefulWidget> createState() => _CameraBarcodeControllerState();
×
30
}
31

32
class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
33
  _CameraBarcodeControllerState() : super();
×
34

35
  bool flash_status = false;
36

37
  int scan_delay = 500;
38
  bool single_scanning = false;
39
  bool scanning_paused = false;
40

41
  String scanned_code = "";
42

43
  @override
×
44
  void initState() {
45
    super.initState();
×
46
    _loadSettings();
×
47
    WakelockPlus.enable();
×
48
  }
49

50
  @override
×
51
  void dispose() {
52
    super.dispose();
×
53
    WakelockPlus.disable();
×
54
  }
55

56
  /*
57
   * Load the barcode scanning settings
58
   */
59
  Future<void> _loadSettings() async {
×
60
    bool _single = await InvenTreeSettingsManager()
×
61
        .getBool(INV_BARCODE_SCAN_SINGLE, false);
×
62

63
    int _delay = await InvenTreeSettingsManager()
×
64
        .getValue(INV_BARCODE_SCAN_DELAY, 500) as int;
×
65

66
    if (mounted) {
×
67
      setState(() {
×
68
        scan_delay = _delay;
×
69
        single_scanning = _single;
×
70
        scanning_paused = false;
×
71
      });
72
    }
73
  }
74

75
  @override
×
76
  Future<void> pauseScan() async {
77
    if (mounted) {
×
78
      setState(() {
×
79
        scanning_paused = true;
×
80
      });
81
    }
82
  }
83

84
  @override
×
85
  Future<void> resumeScan() async {
86
    if (mounted) {
×
87
      setState(() {
×
88
        scanning_paused = false;
×
89
      });
90
    }
91
  }
92

93
  /*
94
   * Callback function when a barcode is scanned
95
   */
NEW
96
  Future<void> onScanSuccess(Code? code) async {
×
97

98
    if (scanning_paused) {
×
99
      return;
100
    }
101

102
    Uint8List raw_data = code?.rawBytes ?? Uint8List(0);
×
103

104
    // Reconstruct barcode from raw data
105
    String barcode;
106

107
    if (raw_data.isNotEmpty) {
×
108
      barcode = "";
109

110
      final buffer = StringBuffer();
×
111

112
      for (int i = 0; i < raw_data.length; i++) {
×
113
        buffer.writeCharCode(raw_data[i]);
×
114
      }
115

116
      barcode = buffer.toString();
×
117

118
    } else {
119
      barcode = code?.text ?? "";
×
120
    }
121

122
    if (mounted) {
×
123
      setState(() {
×
124
        scanned_code = barcode;
×
125
      });
126
    }
127

128
    if (barcode.isNotEmpty) {
×
129

NEW
130
      pauseScan();
×
131

NEW
132
      await handleBarcodeData(barcode).then((_) {
×
UNCOV
133
        if (!single_scanning && mounted) {
×
NEW
134
          resumeScan();
×
135
        }
136
      });
137
    }
138
  }
139

140
  void onControllerCreated(CameraController? controller, Exception? error) {
×
141
    if (error != null) {
142
      sentryReportError(
×
143
        "CameraBarcodeController.onControllerCreated",
144
        error,
145
        null
146
      );
147
    }
148

149
    if (controller == null) {
150
      showSnackIcon(
×
151
        L10().cameraCreationError,
×
152
        icon: TablerIcons.camera_x,
153
        success: false
154
      );
155

156
      if (OneContext.hasContext) {
×
157
        Navigator.pop(OneContext().context!);
×
158
      }
159
    }
160
  }
161

162
  /*
163
   * Build the barcode scanner overlay
164
   */
165
  FixedScannerOverlay BarcodeOverlay(BuildContext context) {
×
166

167
    // Note: Copied from reader_widget.dart:ReaderWidget.build
168
    final Size size = MediaQuery.of(context).size;
×
169
    final double cropSize = min(size.width, size.height) * 0.5;
×
170

171
    return FixedScannerOverlay(
×
172
      borderColor: scanning_paused ? COLOR_WARNING : COLOR_ACTION,
×
173
      overlayColor: Colors.black45,
174
      borderRadius: 1,
175
      borderLength: 15,
176
      borderWidth: 8,
177
      cutOutSize: cropSize,
178
    );
179
  }
180

181
  /*
182
   * Build the barcode reader widget
183
   */
184
  Widget BarcodeReader(BuildContext context) {
×
185

186
    return ReaderWidget(
×
NEW
187
      onScan: onScanSuccess,
×
188
      isMultiScan: false,
189
      tryHarder: true,
190
      tryInverted: true,
191
      tryRotate: true,
192
      showGallery: false,
193
      onControllerCreated: onControllerCreated,
×
194
      scanDelay: Duration(milliseconds: scan_delay),
×
195
      resolution: ResolutionPreset.high,
196
      lensDirection: CameraLensDirection.back,
197
      flashOnIcon: const Icon(Icons.flash_on),
198
      flashOffIcon: const Icon(Icons.flash_off),
199
      toggleCameraIcon: const Icon(TablerIcons.camera_rotate),
200
      actionButtonsBackgroundBorderRadius:
201
      BorderRadius.circular(40),
×
202
      scannerOverlay: BarcodeOverlay(context),
×
203
      actionButtonsBackgroundColor: Colors.black.withOpacity(0.7),
×
204
    );
205
  }
206

207
  Widget topCenterOverlay() {
×
208
    return SafeArea(
×
209
      child: Align(
×
210
        alignment: Alignment.topCenter,
211
        child: Padding(
×
212
          padding: EdgeInsets.only(
×
213
            left: 10,
214
            right: 10,
215
            top: 75,
216
            bottom: 10
217
          ),
218
          child: Text(
×
219
            widget.handler.getOverlayText(context),
×
220
            style: TextStyle(
×
221
              color: Colors.white,
222
              fontSize: 16,
223
              fontWeight: FontWeight.bold
224
            )
225
          )
226
        )
227
      )
228
    );
229
  }
230

231
  Widget bottomCenterOverlay() {
×
232

233
    String info_text = scanning_paused ? L10().barcodeScanPaused : L10().barcodeScanPause;
×
234

235
    String text = scanned_code.isNotEmpty ? scanned_code : info_text;
×
236

237
    if (text.length > 50) {
×
238
      text = text.substring(0, 50) + "...";
×
239
    }
240

241
    return SafeArea(
×
242
      child: Align(
×
243
        alignment: Alignment.bottomCenter,
244
        child: Padding(
×
245
          padding: EdgeInsets.only(
×
246
            left: 10,
247
            right: 10,
248
            top: 10,
249
            bottom: 75
250
          ),
251
          child: Text(
×
252
              text,
253
              textAlign: TextAlign.center,
254
              style: TextStyle(
×
255
                color: Colors.white,
256
                fontSize: 16,
257
                fontWeight: FontWeight.bold
258
              )
259
          ),
260
        )
261
      )
262
    );
263
  }
264

265

266
  /*
267
   *  Display an overlay at the bottom right of the screen
268
   */
269
  Widget bottomRightOverlay() {
×
270
    return SafeArea(
×
271
        child: Align(
×
272
            alignment: Alignment.bottomRight,
273
            child: Padding(
×
274
                padding: EdgeInsets.all(10),
×
275
                child: ClipRRect(
×
276
                    borderRadius: BorderRadius.circular(40),
×
277
                    child: ColoredBox(
×
278
                        color: Colors.black45,
279
                        child: Row(
×
280
                            mainAxisSize: MainAxisSize.min,
281
                            children: scanning_paused ? [] : [
×
282
                              CircularProgressIndicator(
×
283
                                  value: null
284
                              )
285
                              // actionIcon,
286
                            ]
287
                        )
288
                    )
289
                )
290
            )
291
        )
292
    );
293
  }
294

295
  @override
×
296
  Widget build(BuildContext context) {
297

298
    return Scaffold(
×
299
      appBar: AppBar(
×
300
        backgroundColor: COLOR_APP_BAR,
×
301
        title: Text(L10().scanBarcode),
×
302
      ),
303
      body: GestureDetector(
×
304
        onTap: () async {
×
305
          setState(() {
×
306
            scanning_paused = !scanning_paused;
×
307
          });
308
        },
309
        child: Stack(
×
310
          children: <Widget>[
×
311
            Column(
×
312
              children: [
×
313
                Expanded(
×
314
                    child: BarcodeReader(context)
×
315
                ),
316
              ],
317
            ),
318
            topCenterOverlay(),
×
319
            bottomCenterOverlay(),
×
320
            bottomRightOverlay(),
×
321
          ],
322
        ),
323
      ),
324
    );
325
  }
326

327
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc