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

inventree / inventree-app / 12275919744

11 Dec 2024 12:01PM UTC coverage: 8.518% (-0.03%) from 8.545%
12275919744

push

github

web-flow
Merge pull request #575 from inventree/receive-location

Receive location

3 of 61 new or added lines in 25 files covered. (4.92%)

11 existing lines in 5 files now uncovered.

723 of 8488 relevant lines covered (8.52%)

0.3 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
   */
96
  void _onScanSuccess(Code? code) {
×
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
        scanning_paused = barcode.isNotEmpty;
×
126
      });
127
    }
128

129
    if (barcode.isNotEmpty) {
×
130

131
      handleBarcodeData(barcode).then((_) {
×
132
        if (!single_scanning && mounted) {
×
133
          // Resume next scan
134
          setState(() {
×
135
            scanning_paused = false;
×
136
          });
137
        }
138
      });
139
    }
140
  }
141

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

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

NEW
158
      if (OneContext.hasContext) {
×
NEW
159
        Navigator.pop(OneContext().context!);
×
160
      }
161
    }
162
  }
163

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

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

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

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

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

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

233
  Widget bottomCenterOverlay() {
×
234

235
    String info_text = scanning_paused ? L10().barcodeScanPaused : L10().barcodeScanPause;
×
236

237
    String text = scanned_code.isNotEmpty ? scanned_code : info_text;
×
238

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

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

267

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

297
  @override
×
298
  Widget build(BuildContext context) {
299

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

329
}
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