diff --git a/README.md b/README.md
index 28e8f75..e3dfa42 100644
--- a/README.md
+++ b/README.md
@@ -20,14 +20,17 @@ A Flutter project with implementation of all firebase libraries for Android and
1. [Flutter: Publish App to PlayStore | Fully Explained Demo P5](https://youtu.be/qpruGmff5Fw)
+1. [Flutter Firebase MLKIT Tutorial](https://youtu.be/vT6gNFE0GBw)
+
### Screenshots
+
### NOTE
-* This project is still under development.
+- This project is still under development.
# Pull Requests
@@ -48,7 +51,7 @@ I welcome and encourage all pull requests. It usually will take me within 24-48
> If you found this project helpful or you learned something from the source code and want to thank me, consider buying me a cup of :coffee:
>
-> * [PayPal](https://www.paypal.me/imthepk/)
+> - [PayPal](https://www.paypal.me/imthepk/)
# License
diff --git a/android/app/build.gradle b/android/app/build.gradle
index d45c420..d007654 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -33,6 +33,7 @@ android {
targetSdkVersion 27
versionCode 1
versionName "1.0"
+ multiDexEnabled true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
diff --git a/ios/.symlinks/plugins/cloud_firestore b/ios/.symlinks/plugins/cloud_firestore
index 85ecd35..e2ca29c 120000
--- a/ios/.symlinks/plugins/cloud_firestore
+++ b/ios/.symlinks/plugins/cloud_firestore
@@ -1 +1 @@
-/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/cloud_firestore-0.4.0
\ No newline at end of file
+/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/cloud_firestore-0.7.3
\ No newline at end of file
diff --git a/ios/.symlinks/plugins/firebase_admob b/ios/.symlinks/plugins/firebase_admob
index 1879d14..fd55376 120000
--- a/ios/.symlinks/plugins/firebase_admob
+++ b/ios/.symlinks/plugins/firebase_admob
@@ -1 +1 @@
-/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/firebase_admob-0.5.2
\ No newline at end of file
+/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/firebase_admob-0.5.5
\ No newline at end of file
diff --git a/ios/.symlinks/plugins/firebase_auth b/ios/.symlinks/plugins/firebase_auth
index 0479435..1ac721d 120000
--- a/ios/.symlinks/plugins/firebase_auth
+++ b/ios/.symlinks/plugins/firebase_auth
@@ -1 +1 @@
-/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/firebase_auth-0.5.3
\ No newline at end of file
+/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/firebase_auth-0.5.11
\ No newline at end of file
diff --git a/ios/.symlinks/plugins/firebase_core b/ios/.symlinks/plugins/firebase_core
new file mode 120000
index 0000000..6a8c8fc
--- /dev/null
+++ b/ios/.symlinks/plugins/firebase_core
@@ -0,0 +1 @@
+/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/firebase_core-0.2.4
\ No newline at end of file
diff --git a/ios/.symlinks/plugins/google_sign_in b/ios/.symlinks/plugins/google_sign_in
index 7f6af15..d4dcc36 120000
--- a/ios/.symlinks/plugins/google_sign_in
+++ b/ios/.symlinks/plugins/google_sign_in
@@ -1 +1 @@
-/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/google_sign_in-3.0.0
\ No newline at end of file
+/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/google_sign_in-3.0.4
\ No newline at end of file
diff --git a/ios/.symlinks/plugins/image_picker b/ios/.symlinks/plugins/image_picker
new file mode 120000
index 0000000..2045e54
--- /dev/null
+++ b/ios/.symlinks/plugins/image_picker
@@ -0,0 +1 @@
+/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/image_picker-0.4.5
\ No newline at end of file
diff --git a/ios/.symlinks/plugins/mlkit b/ios/.symlinks/plugins/mlkit
new file mode 120000
index 0000000..1c801b1
--- /dev/null
+++ b/ios/.symlinks/plugins/mlkit
@@ -0,0 +1 @@
+/Users/pawankumar/.pub-cache/hosted/pub.dartlang.org/mlkit-0.4.1
\ No newline at end of file
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 123abea..f380fe4 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -28,6 +28,21 @@ PODS:
- Firebase/Firestore (5.0.1):
- Firebase/CoreOnly
- FirebaseFirestore (= 0.12.1)
+ - Firebase/MLVision (5.0.1):
+ - Firebase/CoreOnly
+ - FirebaseMLVision (= 0.9.0)
+ - Firebase/MLVisionBarcodeModel (5.0.1):
+ - Firebase/CoreOnly
+ - FirebaseMLVisionBarcodeModel (= 0.9.0)
+ - Firebase/MLVisionFaceModel (5.0.1):
+ - Firebase/CoreOnly
+ - FirebaseMLVisionFaceModel (= 0.9.0)
+ - Firebase/MLVisionLabelModel (5.0.1):
+ - Firebase/CoreOnly
+ - FirebaseMLVisionLabelModel (= 0.9.0)
+ - Firebase/MLVisionTextModel (5.0.1):
+ - Firebase/CoreOnly
+ - FirebaseMLVisionTextModel (= 0.9.0)
- firebase_admob (0.0.1):
- Firebase/AdMob
- Firebase/Core
@@ -39,6 +54,9 @@ PODS:
- Firebase/Auth
- Firebase/Core
- Flutter
+ - firebase_core (0.0.1):
+ - Firebase/Core
+ - Flutter
- FirebaseAnalytics (5.0.0):
- FirebaseCore (~> 5.0)
- FirebaseInstanceID (~> 3.0)
@@ -65,11 +83,44 @@ PODS:
- Protobuf (~> 3.1)
- FirebaseInstanceID (3.0.0):
- FirebaseCore (~> 5.0)
+ - FirebaseMLCommon (0.9.0)
+ - FirebaseMLVision (0.9.0):
+ - FirebaseCore (~> 5.0)
+ - FirebaseMLCommon (~> 0.9)
+ - GoogleAPIClientForREST/Core (~> 1.3)
+ - GoogleAPIClientForREST/Vision (~> 1.3)
+ - GoogleMobileVision/Detector (~> 1.3.0)
+ - FirebaseMLVisionBarcodeModel (0.9.0):
+ - GoogleMobileVision/BarcodeDetector (~> 1.3.0)
+ - FirebaseMLVisionFaceModel (0.9.0):
+ - GoogleMobileVision/FaceDetector (~> 1.3.0)
+ - FirebaseMLVisionLabelModel (0.9.0):
+ - GoogleMobileVision/LabelDetector (~> 1.3.0)
+ - FirebaseMLVisionTextModel (0.9.0):
+ - GoogleMobileVision/TextDetector (~> 1.3.0)
- Flutter (1.0.0)
- Google-Mobile-Ads-SDK (7.30.0)
- google_sign_in (0.0.1):
- Flutter
- GoogleSignIn (~> 4.0)
+ - GoogleAPIClientForREST/Core (1.3.4):
+ - GTMSessionFetcher (>= 1.1.7)
+ - GoogleAPIClientForREST/Vision (1.3.4):
+ - GoogleAPIClientForREST/Core
+ - GTMSessionFetcher (>= 1.1.7)
+ - GoogleMobileVision/BarcodeDetector (1.3.0):
+ - GoogleMobileVision/Detector (~> 1.3)
+ - GoogleMobileVision/Detector (1.3.0):
+ - GoogleToolboxForMac/Logger (~> 2.1)
+ - "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
+ - GTMSessionFetcher/Core (~> 1.1)
+ - Protobuf (~> 3.1)
+ - GoogleMobileVision/FaceDetector (1.3.0):
+ - GoogleMobileVision/Detector (~> 1.3)
+ - GoogleMobileVision/LabelDetector (1.3.0):
+ - GoogleMobileVision/Detector (~> 1.3)
+ - GoogleMobileVision/TextDetector (1.3.0):
+ - GoogleMobileVision/Detector (~> 1.3)
- GoogleSignIn (4.1.2):
- "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)"
- "GoogleToolboxForMac/NSString+URLArguments (~> 2.1)"
@@ -78,6 +129,8 @@ PODS:
- GoogleToolboxForMac/DebugUtils (2.1.4):
- GoogleToolboxForMac/Defines (= 2.1.4)
- GoogleToolboxForMac/Defines (2.1.4)
+ - GoogleToolboxForMac/Logger (2.1.4):
+ - GoogleToolboxForMac/Defines (= 2.1.4)
- "GoogleToolboxForMac/NSData+zlib (2.1.4)":
- GoogleToolboxForMac/Defines (= 2.1.4)
- "GoogleToolboxForMac/NSDictionary+URLArguments (2.1.4)":
@@ -111,7 +164,17 @@ PODS:
- GTMSessionFetcher/Core (1.1.15)
- GTMSessionFetcher/Full (1.1.15):
- GTMSessionFetcher/Core (= 1.1.15)
+ - image_picker (0.0.1):
+ - Flutter
- leveldb-library (1.20)
+ - mlkit (0.0.1):
+ - Firebase/Core
+ - Firebase/MLVision
+ - Firebase/MLVisionBarcodeModel
+ - Firebase/MLVisionFaceModel
+ - Firebase/MLVisionLabelModel
+ - Firebase/MLVisionTextModel
+ - Flutter
- nanopb (0.3.8):
- nanopb/decode (= 0.3.8)
- nanopb/encode (= 0.3.8)
@@ -124,8 +187,11 @@ DEPENDENCIES:
- firebase_admob (from `.symlinks/plugins/firebase_admob/ios`)
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
+ - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- Flutter (from `.symlinks/flutter/ios`)
- google_sign_in (from `.symlinks/plugins/google_sign_in/ios`)
+ - image_picker (from `.symlinks/plugins/image_picker/ios`)
+ - mlkit (from `.symlinks/plugins/mlkit/ios`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
@@ -137,7 +203,15 @@ SPEC REPOS:
- FirebaseDatabase
- FirebaseFirestore
- FirebaseInstanceID
+ - FirebaseMLCommon
+ - FirebaseMLVision
+ - FirebaseMLVisionBarcodeModel
+ - FirebaseMLVisionFaceModel
+ - FirebaseMLVisionLabelModel
+ - FirebaseMLVisionTextModel
- Google-Mobile-Ads-SDK
+ - GoogleAPIClientForREST
+ - GoogleMobileVision
- GoogleSignIn
- GoogleToolboxForMac
- gRPC
@@ -159,27 +233,42 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_analytics/ios"
firebase_auth:
:path: ".symlinks/plugins/firebase_auth/ios"
+ firebase_core:
+ :path: ".symlinks/plugins/firebase_core/ios"
Flutter:
:path: ".symlinks/flutter/ios"
google_sign_in:
:path: ".symlinks/plugins/google_sign_in/ios"
+ image_picker:
+ :path: ".symlinks/plugins/image_picker/ios"
+ mlkit:
+ :path: ".symlinks/plugins/mlkit/ios"
SPEC CHECKSUMS:
BoringSSL: 60dd24df4af296bf41d78e5841dbb95d75f88c0d
- cloud_firestore: 00886d0b271577dfadd33be97d6bb6095f49499d
+ cloud_firestore: a2d49d9c7219fce31033eb1a2ee953d7733c91d6
Firebase: d6861c2059d8c32d1e6dd8932e22ada346d90a3a
firebase_admob: b8cc46113ddf88a62fec340e9446607a5061a872
firebase_analytics: ad15e54404e21983408c8b0c24a9d11247be7f33
- firebase_auth: 499d5bfb249606a276f6bddf292a3b2d44139f75
+ firebase_auth: 27a5a77a032d557c18f8e223aaf8ec1368f6cdbc
+ firebase_core: c96aa8b2fcf7f5167d32f22034f502f9304952b8
FirebaseAnalytics: 19812b49fa5f283dd6b23edf8a14b5d477029ab8
FirebaseAuth: acbeef02fe7c3a26624e309849f3fe30c84115af
FirebaseCore: cafc814b2d84fc8733f09e653041cc2165332ad7
FirebaseDatabase: 697eb53e5b4fe7cd4fa8756c1f82a9fca011345f
FirebaseFirestore: f686b8e83f3cf8bbc37db6e98e01029a14f01f55
FirebaseInstanceID: 83e0040351565df711a5db3d8ebe5ea21aca998a
+ FirebaseMLCommon: 73a2f53c8b3e6cd18e73dd1be095c3192af26a5e
+ FirebaseMLVision: f9756d8376dd150fb17629eaa7bdc6baa8965677
+ FirebaseMLVisionBarcodeModel: dc1c6add14a3d69061eb6edacf90bb619f7aee52
+ FirebaseMLVisionFaceModel: 0b451ba1d40674dfc4a334ebd40f5f837fe283a1
+ FirebaseMLVisionLabelModel: ab16cb2bc13630ee13d9b0c6104b94165e86933c
+ FirebaseMLVisionTextModel: 8a9cb61296d4d15d60eb0c7c66a2a88790b34376
Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296
Google-Mobile-Ads-SDK: 7404f68120ae8682afeb5af001fbf4aad731c78e
- google_sign_in: 64523e9b42c475b01b726ff87e450425ea6d15bc
+ google_sign_in: cbeb57b96679823d14df6b2240b629b983a42d79
+ GoogleAPIClientForREST: f7951c455df271bc6259b3ddb4073d0026475ccf
+ GoogleMobileVision: 8c57c6b27b0964506cb7163049e67a38e279ea6f
GoogleSignIn: d9ef55b10f0aa401a5de2747f59b725e4b9732ac
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
gRPC: 9362451032695e2dfb7bafcd3740e3a27939e4ff
@@ -188,7 +277,9 @@ SPEC CHECKSUMS:
gRPC-RxLibrary: 1ed5314e8b38cd6e55c9bfa048387136ae925ce9
GTMOAuth2: c77fe325e4acd453837e72d91e3b5f13116857b2
GTMSessionFetcher: 5fa5b80fd20e439ef5f545fb2cb3ca6c6714caa2
+ image_picker: ee00aab0487cedc80a304085219503cc6d0f2e22
leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5
+ mlkit: 42aa3a7af1c9b080e52728dd7999668181e3ab3f
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
Protobuf: 8a9838fba8dae3389230e1b7f8c104aa32389c03
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index e4cff26..6ddddf8 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -280,12 +280,16 @@
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_ROOT}/GTMOAuth2/Source/Touch/GTMOAuth2ViewTouch.xib",
+ "${PODS_CONFIGURATION_BUILD_DIR}/GoogleMobileVision/GoogleMVFaceDetectorResources.bundle",
+ "${PODS_CONFIGURATION_BUILD_DIR}/GoogleMobileVision/GoogleMVTextDetectorResources.bundle",
"${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMOAuth2ViewTouch.nib",
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMVFaceDetectorResources.bundle",
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMVTextDetectorResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle",
);
diff --git a/lib/main.dart b/lib/main.dart
index faa84f3..0ce1814 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,10 +1,8 @@
-import 'package:flutfire/CrudApp/crud_sample.dart';
-import 'package:flutfire/QuotesApp/quotes.dart';
-import 'package:flutfire/WallpaperApp/wall_screen.dart';
+import 'package:flutfire/mlkit/ml_home.dart';
+
import 'package:flutter/material.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
-import 'package:flutfire/QuotesApp/backdrop.dart';
void main() => runApp(new MyApp());
@@ -24,7 +22,7 @@ class MyApp extends StatelessWidget {
),
navigatorObservers: [observer],
// home: new WallScreen(analytics: analytics, observer: observer),
- home: new CrudSample(),
+ home: new MLHome(),
);
}
}
diff --git a/lib/mlkit/ml_detail.dart b/lib/mlkit/ml_detail.dart
new file mode 100644
index 0000000..87cdf4f
--- /dev/null
+++ b/lib/mlkit/ml_detail.dart
@@ -0,0 +1,395 @@
+import 'package:flutfire/mlkit/ml_home.dart';
+import 'package:flutter/material.dart';
+import 'dart:io';
+import 'dart:async';
+import 'package:mlkit/mlkit.dart';
+
+class MLDetail extends StatefulWidget {
+ final File _file;
+ final String _scannerType;
+
+ MLDetail(this._file, this._scannerType);
+
+ @override
+ State createState() {
+ return _MLDetailState();
+ }
+}
+
+class _MLDetailState extends State {
+ FirebaseVisionTextDetector textDetector = FirebaseVisionTextDetector.instance;
+ FirebaseVisionBarcodeDetector barcodeDetector =
+ FirebaseVisionBarcodeDetector.instance;
+ FirebaseVisionLabelDetector labelDetector =
+ FirebaseVisionLabelDetector.instance;
+ FirebaseVisionFaceDetector faceDetector = FirebaseVisionFaceDetector.instance;
+ List _currentTextLabels = [];
+ List _currentBarcodeLabels = [];
+ List _currentLabelLabels = [];
+ List _currentFaceLabels = [];
+
+ Stream sub;
+ StreamSubscription subscription;
+
+ @override
+ void initState() {
+ super.initState();
+ sub = new Stream.empty();
+ subscription = sub.listen((_) => _getImageSize)..onDone(analyzeLabels);
+ }
+
+ void analyzeLabels() async {
+ try {
+ var currentLabels;
+ if (widget._scannerType == TEXT_SCANNER) {
+ currentLabels = await textDetector.detectFromPath(widget._file.path);
+ if (this.mounted) {
+ setState(() {
+ _currentTextLabels = currentLabels;
+ });
+ }
+ } else if (widget._scannerType == BARCODE_SCANNER) {
+ currentLabels = await barcodeDetector.detectFromPath(widget._file.path);
+ if (this.mounted) {
+ setState(() {
+ _currentBarcodeLabels = currentLabels;
+ });
+ }
+ } else if (widget._scannerType == LABEL_SCANNER) {
+ currentLabels = await labelDetector.detectFromPath(widget._file.path);
+ if (this.mounted) {
+ setState(() {
+ _currentLabelLabels = currentLabels;
+ });
+ }
+ } else if (widget._scannerType == FACE_SCANNER) {
+ currentLabels = await faceDetector.detectFromPath(widget._file.path);
+ if (this.mounted) {
+ setState(() {
+ _currentFaceLabels = currentLabels;
+ });
+ }
+ }
+ } catch (e) {
+ print("MyEx: " + e.toString());
+ }
+ }
+
+ @override
+ void dispose() {
+ // TODO: implement dispose
+ super.dispose();
+ subscription?.cancel();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ centerTitle: true,
+ title: Text(widget._scannerType),
+ ),
+ body: Column(
+ children: [
+ buildImage(context),
+ widget._scannerType == TEXT_SCANNER
+ ? buildTextList(_currentTextLabels)
+ : widget._scannerType == BARCODE_SCANNER
+ ? buildBarcodeList(_currentBarcodeLabels)
+ : widget._scannerType == FACE_SCANNER
+ ? buildBarcodeList(_currentFaceLabels)
+ : buildBarcodeList(_currentLabelLabels)
+ ],
+ ));
+ }
+
+ Widget buildImage(BuildContext context) {
+ return Expanded(
+ flex: 2,
+ child: Container(
+ decoration: BoxDecoration(color: Colors.black),
+ child: Center(
+ child: widget._file == null
+ ? Text('No Image')
+ : FutureBuilder(
+ future: _getImageSize(
+ Image.file(widget._file, fit: BoxFit.fitWidth)),
+ builder:
+ (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.hasData) {
+ return Container(
+ foregroundDecoration: (widget._scannerType ==
+ TEXT_SCANNER)
+ ? TextDetectDecoration(
+ _currentTextLabels, snapshot.data)
+ : (widget._scannerType == FACE_SCANNER)
+ ? FaceDetectDecoration(
+ _currentFaceLabels, snapshot.data)
+ : (widget._scannerType == BARCODE_SCANNER)
+ ? BarcodeDetectDecoration(
+ _currentBarcodeLabels,
+ snapshot.data)
+ : LabelDetectDecoration(
+ _currentLabelLabels, snapshot.data),
+ child:
+ Image.file(widget._file, fit: BoxFit.fitWidth));
+ } else {
+ return CircularProgressIndicator();
+ }
+ },
+ ),
+ )),
+ );
+ }
+
+ Widget buildBarcodeList(List barcodes) {
+ if (barcodes.length == 0) {
+ return Expanded(
+ flex: 1,
+ child: Center(
+ child: Text('Nothing detected',
+ style: Theme.of(context).textTheme.subhead),
+ ),
+ );
+ }
+ return Expanded(
+ flex: 1,
+ child: Container(
+ child: ListView.builder(
+ padding: const EdgeInsets.all(1.0),
+ itemCount: barcodes.length,
+ itemBuilder: (context, i) {
+ var text;
+
+ final barcode = barcodes[i];
+ switch (widget._scannerType) {
+ case BARCODE_SCANNER:
+ VisionBarcode res = barcode as VisionBarcode;
+ text = "Raw Value: ${res.rawValue}";
+ break;
+ case FACE_SCANNER:
+ VisionFace res = barcode as VisionFace;
+ text =
+ "Raw Value: ${res.smilingProbability},${res.trackingID}";
+ break;
+ case LABEL_SCANNER:
+ VisionLabel res = barcode as VisionLabel;
+ text = "Raw Value: ${res.label}";
+ break;
+ }
+
+ return _buildTextRow(text);
+ }),
+ ),
+ );
+ }
+
+ Widget buildTextList(List texts) {
+ if (texts.length == 0) {
+ return Expanded(
+ flex: 1,
+ child: Center(
+ child: Text('No text detected',
+ style: Theme.of(context).textTheme.subhead),
+ ));
+ }
+ return Expanded(
+ flex: 1,
+ child: Container(
+ child: ListView.builder(
+ padding: const EdgeInsets.all(1.0),
+ itemCount: texts.length,
+ itemBuilder: (context, i) {
+ return _buildTextRow(texts[i].text);
+ }),
+ ),
+ );
+ }
+
+ Widget _buildTextRow(text) {
+ return ListTile(
+ title: Text(
+ "$text",
+ ),
+ dense: true,
+ );
+ }
+
+ Future _getImageSize(Image image) {
+ Completer completer = Completer();
+ image.image.resolve(ImageConfiguration()).addListener(
+ (ImageInfo info, bool _) => completer.complete(
+ Size(info.image.width.toDouble(), info.image.height.toDouble())));
+ return completer.future;
+ }
+}
+
+/*
+ This code uses the example from azihsoyn/flutter_mlkit
+ https://github.com/azihsoyn/flutter_mlkit/blob/master/example/lib/main.dart
+*/
+
+class BarcodeDetectDecoration extends Decoration {
+ final Size _originalImageSize;
+ final List _barcodes;
+
+ BarcodeDetectDecoration(List barcodes, Size originalImageSize)
+ : _barcodes = barcodes,
+ _originalImageSize = originalImageSize;
+
+ @override
+ BoxPainter createBoxPainter([VoidCallback onChanged]) {
+ return _BarcodeDetectPainter(_barcodes, _originalImageSize);
+ }
+}
+
+class _BarcodeDetectPainter extends BoxPainter {
+ final List _barcodes;
+ final Size _originalImageSize;
+ _BarcodeDetectPainter(barcodes, originalImageSize)
+ : _barcodes = barcodes,
+ _originalImageSize = originalImageSize;
+
+ @override
+ void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
+ final paint = Paint()
+ ..strokeWidth = 2.0
+ ..color = Colors.red
+ ..style = PaintingStyle.stroke;
+
+ final _heightRatio = _originalImageSize.height / configuration.size.height;
+ final _widthRatio = _originalImageSize.width / configuration.size.width;
+ for (var barcode in _barcodes) {
+ final _rect = Rect.fromLTRB(
+ offset.dx + barcode.rect.left / _widthRatio,
+ offset.dy + barcode.rect.top / _heightRatio,
+ offset.dx + barcode.rect.right / _widthRatio,
+ offset.dy + barcode.rect.bottom / _heightRatio);
+ canvas.drawRect(_rect, paint);
+ }
+ canvas.restore();
+ }
+}
+
+class TextDetectDecoration extends Decoration {
+ final Size _originalImageSize;
+ final List _texts;
+ TextDetectDecoration(List texts, Size originalImageSize)
+ : _texts = texts,
+ _originalImageSize = originalImageSize;
+
+ @override
+ BoxPainter createBoxPainter([VoidCallback onChanged]) {
+ return _TextDetectPainter(_texts, _originalImageSize);
+ }
+}
+
+class _TextDetectPainter extends BoxPainter {
+ final List _texts;
+ final Size _originalImageSize;
+ _TextDetectPainter(texts, originalImageSize)
+ : _texts = texts,
+ _originalImageSize = originalImageSize;
+
+ @override
+ void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
+ final paint = Paint()
+ ..strokeWidth = 2.0
+ ..color = Colors.red
+ ..style = PaintingStyle.stroke;
+
+ final _heightRatio = _originalImageSize.height / configuration.size.height;
+ final _widthRatio = _originalImageSize.width / configuration.size.width;
+ for (var text in _texts) {
+ final _rect = Rect.fromLTRB(
+ offset.dx + text.rect.left / _widthRatio,
+ offset.dy + text.rect.top / _heightRatio,
+ offset.dx + text.rect.right / _widthRatio,
+ offset.dy + text.rect.bottom / _heightRatio);
+ canvas.drawRect(_rect, paint);
+ }
+ canvas.restore();
+ }
+}
+
+class FaceDetectDecoration extends Decoration {
+ final Size _originalImageSize;
+ final List _faces;
+ FaceDetectDecoration(List faces, Size originalImageSize)
+ : _faces = faces,
+ _originalImageSize = originalImageSize;
+
+ @override
+ BoxPainter createBoxPainter([VoidCallback onChanged]) {
+ return _FaceDetectPainter(_faces, _originalImageSize);
+ }
+}
+
+class _FaceDetectPainter extends BoxPainter {
+ final List _faces;
+ final Size _originalImageSize;
+ _FaceDetectPainter(faces, originalImageSize)
+ : _faces = faces,
+ _originalImageSize = originalImageSize;
+
+ @override
+ void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
+ final paint = Paint()
+ ..strokeWidth = 2.0
+ ..color = Colors.red
+ ..style = PaintingStyle.stroke;
+
+ final _heightRatio = _originalImageSize.height / configuration.size.height;
+ final _widthRatio = _originalImageSize.width / configuration.size.width;
+ for (var face in _faces) {
+ final _rect = Rect.fromLTRB(
+ offset.dx + face.rect.left / _widthRatio,
+ offset.dy + face.rect.top / _heightRatio,
+ offset.dx + face.rect.right / _widthRatio,
+ offset.dy + face.rect.bottom / _heightRatio);
+ canvas.drawRect(_rect, paint);
+ }
+ canvas.restore();
+ }
+}
+
+class LabelDetectDecoration extends Decoration {
+ final Size _originalImageSize;
+ final List _labels;
+ LabelDetectDecoration(List labels, Size originalImageSize)
+ : _labels = labels,
+ _originalImageSize = originalImageSize;
+
+ @override
+ BoxPainter createBoxPainter([VoidCallback onChanged]) {
+ return _LabelDetectPainter(_labels, _originalImageSize);
+ }
+}
+
+class _LabelDetectPainter extends BoxPainter {
+ final List _labels;
+ final Size _originalImageSize;
+ _LabelDetectPainter(labels, originalImageSize)
+ : _labels = labels,
+ _originalImageSize = originalImageSize;
+
+ @override
+ void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
+ final paint = Paint()
+ ..strokeWidth = 2.0
+ ..color = Colors.red
+ ..style = PaintingStyle.stroke;
+
+ final _heightRatio = _originalImageSize.height / configuration.size.height;
+ final _widthRatio = _originalImageSize.width / configuration.size.width;
+ for (var label in _labels) {
+ final _rect = Rect.fromLTRB(
+ offset.dx + label.rect.left / _widthRatio,
+ offset.dy + label.rect.top / _heightRatio,
+ offset.dx + label.rect.right / _widthRatio,
+ offset.dy + label.rect.bottom / _heightRatio);
+ canvas.drawRect(_rect, paint);
+ }
+ canvas.restore();
+ }
+}
diff --git a/lib/mlkit/ml_home.dart b/lib/mlkit/ml_home.dart
new file mode 100644
index 0000000..04373e8
--- /dev/null
+++ b/lib/mlkit/ml_home.dart
@@ -0,0 +1,182 @@
+import 'package:flutfire/mlkit/ml_detail.dart';
+import 'package:flutter/material.dart';
+import 'package:image_picker/image_picker.dart';
+import 'dart:io';
+
+const String TEXT_SCANNER = 'TEXT_SCANNER';
+const String BARCODE_SCANNER = 'BARCODE_SCANNER';
+const String LABEL_SCANNER = 'LABEL_SCANNER';
+const String FACE_SCANNER = 'FACE_SCANNER';
+
+class MLHome extends StatefulWidget {
+ MLHome({Key key}) : super(key: key);
+
+ @override
+ State createState() => _MLHomeState();
+}
+
+class _MLHomeState extends State {
+ static const String CAMERA_SOURCE = 'CAMERA_SOURCE';
+ static const String GALLERY_SOURCE = 'GALLERY_SOURCE';
+
+ final GlobalKey _scaffoldKey = GlobalKey();
+
+ File _file;
+ String _selectedScanner = TEXT_SCANNER;
+
+ @override
+ Widget build(BuildContext context) {
+ final columns = List();
+ columns.add(buildRowTitle(context, 'Select Scanner Type'));
+ columns.add(buildSelectScannerRowWidget(context));
+ columns.add(buildRowTitle(context, 'Pick Image'));
+ columns.add(buildSelectImageRowWidget(context));
+
+ return Scaffold(
+ key: _scaffoldKey,
+ appBar: AppBar(
+ centerTitle: true,
+ title: Text('MLKit Demo'),
+ ),
+ body: SingleChildScrollView(
+ child: Column(
+ children: columns,
+ ),
+ ));
+ }
+
+ Widget buildRowTitle(BuildContext context, String title) {
+ return Center(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0),
+ child: Text(
+ title,
+ style: Theme.of(context).textTheme.headline,
+ ),
+ ));
+ }
+
+ Widget buildSelectImageRowWidget(BuildContext context) {
+ return Row(
+ children: [
+ Expanded(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 8.0),
+ child: RaisedButton(
+ color: Colors.blue,
+ textColor: Colors.white,
+ splashColor: Colors.blueGrey,
+ onPressed: () {
+ onPickImageSelected(CAMERA_SOURCE);
+ },
+ child: const Text('Camera')),
+ )),
+ Expanded(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 8.0),
+ child: RaisedButton(
+ color: Colors.blue,
+ textColor: Colors.white,
+ splashColor: Colors.blueGrey,
+ onPressed: () {
+ onPickImageSelected(GALLERY_SOURCE);
+ },
+ child: const Text('Gallery')),
+ ))
+ ],
+ );
+ }
+
+ Widget buildSelectScannerRowWidget(BuildContext context) {
+ return Wrap(
+ children: [
+ RadioListTile(
+ title: Text('Text Recognition'),
+ groupValue: _selectedScanner,
+ value: TEXT_SCANNER,
+ onChanged: onScannerSelected,
+ ),
+ RadioListTile(
+ title: Text('Barcode Scanner'),
+ groupValue: _selectedScanner,
+ value: BARCODE_SCANNER,
+ onChanged: onScannerSelected,
+ ),
+ RadioListTile(
+ title: Text('Label Scanner'),
+ groupValue: _selectedScanner,
+ value: LABEL_SCANNER,
+ onChanged: onScannerSelected,
+ ),
+ RadioListTile(
+ title: Text('Face Scanner'),
+ groupValue: _selectedScanner,
+ value: FACE_SCANNER,
+ onChanged: onScannerSelected,
+ )
+ ],
+ );
+ }
+
+ Widget buildImageRow(BuildContext context, File file) {
+ return SizedBox(
+ height: 500.0,
+ child: Image.file(
+ file,
+ fit: BoxFit.fitWidth,
+ ));
+ }
+
+ Widget buildDeleteRow(BuildContext context) {
+ return Center(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
+ child: RaisedButton(
+ color: Colors.red,
+ textColor: Colors.white,
+ splashColor: Colors.blueGrey,
+ onPressed: () {
+ setState(() {
+ _file = null;
+ });
+ ;
+ },
+ child: const Text('Delete Image')),
+ ),
+ );
+ }
+
+ void onScannerSelected(String scanner) {
+ setState(() {
+ _selectedScanner = scanner;
+ });
+ }
+
+ void onPickImageSelected(String source) async {
+ var imageSource;
+ if (source == CAMERA_SOURCE) {
+ imageSource = ImageSource.camera;
+ } else {
+ imageSource = ImageSource.gallery;
+ }
+
+ final scaffold = _scaffoldKey.currentState;
+
+ try {
+ final file = await ImagePicker.pickImage(source: imageSource);
+ if (file == null) {
+ throw Exception('File is not available');
+ }
+
+ Navigator.push(
+ context,
+ new MaterialPageRoute(
+ builder: (context) => MLDetail(file, _selectedScanner)),
+ );
+ } catch (e) {
+ scaffold.showSnackBar(SnackBar(
+ content: Text(e.toString()),
+ ));
+ }
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
index b3be2eb..35c11d0 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -172,6 +172,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
+ image_picker:
+ dependency: "direct main"
+ description:
+ name: image_picker
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.5"
io:
dependency: transitive
description:
@@ -228,6 +235,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.6+1"
+ mlkit:
+ dependency: "direct main"
+ description:
+ name: mlkit
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.1"
multi_server_socket:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 1d791d5..944a468 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -14,6 +14,8 @@ dependencies:
flutter_staggered_grid_view:
firebase_admob: ^0.5.2
firebase_analytics: ^0.2.3
+ mlkit:
+ image_picker:
dev_dependencies: