diff --git a/.vscode/settings.json b/.vscode/settings.json index d26c00f..2692c62 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,7 @@ { - "cmake.sourceDirectory": "${workspaceFolder}/ios/Classes/src" + "cmake.sourceDirectory": "${workspaceFolder}/ios/Classes/src", + "files.associations": { + "__config": "cpp", + "__nullptr": "cpp" + } } \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 979477b..00998d5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:flutter_zxing_example/utils/db_service.dart'; import 'package:flutter_zxing_example/utils/extensions.dart'; import 'package:nb_utils/nb_utils.dart'; @@ -13,6 +14,7 @@ import 'utils/scroll_behavior.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await _initializeAppStore(); + await DbService.instance.initializeApp(); runApp(const MyApp()); } diff --git a/example/lib/models/code.dart b/example/lib/models/code.dart new file mode 100644 index 0000000..6b69c43 --- /dev/null +++ b/example/lib/models/code.dart @@ -0,0 +1,24 @@ +import 'package:flutter_zxing/flutter_zxing.dart'; +import 'package:hive_flutter/hive_flutter.dart'; + +part "code.g.dart"; + +@HiveType(typeId: 0) +class Code extends HiveObject { + @HiveField(0) + bool? isValid; + + @HiveField(1) + int? format; + + @HiveField(2) + String? text; + + Code(); + + Code.fromCodeResult(CodeResult result) { + isValid = result.isValidBool; + format = result.format; + text = result.textString; + } +} diff --git a/example/lib/models/code.g.dart b/example/lib/models/code.g.dart new file mode 100644 index 0000000..a2b169e --- /dev/null +++ b/example/lib/models/code.g.dart @@ -0,0 +1,46 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'code.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class CodeAdapter extends TypeAdapter { + @override + final int typeId = 0; + + @override + Code read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return Code() + ..isValid = fields[0] as bool? + ..format = fields[1] as int? + ..text = fields[2] as String?; + } + + @override + void write(BinaryWriter writer, Code obj) { + writer + ..writeByte(3) + ..writeByte(0) + ..write(obj.isValid) + ..writeByte(1) + ..write(obj.format) + ..writeByte(2) + ..write(obj.text); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CodeAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/example/lib/models/encode.dart b/example/lib/models/encode.dart new file mode 100644 index 0000000..d018974 --- /dev/null +++ b/example/lib/models/encode.dart @@ -0,0 +1,34 @@ +import 'dart:typed_data'; + +import 'package:flutter_zxing/flutter_zxing.dart'; +import 'package:hive_flutter/hive_flutter.dart'; + +part "encode.g.dart"; + +@HiveType(typeId: 1) +class Encode extends HiveObject { + @HiveField(0) + bool? isValid; + + @HiveField(1) + int? format; + + @HiveField(2) + String? text; + + @HiveField(3) + Uint8List? data; + + @HiveField(4) + int? length; + + Encode(); + + Encode.fromEncodeResult(EncodeResult result) { + isValid = result.isValidBool; + format = result.format; + text = result.textString; + data = result.bytes as Uint8List?; + length = result.length; + } +} diff --git a/example/lib/models/encode.g.dart b/example/lib/models/encode.g.dart new file mode 100644 index 0000000..f4f505f --- /dev/null +++ b/example/lib/models/encode.g.dart @@ -0,0 +1,52 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'encode.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class EncodeAdapter extends TypeAdapter { + @override + final int typeId = 1; + + @override + Encode read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return Encode() + ..isValid = fields[0] as bool? + ..format = fields[1] as int? + ..text = fields[2] as String? + ..data = fields[3] as Uint8List? + ..length = fields[4] as int?; + } + + @override + void write(BinaryWriter writer, Encode obj) { + writer + ..writeByte(5) + ..writeByte(0) + ..write(obj.isValid) + ..writeByte(1) + ..write(obj.format) + ..writeByte(2) + ..write(obj.text) + ..writeByte(3) + ..write(obj.data) + ..writeByte(4) + ..write(obj.length); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is EncodeAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/example/lib/models/models.dart b/example/lib/models/models.dart new file mode 100644 index 0000000..b10b351 --- /dev/null +++ b/example/lib/models/models.dart @@ -0,0 +1,2 @@ +export 'code.dart'; +export 'encode.dart'; diff --git a/example/lib/pages/history_page.dart b/example/lib/pages/history_page.dart index e01b8a5..fe60d67 100644 --- a/example/lib/pages/history_page.dart +++ b/example/lib/pages/history_page.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_zxing/flutter_zxing.dart'; +import 'package:flutter_zxing_example/models/code.dart'; +import 'package:flutter_zxing_example/utils/db_service.dart'; +import 'package:hive_flutter/hive_flutter.dart'; class HistoryPage extends StatefulWidget { const HistoryPage({ @@ -12,8 +14,6 @@ class HistoryPage extends StatefulWidget { } class _HistoryPageState extends State { - final _resultQueue = []; - @override Widget build(BuildContext context) { return Scaffold( @@ -25,42 +25,47 @@ class _HistoryPageState extends State { } _buildResultList() { - return _resultQueue.isEmpty - ? const Center( - child: Text( - 'No Results', - style: TextStyle(fontSize: 24), - )) - : ListView.builder( - itemCount: _resultQueue.length, - itemBuilder: (context, index) { - final result = _resultQueue[index]; - return ListTile( - title: Text(result.textString ?? ''), - subtitle: Text(result.formatString), - trailing: ButtonBar( - mainAxisSize: MainAxisSize.min, - children: [ - // Copy button - TextButton( - child: const Text('Copy'), - onPressed: () { - Clipboard.setData( - ClipboardData(text: result.textString)); - }, - ), - // Remove button - IconButton( - icon: const Icon(Icons.delete, color: Colors.red), - onPressed: () { - _resultQueue.removeAt(index); - setState(() {}); - }, - ), - ], - ), - ); - }, - ); + return ValueListenableBuilder>( + valueListenable: DbService.instance.getCodes().listenable(), + builder: (context, box, _) { + final results = box.values.toList().cast(); + return results.isEmpty + ? const Center( + child: Text( + 'No Results', + style: TextStyle(fontSize: 24), + )) + : ListView.builder( + itemCount: results.length, + itemBuilder: (context, index) { + final result = results[index]; + return ListTile( + title: Text(result.text ?? ''), + subtitle: Text(result.format.toString()), + trailing: ButtonBar( + mainAxisSize: MainAxisSize.min, + children: [ + // Copy button + TextButton( + child: const Text('Copy'), + onPressed: () { + Clipboard.setData( + ClipboardData(text: result.text)); + }, + ), + // Remove button + IconButton( + icon: const Icon(Icons.delete, color: Colors.red), + onPressed: () { + // DbService.instance.deleteCode(result); + // setState(() {}); + }, + ), + ], + ), + ); + }, + ); + }); } } diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index 73a3119..e70d5a8 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -19,14 +19,14 @@ class _HomePageState extends State { final creatorPage = const CreatorPage(); final historyPage = const HistoryPage(); final scannerPage = const ScannerPage(); - final notificationsPage = const ScannerPage(); + final helpPage = Container(); final settingsPage = const SettingsPage(); dynamic pages() => [ creatorPage, historyPage, scannerPage, - notificationsPage, + helpPage, settingsPage, ]; @@ -34,7 +34,7 @@ class _HomePageState extends State { const TabItem(icon: FontAwesomeIcons.barcode), const TabItem(icon: FontAwesomeIcons.clockRotateLeft), const TabItem(icon: Icons.qr_code_scanner), - const TabItem(icon: FontAwesomeIcons.solidBell), + const TabItem(icon: FontAwesomeIcons.circleQuestion), const TabItem(icon: FontAwesomeIcons.gear), ]; diff --git a/example/lib/pages/scanner_page.dart b/example/lib/pages/scanner_page.dart index f3eadd3..bca218d 100644 --- a/example/lib/pages/scanner_page.dart +++ b/example/lib/pages/scanner_page.dart @@ -2,6 +2,8 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_zxing/flutter_zxing.dart'; +import 'package:flutter_zxing_example/models/models.dart'; +import 'package:flutter_zxing_example/utils/db_service.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:image_picker/image_picker.dart'; import 'package:image/image.dart' as imglib; @@ -26,9 +28,7 @@ class _ScannerPageState extends State { ), body: ZxingReaderWidget( onScan: (result) async { - // _resultQueue.insert(0, result); - // await Future.delayed(const Duration(milliseconds: 500)); - // setState(() {}); + addCode(result); }, ), floatingActionButton: FloatingActionButton( @@ -49,7 +49,7 @@ class _ScannerPageState extends State { 0, ); if (result.isValidBool) { - debugPrint(result.textString); + addCode(result); } } } @@ -57,4 +57,23 @@ class _ScannerPageState extends State { ), ); } + + void addCode(CodeResult result) { + Code code = Code.fromCodeResult(result); + DbService.instance.addCode(code); + + // show snackbar + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Padding( + padding: const EdgeInsets.only(bottom: 30.0), + child: Text( + code.text ?? '', + textAlign: TextAlign.center, + ), + ), + ), + ); + } } diff --git a/example/lib/utils/db_service.dart b/example/lib/utils/db_service.dart new file mode 100644 index 0000000..03a4804 --- /dev/null +++ b/example/lib/utils/db_service.dart @@ -0,0 +1,50 @@ +import 'package:flutter_zxing_example/models/models.dart'; +import 'package:hive_flutter/hive_flutter.dart'; + +class DbService { + DbService._privateConstructor(); + + static final DbService instance = DbService._privateConstructor(); + + Future initializeApp() async { + await Hive.initFlutter(); + Hive.registerAdapter(CodeAdapter()); + + await Hive.openBox('codes'); + await Hive.openBox('encodes'); + + // Hive.box('codes').close(); + } + + Box getCodes() => Hive.box('codes'); + + Future deleteCodes() async { + var codes = getCodes(); + await codes.deleteAll(codes.keys); + return; + } + + Future addCode(Code value) async { + var codes = getCodes(); + if (!codes.values.contains(value)) { + return codes.add(value); + } + return; + } + + Box getEncodes() => Hive.box('encodes'); + + Future deleteEncodes() async { + var encodes = getEncodes(); + await encodes.deleteAll(encodes.keys); + return; + } + + Future addEncode(Encode value) async { + var encodes = getEncodes(); + if (!encodes.values.contains(value)) { + return encodes.add(value); + } + return; + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index d30b13f..ac7926c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -378,6 +378,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + hive: + dependency: "direct main" + description: + name: hive + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + hive_generator: + dependency: "direct dev" + description: + name: hive_generator + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" http: dependency: transitive description: @@ -782,6 +803,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.2" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" source_span: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index cd27528..35e0057 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,6 +18,8 @@ dependencies: flutter_zxing: path: ../ font_awesome_flutter: ^10.1.0 + hive: ^2.1.0 + hive_flutter: ^1.1.0 image_picker: ^0.8.5 nb_utils: ^4.5.0 path_provider: ^2.0.9 @@ -32,6 +34,7 @@ dev_dependencies: flutter_lints: ^1.0.0 flutter_test: sdk: flutter + hive_generator: mobx_codegen: flutter: diff --git a/ios/Classes/src/native_zxing.cpp b/ios/Classes/src/native_zxing.cpp index ca5c93e..5002977 100644 --- a/ios/Classes/src/native_zxing.cpp +++ b/ios/Classes/src/native_zxing.cpp @@ -99,7 +99,7 @@ extern "C" { long long start = get_now(); - struct EncodeResult result = {nullptr, 0, false, nullptr}; + struct EncodeResult result = {0, contents, Format(format), nullptr, 0, nullptr}; try { auto writer = MultiFormatWriter(BarcodeFormat(format)).setMargin(margin).setEccLevel(eccLevel); diff --git a/ios/Classes/src/native_zxing.h b/ios/Classes/src/native_zxing.h index 9a6b840..ff40b6a 100644 --- a/ios/Classes/src/native_zxing.h +++ b/ios/Classes/src/native_zxing.h @@ -37,9 +37,11 @@ extern "C" struct EncodeResult { + int isValid; + char *text; + enum Format format; const unsigned int *data; int length; - int isValid; char *error; }; diff --git a/lib/flutter_zxing.dart b/lib/flutter_zxing.dart index 89fc9c5..98ecfd9 100644 --- a/lib/flutter_zxing.dart +++ b/lib/flutter_zxing.dart @@ -75,20 +75,20 @@ extension Uint8ListBlobConversion on Uint8List { } } -extension Encode on EncodeResult { +extension CodeExt on CodeResult { bool get isValidBool => isValid == 1; - Uint32List get bytes => data.asTypedList(length); - String get errorMessage => error.cast().toDartString(); + String? get textString => + text == nullptr ? null : text.cast().toDartString(); + String get formatString => CodeFormat.formatName(format); } -extension Code on CodeResult { +extension EncodeExt on EncodeResult { bool get isValidBool => isValid == 1; String? get textString => text == nullptr ? null : text.cast().toDartString(); - - String get formatString { - return CodeFormat.formatName(format); - } + String get formatString => CodeFormat.formatName(format); + Uint32List get bytes => data.asTypedList(length); + String get errorMessage => error.cast().toDartString(); } extension CodeFormat on Format { diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index 019b164..1a67a93 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -216,13 +216,18 @@ class CodeResult extends ffi.Struct { } class EncodeResult extends ffi.Struct { - external ffi.Pointer data; + @ffi.Int32() + external int isValid; + + external ffi.Pointer text; @ffi.Int32() - external int length; + external int format; + + external ffi.Pointer data; @ffi.Int32() - external int isValid; + external int length; external ffi.Pointer error; }