diff --git a/melos.yaml b/melos.yaml index 7669dab..0b210e7 100644 --- a/melos.yaml +++ b/melos.yaml @@ -22,13 +22,13 @@ scripts: # run: melos run publish:dart && melos run publish:flutter # description: Run publish for all packages. - prep:dart: - run: melos run build && melos run meta && melos run markdown && melos run analyze && melos run format - description: Run prep steps dart. + prep: + run: melos run build && melos run meta && melos run markdown && melos run format && melos run analyze + description: Run prep steps for dart and flutter. - prep:flutter: - run: melos run build && melos run meta && melos run markdown && melos run analyze && melos run format - description: Run prep steps Flutter. + # prep:flutter: + # run: melos run build && melos run meta && melos run markdown && melos run analyze && melos run format + # description: Run prep steps Flutter. ## TODO: Add commit steps for Dart and Flutter packages. @@ -66,7 +66,7 @@ scripts: depends-on: build_runner analyze: - exec: dart analyze --fatal-infos . + exec: dart analyze . description: Run `dart analyze` in all packages. format: diff --git a/packages/google_vision/pubspec.yaml b/packages/google_vision/pubspec.yaml index caf675c..431f6a1 100644 --- a/packages/google_vision/pubspec.yaml +++ b/packages/google_vision/pubspec.yaml @@ -1,6 +1,6 @@ name: google_vision description: Allows you to add Google Visions image labeling, face, logo, and landmark detection, OCR, and detection of explicit content, into cross platform applications. -version: 1.3.0+3 +version: 1.3.0+4 repository: https://github.com/faithoflifedev/google_vision homepage: https://github.com/faithoflifedev/google_vision/tree/main/packages/google_vision diff --git a/packages/google_vision/tool/README.md b/packages/google_vision/tool/README.md index 5c93726..9a34b12 100644 --- a/packages/google_vision/tool/README.md +++ b/packages/google_vision/tool/README.md @@ -1,5 +1,9 @@ # Google Vision Images REST API Client + +[![pub package](https://img.shields.io/pub/v/google_vision.svg)](https://pub.dartlang.org/packages/google_vision) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + Native [Dart](https://dart.dev/) package that integrates Google Vision features, including image labeling, face, logo, and landmark detection, optical character recognition (OCR), and detection of explicit content, into applications. - [Google Vision Images REST API Client](#google-vision-images-rest-api-client) @@ -21,7 +25,6 @@ Native [Dart](https://dart.dev/) package that integrates Google Vision features, ## Project Status -[![pub package](https://img.shields.io/pub/v/google_vision.svg)](https://pub.dartlang.org/packages/google_vision) [![Build Status](https://github.com/faithoflifedev/google_vision/workflows/Dart/badge.svg)](https://github.com/faithoflifedev/google_vision/actions) [![github last commit](https://shields.io/github/last-commit/faithoflifedev/google_vision)](https://shields.io/github/last-commit/faithoflifedev/google_vision) [![github build](https://img.shields.io/github/actions/workflow/status/faithoflifedev/google_vision_workspace/dart.yaml?branch=main)](https://shields.io/github/workflow/status/faithoflifedev/google_vision/Dart) [![github issues](https://shields.io/github/issues/faithoflifedev/google_vision)](https://shields.io/github/issues/faithoflifedev/google_vision) diff --git a/packages/google_vision_flutter/example/assets/dj.jpg b/packages/google_vision_flutter/example/assets/dj.jpg new file mode 100644 index 0000000..f50e8a6 Binary files /dev/null and b/packages/google_vision_flutter/example/assets/dj.jpg differ diff --git a/packages/google_vision_flutter/example/lib/main.dart b/packages/google_vision_flutter/example/lib/main.dart index 5b85c60..6608406 100644 --- a/packages/google_vision_flutter/example/lib/main.dart +++ b/packages/google_vision_flutter/example/lib/main.dart @@ -9,6 +9,7 @@ import 'label_detection.dart'; import 'landmark_detection.dart'; import 'logo_detection.dart'; import 'multiple_detections.dart'; +import 'multiple_face_detections.dart'; import 'object_localization.dart'; import 'safe_search_detection.dart'; import 'text_detection.dart'; @@ -66,6 +67,9 @@ class MyApp extends StatelessWidget { '/webdetection': (context) => const WebDetection( title: 'Document Text Detection from PDF', ), + '/multipleface': (context) => const MultipleFaceDetection( + title: 'Multiple Image Face Detection', + ), }, ); } @@ -140,6 +144,10 @@ class MenuScreen extends StatelessWidget { child: const Text('Web Detection'), onPressed: () => Navigator.pushNamed(context, '/webdetection'), ), + ElevatedButton( + child: const Text('Multiple Image Face Detection'), + onPressed: () => Navigator.pushNamed(context, '/multipleface'), + ), const SizedBox( height: 30, child: Text('File Functions'), diff --git a/packages/google_vision_flutter/example/lib/multiple_detections.dart b/packages/google_vision_flutter/example/lib/multiple_detections.dart index 508bf80..cb0bc4c 100644 --- a/packages/google_vision_flutter/example/lib/multiple_detections.dart +++ b/packages/google_vision_flutter/example/lib/multiple_detections.dart @@ -11,8 +11,10 @@ class MultipleDetections extends StatefulWidget { } class _MyHomePageState extends State { + static const assetName = 'assets/young-man-smiling.jpg'; + final _processImage = Image.asset( - 'assets/young-man-smiling.jpg', // 'assets/logo.png', // 'assets/young-man-smiling.jpg' + assetName, fit: BoxFit.fitWidth, width: 300, ); @@ -34,7 +36,7 @@ class _MyHomePageState extends State { children: [ const Padding( padding: EdgeInsets.all(8.0), - child: Text('assets/young-man-smiling'), + child: Text(assetName), ), const Padding( padding: EdgeInsets.all(8.0), @@ -62,13 +64,11 @@ class _MyHomePageState extends State { features: [ Feature( maxResults: 10, - type: AnnotationType - .faceDetection, // 'LOGO_DETECTION', // 'FACE_DETECTION' + type: AnnotationType.faceDetection, ), Feature( maxResults: 10, - type: AnnotationType - .objectLocalization, // 'LOGO_DETECTION', // 'FACE_DETECTION' + type: AnnotationType.objectLocalization, ), ], builder: ( diff --git a/packages/google_vision_flutter/example/lib/multiple_face_detections.dart b/packages/google_vision_flutter/example/lib/multiple_face_detections.dart new file mode 100644 index 0000000..a937613 --- /dev/null +++ b/packages/google_vision_flutter/example/lib/multiple_face_detections.dart @@ -0,0 +1,220 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:google_vision/google_vision.dart' as gv; +import 'package:google_vision_flutter/google_vision_flutter.dart'; + +class MultipleFaceDetection extends StatefulWidget { + const MultipleFaceDetection({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + FutureOr? _googleVision; + + static const assetName1 = 'assets/young-man-smiling.jpg'; + + static const assetName2 = 'assets/dj.jpg'; + + final _processImage1 = Image.asset( + assetName1, + fit: BoxFit.fitWidth, + height: 300, + ); + + final _processImage2 = Image.asset( + assetName2, + fit: BoxFit.fitWidth, + height: 300, + ); + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _getGoogleVision(); + }); + } + + FutureOr _getGoogleVision() async { + _googleVision ??= await GoogleVision.withAsset( + 'assets/service_credentials.json', + ); + + setState(() {}); + } + + @override + Widget build(BuildContext context) => SafeArea( + child: Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text(widget.title), + ), + body: SingleChildScrollView( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column(children: [ + const Padding( + padding: EdgeInsets.all(8.0), + child: Text(assetName1), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: _processImage1, + ), + ]), + Column(children: [ + const Padding( + padding: EdgeInsets.all(8.0), + child: Text(assetName2), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: _processImage2, + ), + ]), + ], + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'Processed images will appear below:', + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: _googleVision == null + ? const CircularProgressIndicator() + : GoogleVisionImageBuilder.faceDetection( + googleVision: _googleVision!, + imageProvider: _processImage1.image, + builder: ( + BuildContext context, + List? faceAnnotations, + ) => + CustomPaint( + foregroundPainter: AnnotationPainter( + faceAnnotations: faceAnnotations, + ), + child: Image(image: _processImage1.image), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: _googleVision == null + ? const CircularProgressIndicator() + : GoogleVisionImageBuilder.faceDetection( + googleVision: Future.value(_googleVision), + imageProvider: _processImage2.image, + builder: ( + BuildContext context, + List? faceAnnotations, + ) => + CustomPaint( + foregroundPainter: AnnotationPainter( + faceAnnotations: faceAnnotations, + ), + child: Image(image: _processImage2.image), + ), + ), + ) + ], + ), + ), + ), + ), + ); +} + +class AnnotationPainter extends CustomPainter { + final List? faceAnnotations; + + AnnotationPainter({ + required this.faceAnnotations, + }); + + @override + void paint( + Canvas canvas, + Size size, + ) { + // face detection + for (var faceAnnotation in faceAnnotations!) { + drawAnnotationsRect( + vertices: faceAnnotation.boundingPoly.vertices, + canvas: canvas, + ); + + drawString( + text: 'Face - ${(faceAnnotation.detectionConfidence * 100).toInt()}%', + offset: faceAnnotation.boundingPoly.vertices.first.toOffset(), + canvas: canvas, + size: size, + ); + } + } + + void drawString({ + required String text, + required Offset offset, + required Canvas canvas, + required Size size, + Color? color, + }) { + color ??= Colors.red.shade900; + + final tp = TextPainter( + text: TextSpan( + text: text, + style: TextStyle(color: color), + ), + textAlign: TextAlign.left, + textDirection: TextDirection.ltr, + ); + + tp.layout(); + + tp.paint(canvas, offset); + } + + void drawAnnotationsRect({ + required List vertices, + required Canvas canvas, + Color? color, + double strokeWidth = 1, + }) { + color ??= Colors.red.shade400; + + final paint = Paint(); + + paint.style = PaintingStyle.stroke; + paint.strokeWidth = strokeWidth; + paint.color = color; + + canvas.drawRect( + Rect.fromPoints( + vertices.first.toOffset(), + vertices[2].toOffset(), + ), + paint, + ); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} diff --git a/packages/google_vision_flutter/example/pubspec.yaml b/packages/google_vision_flutter/example/pubspec.yaml index 7cce803..78fdc39 100644 --- a/packages/google_vision_flutter/example/pubspec.yaml +++ b/packages/google_vision_flutter/example/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: color: ^3.0.0 + google_vision: ^1.3.0+2 google_vision_flutter: path: ../ syncfusion_flutter_pdfviewer: ^26.2.11 @@ -28,6 +29,7 @@ flutter: - assets/allswell.pdf - assets/census2010.jpg - assets/cn_tower.jpg + - assets/dj.jpg # https://commons.wikimedia.org/wiki/Category:Images#/media/File:DJ_Kelblizz.jpg - assets/google_logo.jpg - assets/service_credentials.json - assets/setagaya_small.jpg diff --git a/packages/google_vision_flutter/lib/google_vision_flutter.dart b/packages/google_vision_flutter/lib/google_vision_flutter.dart index bcb147d..8e08289 100644 --- a/packages/google_vision_flutter/lib/google_vision_flutter.dart +++ b/packages/google_vision_flutter/lib/google_vision_flutter.dart @@ -6,7 +6,6 @@ export 'src/google_vision.dart'; export 'src/google_vision_builder_base.dart'; export 'src/google_vision_builder.dart'; export 'src/google_vision_file_builder.dart'; -export 'src/google_vision_future_resolver.dart'; export 'src/google_vision_image_builder.dart'; export 'src/image_detail.dart'; export 'src/input_config.dart'; diff --git a/packages/google_vision_flutter/lib/src/google_vision.dart b/packages/google_vision_flutter/lib/src/google_vision.dart index d2d2155..b35298a 100644 --- a/packages/google_vision_flutter/lib/src/google_vision.dart +++ b/packages/google_vision_flutter/lib/src/google_vision.dart @@ -12,6 +12,6 @@ class GoogleVision extends gv.GoogleVision { } /// Create a new instance of [GoogleVision] with the given [apiKey]. - static Future withApiKey(String apiKey) async => + static gv.GoogleVision withApiKey(String apiKey) => gv.GoogleVision.withApiKey(apiKey); } diff --git a/packages/google_vision_flutter/lib/src/google_vision_builder.dart b/packages/google_vision_flutter/lib/src/google_vision_builder.dart index 7dc07f2..da81da4 100644 --- a/packages/google_vision_flutter/lib/src/google_vision_builder.dart +++ b/packages/google_vision_flutter/lib/src/google_vision_builder.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_image_converter/flutter_image_converter.dart'; import 'package:google_vision/google_vision.dart' as gv; import 'package:google_vision_flutter/google_vision_flutter.dart' hide GoogleVision, JsonImage, InputConfig; @@ -16,7 +17,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder; /// Creates a new instance of [GoogleVisionBuilder]. - GoogleVisionBuilder({ + const GoogleVisionBuilder({ super.key, required super.googleVision, required this.imageProvider, @@ -24,6 +25,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { required this.builder, super.onError, super.onLoading, + super.parent, int maxResults = 10, }); @@ -38,6 +40,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -66,6 +69,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -94,6 +98,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -122,6 +127,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -150,6 +156,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -178,6 +185,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -206,6 +214,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -234,6 +243,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -262,6 +272,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -290,6 +301,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -318,6 +330,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -346,6 +359,7 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { ) builder, Widget Function(Object)? onError, Widget Function()? onLoading, + String? parent, int maxResults = 10, }) => GoogleVisionBuilder( @@ -363,46 +377,29 @@ class GoogleVisionBuilder extends GoogleVisionBuilderBase { AnnotationType.webDetection, maxResults), ); - /// Builds the widget. - @override - Widget build(BuildContext context) { - final googleVisionFutureResolver = GoogleVisionFutureResolver( - googleVisionFuture: googleVision, - imageProvider: imageProvider, - ); - - return FutureBuilder( - future: googleVisionFutureResolver.resolve( - (byteBuffer) => AnnotationRequests( - requests: [ - AnnotationRequest( - jsonImage: gv.JsonImage(byteBuffer: byteBuffer), - features: features, - ) - ], - ), - ), - builder: ( - context, - snapshot, - ) { - Widget? widget = onLoading == null - ? const Center(child: CircularProgressIndicator()) - : onLoading!(); + Future _annotatedResponses() async { + final googleVision = await this.googleVision; - if (snapshot.hasData) { - widget = builder( - context, - snapshot, - ); - } else if (snapshot.hasError) { - widget = onError == null - ? onError!(snapshot.error!) - : Center(child: Text('${snapshot.error}')); - } + final byteData = await imageProvider.pngByteData; - return widget; - }, + return googleVision.annotate( + requests: AnnotationRequests( + requests: [ + AnnotationRequest( + jsonImage: gv.JsonImage(byteBuffer: byteData.buffer), + features: features, + ) + ], + ), + parent: parent, ); } + + /// Builds the widget. + @override + Widget build(BuildContext context) => getBuild( + context, + _annotatedResponses(), + builder, + ); } diff --git a/packages/google_vision_flutter/lib/src/google_vision_builder_base.dart b/packages/google_vision_flutter/lib/src/google_vision_builder_base.dart index 36d7a4c..143305b 100644 --- a/packages/google_vision_flutter/lib/src/google_vision_builder_base.dart +++ b/packages/google_vision_flutter/lib/src/google_vision_builder_base.dart @@ -1,9 +1,11 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:google_vision/google_vision.dart' as gv; import 'package:google_vision_flutter/google_vision_flutter.dart'; abstract class GoogleVisionBuilderBase extends StatelessWidget { - final Future googleVision; + final FutureOr googleVision; /// The error builder for the [GoogleVisionFileBuilder]. final Widget Function(Object error)? onError; @@ -14,26 +16,26 @@ abstract class GoogleVisionBuilderBase extends StatelessWidget { /// The list of [Feature] to be used for the image annotation. final List features; - static GoogleVisionFutureResolver? _googleVisionFutureResolver; - - GoogleVisionFutureResolver get googleVisionResolver => - _googleVisionFutureResolver == null - ? throw Exception( - 'GoogleVisionFutureResolver has not been initialized properly.') - : _googleVisionFutureResolver!; + /// Optional. Target project and location to make a call. + /// + /// Format: projects/{project-id}/locations/{location-id}. + /// + /// If no parent is specified, a region will be chosen automatically. + /// + /// Supported location-ids: us: USA country only, eu: The European Union. + /// + /// Example: projects/project-A/locations/eu. + final String? parent; /// Creates a new instance of [GoogleVisionBuilderBase]. - GoogleVisionBuilderBase({ + const GoogleVisionBuilderBase({ super.key, this.onError, this.onLoading, required this.googleVision, required this.features, - }) { - _googleVisionFutureResolver = GoogleVisionFutureResolver( - googleVisionFuture: googleVision, - ); - } + this.parent, + }); /// Gets the list of [Feature] for the specified [AnnotationType]. static List getFeatures( @@ -44,4 +46,37 @@ abstract class GoogleVisionBuilderBase extends StatelessWidget { type: annotationType, ) ]; + + Widget getBuild( + BuildContext context, + Future future, + Widget Function( + BuildContext context, + AsyncSnapshot snapshot, + ) builder, + ) => + FutureBuilder( + future: future, + builder: ( + context, + snapshot, + ) { + Widget? widget = onLoading == null + ? const Center(child: CircularProgressIndicator()) + : onLoading!(); + + if (snapshot.hasData) { + widget = builder( + context, + snapshot, + ); + } else if (snapshot.hasError) { + widget = onError == null + ? onError!(snapshot.error!) + : Center(child: Text('${snapshot.error}')); + } + + return widget; + }, + ); } diff --git a/packages/google_vision_flutter/lib/src/google_vision_file_builder.dart b/packages/google_vision_flutter/lib/src/google_vision_file_builder.dart index 266061d..a5a85cd 100644 --- a/packages/google_vision_flutter/lib/src/google_vision_file_builder.dart +++ b/packages/google_vision_flutter/lib/src/google_vision_file_builder.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:google_vision/google_vision.dart' as gv; import 'package:google_vision_flutter/google_vision_flutter.dart'; @@ -14,7 +16,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { ) builder; /// Creates a new instance of [GoogleVisionFileBuilder]. - GoogleVisionFileBuilder({ + const GoogleVisionFileBuilder({ super.key, required super.googleVision, required this.inputConfig, @@ -22,13 +24,14 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { required this.builder, super.onError, super.onLoading, + super.parent, int maxResults = 10, }); /// Creates a new instance of [GoogleVisionFileBuilder] for crop hint detections. factory GoogleVisionFileBuilder.cropHints({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -56,7 +59,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for document text detections. factory GoogleVisionFileBuilder.documentTextDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -84,7 +87,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for face detections. factory GoogleVisionFileBuilder.faceDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -112,7 +115,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for image properties detection. factory GoogleVisionFileBuilder.imageProperties({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -140,7 +143,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for label detections. factory GoogleVisionFileBuilder.labelDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -168,7 +171,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for landmark detections. factory GoogleVisionFileBuilder.landmarkDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -196,7 +199,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for logo detections. factory GoogleVisionFileBuilder.logoDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -224,7 +227,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for object localization detections. factory GoogleVisionFileBuilder.objectLocalization({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -252,7 +255,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for product search detections. factory GoogleVisionFileBuilder.productSearch({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -280,7 +283,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for safe search detections. factory GoogleVisionFileBuilder.safeSearchDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -308,7 +311,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for text detections. factory GoogleVisionFileBuilder.textDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -338,7 +341,7 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionFileBuilder] for web detections. factory GoogleVisionFileBuilder.webDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required Future inputConfig, required Widget Function( BuildContext, @@ -363,40 +366,27 @@ class GoogleVisionFileBuilder extends GoogleVisionBuilderBase { AnnotationType.webDetection, maxResults), ); - /// Builds the widget. - @override - Widget build(BuildContext context) { - final googleVisionFutureResolver = GoogleVisionFutureResolver( - googleVisionFuture: googleVision, - inputConfigFuture: inputConfig, - ); - - return FutureBuilder( - future: googleVisionFutureResolver.resolveFile((inputConfig) => [ - AnnotateFileRequest( - inputConfig: inputConfig, - features: features, - ) - ]), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - Widget? widget = onLoading == null - ? const Center(child: CircularProgressIndicator()) - : onLoading!(); + Future _batchAnnotateFilesResponse() async { + final googleVision = await this.googleVision; - if (snapshot.hasData) { - widget = builder( - context, - snapshot, - ); - } else if (snapshot.hasError) { - widget = onError == null - ? onError!(snapshot.error!) - : Center(child: Text('${snapshot.error}')); - } + final inputConfig = await this.inputConfig; - return widget; - }, + return googleVision.file.annotate( + requests: [ + AnnotateFileRequest( + inputConfig: inputConfig, + features: features, + ) + ], + parent: parent, ); } + + /// Builds the widget. + @override + Widget build(BuildContext context) => getBuild( + context, + _batchAnnotateFilesResponse(), + builder, + ); } diff --git a/packages/google_vision_flutter/lib/src/google_vision_future_resolver.dart b/packages/google_vision_flutter/lib/src/google_vision_future_resolver.dart deleted file mode 100644 index 7913db5..0000000 --- a/packages/google_vision_flutter/lib/src/google_vision_future_resolver.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter_image_converter/flutter_image_converter.dart'; -import 'package:google_vision/google_vision.dart' as gv; -import 'package:google_vision_flutter/google_vision_flutter.dart'; - -/// this class is used to remove the use of nested FutureBuilders. It resolves -/// the [GoogleVision] authentication future and the [InputConfig] or -/// [ImageProvider] future, allowing the final GoogleVision API call to be -/// provided as the final future as would be expected by the user. -class GoogleVisionFutureResolver { - /// The final [GoogleVision] object that is resolved from the [googleVisionFuture]. - late final gv.GoogleVision? _googleVision; - - /// The future that resolves the [GoogleVision] object. - final Future googleVisionFuture; - - /// The [ImageProvider] that is used to provide the image data for the API - /// call. - final ImageProvider? imageProvider; - - /// The future that resolves the [InputConfig] object. - final Future? inputConfigFuture; - - /// Error message for when the [GoogleVision] object has not been initialized - /// properly. - static const inputConfigError = - 'InputConfig has not been initialized properly.'; - - /// Error message for when the [ImageProvider] object has not been initialized - /// properly. - static const imageProviderError = - 'ImageProvider has not been initialized properly.'; - - gv.GoogleVision get googleVision => - _googleVision ?? - (throw Exception('GoogleVision has not been initialized properly.')); - - /// Constructor for the [GoogleVisionFutureResolver] class. - GoogleVisionFutureResolver({ - required this.googleVisionFuture, - this.imageProvider, - this.inputConfigFuture, - }); - - @Deprecated('Use resolveImage instead') - - /// Resolves the [GoogleVision] object and the [ImageProvider] object for use - /// by the FutureBuilder. - Future resolve( - AnnotationRequests Function(ByteBuffer?) requestFunction, { - String? parent, - }) async { - _googleVision = await googleVisionFuture; - - if (imageProvider == null) { - throw Exception(imageProviderError); - } - - final byteData = await imageProvider!.pngByteData; - - return googleVision.annotate( - requests: requestFunction(byteData.buffer), - parent: parent, - ); - } - - /// Resolves the [GoogleVision] object and the [ImageProvider] object for use - /// by the FutureBuilder. - Future resolveImage( - List Function(ByteBuffer?) requestFunction, { - String? parent, - }) async { - _googleVision = await googleVisionFuture; - - if (imageProvider == null) { - throw Exception(imageProviderError); - } - - final byteData = await imageProvider!.pngByteData; - - return googleVision.image.annotate( - requests: requestFunction(byteData.buffer), - parent: parent, - ); - } - - /// Resolves the [GoogleVision] object and the [InputConfig] object for use by - /// the FutureBuilder. - Future resolveFile( - List Function(InputConfig) requestFunction, { - String? parent, - }) async { - _googleVision = await googleVisionFuture; - - if (inputConfigFuture == null) { - throw Exception(inputConfigError); - } - - final inputConfig = await inputConfigFuture; - - if (inputConfig == null) { - throw Exception(inputConfigError); - } - - return googleVision.file.annotate( - requests: requestFunction(inputConfig), - parent: parent, - ); - } -} diff --git a/packages/google_vision_flutter/lib/src/google_vision_image_builder.dart b/packages/google_vision_flutter/lib/src/google_vision_image_builder.dart index e75e00f..76cd85e 100644 --- a/packages/google_vision_flutter/lib/src/google_vision_image_builder.dart +++ b/packages/google_vision_flutter/lib/src/google_vision_image_builder.dart @@ -1,4 +1,7 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:flutter_image_converter/flutter_image_converter.dart'; import 'package:google_vision/google_vision.dart' as gv; import 'package:google_vision_flutter/google_vision_flutter.dart' hide GoogleVision, JsonImage; @@ -15,7 +18,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { ) builder; /// Creates a new instance of [GoogleVisionImageBuilder]. - GoogleVisionImageBuilder({ + const GoogleVisionImageBuilder({ super.key, required super.googleVision, required this.imageProvider, @@ -29,7 +32,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for crop hint detections. factory GoogleVisionImageBuilder.cropHints({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -57,7 +60,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for document text detections. factory GoogleVisionImageBuilder.documentTextDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -85,7 +88,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for face detections. factory GoogleVisionImageBuilder.faceDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -113,7 +116,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for image properties detection. factory GoogleVisionImageBuilder.imageProperties({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -141,7 +144,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for label detections. factory GoogleVisionImageBuilder.labelDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -169,7 +172,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for landmark detections. factory GoogleVisionImageBuilder.landmarkDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -197,7 +200,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for logo detections. factory GoogleVisionImageBuilder.logoDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -225,7 +228,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for object localization detections. factory GoogleVisionImageBuilder.objectLocalization({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -253,7 +256,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for product search detections. factory GoogleVisionImageBuilder.productSearch({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -281,7 +284,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for safe search detections. factory GoogleVisionImageBuilder.safeSearchDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -309,7 +312,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for text detections. factory GoogleVisionImageBuilder.textDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -337,7 +340,7 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { /// Creates a new instance of [GoogleVisionImageBuilder] for web detections. factory GoogleVisionImageBuilder.webDetection({ Key? key, - required Future googleVision, + required FutureOr googleVision, required ImageProvider imageProvider, required Widget Function( BuildContext, @@ -362,39 +365,27 @@ class GoogleVisionImageBuilder extends GoogleVisionBuilderBase { AnnotationType.webDetection, maxResults), ); - /// Builds the widget. - @override - Widget build(BuildContext context) { - final googleVisionFutureResolver = GoogleVisionFutureResolver( - googleVisionFuture: googleVision, - imageProvider: imageProvider, - ); - - return FutureBuilder( - future: googleVisionFutureResolver.resolveImage((byteBuffer) => [ - AnnotateImageRequest( - jsonImage: gv.JsonImage(byteBuffer: byteBuffer), - features: features) - ]), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - Widget? widget = onLoading == null - ? const Center(child: CircularProgressIndicator()) - : onLoading!(); + Future _batchAnnotateImagesResponse() async { + final googleVision = await this.googleVision; - if (snapshot.hasData) { - widget = builder( - context, - snapshot, - ); - } else if (snapshot.hasError) { - widget = onError == null - ? onError!(snapshot.error!) - : Center(child: Text('${snapshot.error}')); - } + final byteData = await imageProvider.pngByteData; - return widget; - }, + return googleVision.image.annotate( + requests: [ + AnnotateImageRequest( + jsonImage: gv.JsonImage(byteBuffer: byteData.buffer), + features: features, + ) + ], + parent: parent, ); } + + /// Builds the widget. + @override + Widget build(BuildContext context) => getBuild( + context, + _batchAnnotateImagesResponse(), + builder, + ); } diff --git a/packages/google_vision_flutter/pubspec.yaml b/packages/google_vision_flutter/pubspec.yaml index b378d94..5c477eb 100644 --- a/packages/google_vision_flutter/pubspec.yaml +++ b/packages/google_vision_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: google_vision_flutter description: Add Google Visions image labeling, face, logo, and landmark detection into your Flutter applications. -version: 1.3.0+3 +version: 1.3.0+4 repository: https://github.com/faithoflifedev/google_vision homepage: https://github.com/faithoflifedev/google_vision/tree/main/packages/google_vision_flutter