Created
December 15, 2018 03:35
Star
You must be signed in to star a gist
Flutter Vision Final Version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'dart:io'; | |
import 'package:camera/camera.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:path_provider/path_provider.dart'; | |
import 'package:firebase_ml_vision/firebase_ml_vision.dart'; | |
import 'package:cloud_firestore/cloud_firestore.dart'; | |
import 'package:firebase_storage/firebase_storage.dart'; | |
import 'package:uuid/uuid.dart'; | |
class CameraScreen extends StatefulWidget { | |
@override | |
_CameraScreenState createState() { | |
return _CameraScreenState(); | |
} | |
} | |
class _CameraScreenState extends State<CameraScreen> { | |
CameraController controller; | |
String imagePath; | |
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); | |
@override | |
void initState() { | |
super.initState(); | |
controller = CameraController(cameras[0], ResolutionPreset.medium); | |
controller.initialize().then((_) { | |
if (!mounted) { | |
return; | |
} | |
setState(() {}); | |
}); | |
} | |
@override | |
void dispose() { | |
controller?.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
key: _scaffoldKey, | |
appBar: AppBar( | |
title: const Text('Flutter Vision'), | |
), | |
body: Column( | |
children: <Widget>[ | |
Expanded( | |
child: Container( | |
child: Padding( | |
padding: const EdgeInsets.all(1.0), | |
child: Center( | |
child: _cameraPreviewWidget(), | |
), | |
), | |
), | |
), | |
_captureControlRowWidget(), | |
], | |
), | |
); | |
} | |
/// Display the preview from the camera (or a message if the preview is not available). | |
Widget _cameraPreviewWidget() { | |
if (controller == null || !controller.value.isInitialized) { | |
return const Text( | |
'Tap a camera', | |
style: TextStyle( | |
color: Colors.white, | |
fontSize: 24.0, | |
fontWeight: FontWeight.w900, | |
), | |
); | |
} else { | |
return AspectRatio( | |
aspectRatio: controller.value.aspectRatio, | |
child: CameraPreview(controller), | |
); | |
} | |
} | |
/// Display the control bar with buttons to take pictures. | |
Widget _captureControlRowWidget() { | |
return Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
mainAxisSize: MainAxisSize.max, | |
children: <Widget>[ | |
IconButton( | |
icon: const Icon(Icons.camera_alt), | |
color: Colors.blue, | |
onPressed: controller != null && | |
controller.value.isInitialized | |
? onTakePictureButtonPressed | |
: null, | |
) | |
], | |
); | |
} | |
String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); | |
void showInSnackBar(String message) { | |
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message))); | |
} | |
void onTakePictureButtonPressed() { | |
takePicture().then((String filePath) { | |
if (mounted) { | |
setState(() { | |
imagePath = filePath; | |
}); | |
if (filePath != null) { | |
detectLabels().then((_) { | |
}); | |
} | |
} | |
}); | |
} | |
Future<void> detectLabels() async { | |
final FirebaseVisionImage visionImage = FirebaseVisionImage.fromFilePath(imagePath); | |
final LabelDetector labelDetector = FirebaseVision.instance.labelDetector(); | |
final List<Label> labels = await labelDetector.detectInImage(visionImage); | |
List<String> labelTexts = new List(); | |
for (Label label in labels) { | |
final String text = label.label; | |
labelTexts.add(text); | |
} | |
final String uuid = Uuid().v1(); | |
final String downloadURL = await _uploadFile(uuid); | |
Navigator.push( | |
context, | |
MaterialPageRoute(builder: (context) => ItemsListScreen()), | |
); | |
await _addItem(downloadURL, labelTexts); | |
} | |
Future<void> _addItem(String downloadURL, List<String> labels) async { | |
await Firestore.instance.collection('items').add(<String, dynamic> { | |
'downloadURL': downloadURL, | |
'labels': labels | |
}); | |
} | |
Future<String> _uploadFile(filename) async { | |
final File file = File(imagePath); | |
final StorageReference ref = FirebaseStorage.instance.ref().child('$filename.jpg'); | |
final StorageUploadTask uploadTask = ref.putFile( | |
file, | |
StorageMetadata( | |
contentLanguage: 'en', | |
), | |
); | |
final downloadURL = await (await uploadTask.onComplete).ref.getDownloadURL(); | |
return downloadURL.toString(); | |
} | |
Future<String> takePicture() async { | |
if (!controller.value.isInitialized) { | |
showInSnackBar('Error: select a camera first.'); | |
return null; | |
} | |
final Directory extDir = await getApplicationDocumentsDirectory(); | |
final String dirPath = '${extDir.path}/Pictures/flutter_vision'; | |
await Directory(dirPath).create(recursive: true); | |
final String filePath = '$dirPath/${timestamp()}.jpg'; | |
if (controller.value.isTakingPicture) { | |
// A capture is already pending, do nothing. | |
return null; | |
} | |
try { | |
await controller.takePicture(filePath); | |
} on CameraException catch (e) { | |
_showCameraException(e); | |
return null; | |
} | |
return filePath; | |
} | |
void _showCameraException(CameraException e) { | |
logError(e.code, e.description); | |
showInSnackBar('Error: ${e.code}\n${e.description}'); | |
} | |
} | |
List<CameraDescription> cameras; | |
class ItemsListScreen extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('My Items'), | |
), | |
body: ItemsList(firestore: Firestore.instance), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
Navigator.push( | |
context, | |
MaterialPageRoute(builder: (context) => CameraScreen()), | |
); | |
}, | |
child: const Icon(Icons.add), | |
), | |
); | |
} | |
} | |
class ItemsList extends StatelessWidget { | |
ItemsList({this.firestore}); | |
final Firestore firestore; | |
@override | |
Widget build(BuildContext context) { | |
return StreamBuilder<QuerySnapshot>( | |
stream: firestore.collection('items').snapshots(), | |
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { | |
if (!snapshot.hasData) return const Text('Loading...'); | |
final int itemsCount = snapshot.data.documents.length; | |
return ListView.builder( | |
itemCount: itemsCount, | |
itemBuilder: (_, int index) { | |
final DocumentSnapshot document = snapshot.data.documents[index]; | |
return SafeArea( | |
top: false, | |
bottom: false, | |
child: Container( | |
padding: const EdgeInsets.all(8.0), | |
height: 310.0, | |
child: Card( | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.only( | |
topLeft: Radius.circular(16.0), | |
topRight: Radius.circular(16.0), | |
bottomLeft: Radius.circular(16.0), | |
bottomRight: Radius.circular(16.0), | |
), | |
), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: <Widget>[ | |
// photo and title | |
SizedBox( | |
height: 184.0, | |
child: Stack( | |
children: <Widget>[ | |
Positioned.fill( | |
child: Image.network( | |
document['downloadURL'] | |
), | |
), | |
], | |
), | |
), | |
Expanded( | |
child: Padding( | |
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0), | |
child: DefaultTextStyle( | |
softWrap: true, | |
//overflow: TextOverflow., | |
style: Theme.of(context).textTheme.subhead, | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: <Widget>[ | |
Text(document['labels'].join(', ')), | |
] | |
), | |
), | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
}, | |
); | |
}, | |
); | |
} | |
} | |
class FlutterVisionApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
home: ItemsListScreen(), | |
); | |
} | |
} | |
Future<void> main() async { | |
// Fetch the available cameras before initializing the app. | |
try { | |
cameras = await availableCameras(); | |
} on CameraException catch (e) { | |
logError(e.code, e.description); | |
} | |
runApp(FlutterVisionApp()); | |
} | |
void logError(String code, String message) => | |
print('Error: $code\nError Message: $message'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment