Browse Source

take photo and recording video with plugin camera

master
daivph 5 years ago
parent
commit
a660de8819
7 changed files with 475 additions and 13 deletions
  1. +12
    -0
      ios/Podfile.lock
  2. +10
    -1
      lib/main.dart
  3. +386
    -0
      lib/presentation/custom_widgets/camera_helper.dart
  4. +20
    -6
      lib/presentation/custom_widgets/widget_media_helper.dart
  5. +11
    -1
      lib/presentation/screens/login/view/login_page.dart
  6. +27
    -4
      pubspec.lock
  7. +9
    -1
      pubspec.yaml

+ 12
- 0
ios/Podfile.lock View File

@@ -3,6 +3,8 @@ PODS:
- Flutter
- MTBBarcodeScanner
- SwiftProtobuf
- camera (0.0.1):
- Flutter
- Firebase/CoreOnly (6.26.0):
- FirebaseCore (= 6.7.2)
- Firebase/Messaging (6.26.0):
@@ -77,6 +79,8 @@ PODS:
- nanopb/encode (1.30906.0)
- package_info (0.0.1):
- Flutter
- path_provider (0.0.1):
- Flutter
- PromisesObjC (1.2.10)
- Protobuf (3.13.0)
- shared_preferences (0.0.1):
@@ -87,11 +91,13 @@ PODS:

DEPENDENCIES:
- barcode_scan (from `.symlinks/plugins/barcode_scan/ios`)
- camera (from `.symlinks/plugins/camera/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- image_picker (from `.symlinks/plugins/image_picker/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`)

@@ -116,6 +122,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
barcode_scan:
:path: ".symlinks/plugins/barcode_scan/ios"
camera:
:path: ".symlinks/plugins/camera/ios"
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
@@ -126,6 +134,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/image_picker/ios"
package_info:
:path: ".symlinks/plugins/package_info/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios"
video_player:
@@ -133,6 +143,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
barcode_scan: a5c27959edfafaa0c771905bad0b29d6d39e4479
camera: a0ca5080336f7af47b88436e5e26da3dee5568f0
Firebase: 7cf5f9c67f03cb3b606d1d6535286e1080e57eb6
firebase_core: 3134fe79d257d430f163b558caf52a10a87efe8a
firebase_messaging: 6061cbdfe4463502a0d4d7049820c25d1757a095
@@ -150,6 +161,7 @@ SPEC CHECKSUMS:
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151
Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d

+ 10
- 1
lib/main.dart View File

@@ -1,4 +1,5 @@
import 'package:barcode_scan/barcode_scan.dart';
import 'package:camera/camera.dart';
import 'package:farm_tpf/presentation/screens/plot_detail/bloc/plot_detail_bloc.dart';
import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart';
import 'package:flutter/material.dart';
@@ -11,7 +12,15 @@ import 'custom_model/CropPlot.dart';
import 'data/repository/authentication_repository.dart';
import 'data/repository/repository.dart';

void main() {
List<CameraDescription> cameras = [];
Future<void> main() async {
// Fetch the available cameras before initializing the app.
try {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
} on CameraException catch (e) {
print(e.description);
}
runApp(App(authenticationRepository: AuthenticationRepository()));
}


+ 386
- 0
lib/presentation/custom_widgets/camera_helper.dart View File

@@ -0,0 +1,386 @@
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:farm_tpf/main.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:video_player/video_player.dart';
import 'package:path_provider/path_provider.dart';

class CameraHelper extends StatefulWidget {
@override
_CameraHelperState createState() => _CameraHelperState();
}

/// Returns a suitable camera icon for [direction].
IconData getCameraLensIcon(CameraLensDirection direction) {
switch (direction) {
case CameraLensDirection.back:
return Icons.camera_rear;
case CameraLensDirection.front:
return Icons.camera_front;
case CameraLensDirection.external:
return Icons.camera;
}
throw ArgumentError('Unknown lens direction');
}

void logError(String code, String message) =>
print('Error: $code\nError Message: $message');

class _CameraHelperState extends State<CameraHelper>
with WidgetsBindingObserver {
CameraController controller;
String imagePath;
String videoPath;
VideoPlayerController videoController;
VoidCallback videoPlayerListener;
bool enableAudio = true;
int indexCamera = 0;

@override
void initState() {
super.initState();
controller =
CameraController(cameras[indexCamera], ResolutionPreset.medium);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
WidgetsBinding.instance.addObserver(this);
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// App state changed before we got the chance to initialize.
if (controller == null || !controller.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
controller?.dispose();
} else if (state == AppLifecycleState.resumed) {
if (controller != null) {
onNewCameraSelected(controller.description);
}
}
}

final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: SafeArea(
top: false,
bottom: false,
child: Column(
children: <Widget>[
Expanded(
child: Container(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Center(
child: _cameraPreviewWidget(),
),
),
decoration: BoxDecoration(
color: Colors.black,
border: Border.all(
color:
controller != null && controller.value.isRecordingVideo
? Colors.redAccent
: Colors.grey,
width:
controller != null && controller.value.isRecordingVideo
? 1.0
: 0.0,
),
),
),
),
_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(
'Đang mở camera ....',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
);
} else {
return AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
);
}
}

/// Display the control bar with buttons to take pictures and record videos.
Widget _captureControlRowWidget() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Get.back();
}),
_cameraTogglesRowWidget(),
IconButton(
icon: const Icon(Icons.camera_alt),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onTakePictureButtonPressed
: null,
),
IconButton(
icon: const Icon(Icons.videocam),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onVideoRecordButtonPressed
: null,
),
IconButton(
icon: controller != null && controller.value.isRecordingPaused
? Icon(Icons.play_arrow)
: Icon(Icons.pause),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? (controller != null && controller.value.isRecordingPaused
? onResumeButtonPressed
: onPauseButtonPressed)
: null,
),
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? onStopButtonPressed
: null,
)
],
);
}

/// Display a row of toggle to select the camera (or a message if no camera is available).
Widget _cameraTogglesRowWidget() {
if (cameras.isEmpty) {
return const Text('Không có camera');
} else {
bool disableSwitch =
controller != null && controller.value.isRecordingVideo;
if (indexCamera == cameras.length - 1) {
indexCamera = 0;
} else {
indexCamera++;
}
return IconButton(
icon: Icon(
Icons.switch_camera,
color: Colors.green,
),
onPressed: disableSwitch == true
? null
: () {
onNewCameraSelected(cameras[indexCamera]);
});
}
}

String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();

void showInSnackBar(String message) {
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message)));
}

void onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) {
await controller.dispose();
}
controller = CameraController(
cameraDescription,
ResolutionPreset.medium,
enableAudio: enableAudio,
);

// If the controller is updated then update the UI.
controller.addListener(() {
if (mounted) setState(() {});
if (controller.value.hasError) {
showInSnackBar('Lỗi camera: ${controller.value.errorDescription}');
}
});

try {
await controller.initialize();
} on CameraException catch (e) {
_showCameraException(e);
}

if (mounted) {
setState(() {});
}
}

void onTakePictureButtonPressed() {
takePicture().then((String filePath) {
if (mounted) {
setState(() {
imagePath = filePath;
videoController?.dispose();
videoController = null;
});
if (filePath != null) print('Picture saved to $filePath');
}
});
}

void onVideoRecordButtonPressed() {
startVideoRecording().then((String filePath) {
if (mounted) setState(() {});
if (filePath != null) print('Saving video to $filePath');
});
}

void onStopButtonPressed() {
stopVideoRecording().then((_) {
if (mounted) setState(() {});
print('Video recorded to: $videoPath');
});
}

void onPauseButtonPressed() {
pauseVideoRecording().then((_) {
if (mounted) setState(() {});
print('Video recording paused');
});
}

void onResumeButtonPressed() {
resumeVideoRecording().then((_) {
if (mounted) setState(() {});
print('Video recording resumed');
});
}

Future<String> startVideoRecording() async {
if (!controller.value.isInitialized) {
showInSnackBar('Vui lòng chọn camera');
return null;
}

final Directory extDir = await getApplicationDocumentsDirectory();
final String dirPath = '${extDir.path}/Movies/tpf';
await Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.mp4';

if (controller.value.isRecordingVideo) {
// A recording is already started, do nothing.
return null;
}

try {
videoPath = filePath;
await controller.startVideoRecording(filePath);
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
return filePath;
}

Future<void> stopVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}

try {
await controller.stopVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
}

Future<void> pauseVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}

try {
await controller.pauseVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}

Future<void> resumeVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}

try {
await controller.resumeVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}

Future<String> takePicture() async {
if (!controller.value.isInitialized) {
showInSnackBar('Vui lòng chọn camera');
return null;
}
final Directory extDir = await getApplicationDocumentsDirectory();
final String dirPath = '${extDir.path}/Pictures/tpf';
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('Lỗi: ${e.code}\n${e.description}');
}
}

+ 20
- 6
lib/presentation/custom_widgets/widget_media_helper.dart View File

@@ -103,7 +103,7 @@ class _WidgetMediaHelperState extends State<WidgetMediaHelper> {
),
defaultTargetPlatform == TargetPlatform.android
? FutureBuilder<void>(
future: retrieveLostData(),
future: retrieveLostData(context),
builder: (BuildContext context,
AsyncSnapshot<void> snapshot) {
switch (snapshot.connectionState) {
@@ -253,10 +253,11 @@ class _WidgetMediaHelperState extends State<WidgetMediaHelper> {
Future<void> _playVideo(PickedFile file) async {
if (file != null && mounted) {
await _disposeVideoController();
File f = File(file.path);
_controller = VideoPlayerController.file(File(file.path));
await _controller.setVolume(1.0);
await _controller.initialize();
await _controller.setLooping(true);
await _controller.setLooping(false);
await _controller.play();
}
}
@@ -266,9 +267,20 @@ class _WidgetMediaHelperState extends State<WidgetMediaHelper> {
await _controller.setVolume(0.0);
}
if (isVideo) {
final PickedFile file = await _picker.getVideo(
source: source, maxDuration: const Duration(seconds: 10));
await _playVideo(file);
// final video = await FilePicker.getFile(type: FileType.video);
final PickedFile pickedFile = await _picker.getVideo(source: source);
Get.find<ChangeImageController>().updateFile(pickedFile);
Media newMedia = Media()
..isVideo = false
..isServerFile = false
..pathFile = pickedFile.path;
currentItems.add(newMedia);
print(pickedFile.path);
files.add(pickedFile.path);
_playVideo(pickedFile);
// BlocProvider.of<MediaHelperBloc>(context)
// ..add(ChangeListMedia(items: currentItems));
// widget.onChangeFiles(files);
} else {
await _displayPickImageDialog(context,
(double maxWidth, double maxHeight, int quality) async {
@@ -372,7 +384,9 @@ class _WidgetMediaHelperState extends State<WidgetMediaHelper> {
});
}

Future<void> retrieveLostData() async {
Future<void> retrieveLostData(
BuildContext context,
) async {
final LostData response = await _picker.getLostData();
if (response.isEmpty) {
return;

+ 11
- 1
lib/presentation/screens/login/view/login_page.dart View File

@@ -1,7 +1,9 @@
import 'package:farm_tpf/data/repository/authentication_repository.dart';
import 'package:farm_tpf/presentation/custom_widgets/camera_helper.dart';
import 'package:farm_tpf/presentation/screens/login/bloc/login_bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:keyboard_dismisser/keyboard_dismisser.dart';

import 'login_form.dart';
@@ -25,7 +27,15 @@ class LoginPage extends StatelessWidget {
);
},
child: ListView(
children: <Widget>[WidgetTopWelcome(), LoginForm()],
children: <Widget>[
WidgetTopWelcome(),
LoginForm(),
IconButton(
icon: Icon(Icons.cake),
onPressed: () {
Get.to(CameraHelper());
})
],
),
),
),

+ 27
- 4
pubspec.lock View File

@@ -106,6 +106,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "7.1.0"
camera:
dependency: "direct main"
description:
name: camera
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.8+5"
change_app_package_name:
dependency: "direct main"
description:
@@ -376,10 +383,12 @@ packages:
image_picker:
dependency: "direct main"
description:
name: image_picker
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.7+7"
path: "packages/image_picker/image_picker"
ref: "9cac5347d9ab9d111a918a8154fb060565b1bf31"
resolved-ref: "9cac5347d9ab9d111a918a8154fb060565b1bf31"
url: "https://github.com/miguelpruivo/plugins"
source: git
version: "0.6.7+5"
image_picker_platform_interface:
dependency: transitive
description:
@@ -499,6 +508,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.14"
path_provider_linux:
dependency: transitive
description:
@@ -506,6 +522,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+2"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+4"
path_provider_platform_interface:
dependency: transitive
description:

+ 9
- 1
pubspec.yaml View File

@@ -26,7 +26,12 @@ dependencies:
pattern_formatter: ^1.0.2
rxdart: ^0.23.0
barcode_scan: ^3.0.1
image_picker: ^0.6.7+7
#https://github.com/flutter/plugins/pull/2860
image_picker:
git:
url: https://github.com/miguelpruivo/plugins
ref: 9cac5347d9ab9d111a918a8154fb060565b1bf31
path: packages/image_picker/image_picker
video_player: ^0.10.11+2
flutter_plugin_android_lifecycle: ^1.0.4
shimmer: ^1.1.1
@@ -42,6 +47,9 @@ dependencies:
http_parser: ^3.1.4
rflutter_alert: ^1.1.0

camera: ^0.5.8+5
path_provider: ^1.6.14

dev_dependencies:
flutter_test:
sdk: flutter

Loading…
Cancel
Save