Skip to content

Instantly share code, notes, and snippets.

@cbetz
Created December 15, 2018 03:35
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save cbetz/e56936f3850eb97e3fc53f833d98fd74 to your computer and use it in GitHub Desktop.
Flutter Vision Final Version
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