Last active
August 4, 2023 21:15
-
-
Save Alvarocda/40c64333cbd93d33158a0d62ae133bec to your computer and use it in GitHub Desktop.
This file contains hidden or 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 'package:camera/camera.dart'; | |
import 'package:camera_example/camera_screen_controller.dart'; | |
import 'package:flutter/material.dart'; | |
/// | |
/// | |
/// | |
class CameraScreen extends StatefulWidget { | |
/// | |
/// | |
/// | |
const CameraScreen({Key? key}) : super(key: key); | |
/// | |
/// | |
/// | |
@override | |
_CameraScreenState createState() => _CameraScreenState(); | |
} | |
/// | |
/// | |
/// | |
class _CameraScreenState extends State<CameraScreen> with WidgetsBindingObserver { | |
late final CameraScreenController _controller = CameraScreenController(context: context); | |
/// | |
/// | |
/// | |
@override | |
Widget build(BuildContext context) { | |
return FutureBuilder<void>( | |
future: _controller.initializeCamera(), | |
builder: (BuildContext context, AsyncSnapshot<void> snapshot) { | |
if (snapshot.connectionState != ConnectionState.done) { | |
return const Scaffold( | |
body: Text('Loading camera'), | |
); | |
} | |
if (snapshot.hasError) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Gravação'), | |
), | |
body: const Center( | |
child: Text( | |
'Failed to load camera.', | |
textAlign: TextAlign.center, | |
), | |
), | |
); | |
} | |
return Scaffold( | |
backgroundColor: Colors.black, | |
body: Stack( | |
alignment: Alignment.bottomCenter, | |
children: <Widget>[ | |
ValueListenableBuilder<CameraLensDirection>( | |
valueListenable: _controller.activeCamera, | |
builder: ( | |
BuildContext context, | |
CameraLensDirection value, | |
Widget? child, | |
) { | |
return CameraPreview(_controller.cameraController!); | |
}, | |
), | |
Container( | |
padding: const EdgeInsets.all(16), | |
color: Colors.black38, | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: <Widget>[ | |
_toggleFlash(), | |
_startStopRecordButton(), | |
_toggleCameraButton(), | |
], | |
), | |
], | |
), | |
), | |
], | |
), | |
); | |
}, | |
); | |
} | |
/// | |
/// | |
/// | |
Widget _toggleFlash() { | |
return InkWell( | |
onTap: _controller.toggleFlash, | |
child: Card( | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(50), | |
), | |
color: Colors.white, | |
child: Container( | |
height: 65, | |
width: 65, | |
child: ValueListenableBuilder<FlashMode>( | |
valueListenable: _controller.flashMode, | |
builder: ( | |
BuildContext context, | |
FlashMode flashMode, | |
Widget? child, | |
) { | |
return Icon( | |
flashMode == FlashMode.off ? Icons.flash_off : Icons.flash_on, | |
size: 35, | |
); | |
}, | |
), | |
), | |
), | |
); | |
} | |
/// | |
/// | |
/// | |
Widget _toggleCameraButton() { | |
return InkWell( | |
onTap: _controller.toggleCamera, | |
child: Card( | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(50), | |
), | |
color: Colors.white, | |
child: Container( | |
height: 65, | |
width: 65, | |
child: const Icon( | |
Icons.switch_camera, | |
size: 35, | |
), | |
), | |
), | |
); | |
} | |
/// | |
/// | |
/// | |
Widget _startStopRecordButton() { | |
return InkWell( | |
onTap: _controller.startStopRecord, | |
child: ValueListenableBuilder<bool>( | |
valueListenable: _controller.recording, | |
builder: (BuildContext context, bool value, Widget? child) { | |
return Card( | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(50), | |
), | |
color: value ? Colors.white : Colors.red, | |
child: Container( | |
height: 95, | |
width: 95, | |
child: Icon( | |
value ? Icons.stop : Icons.play_arrow, | |
color: value ? Colors.black : Colors.white, | |
), | |
), | |
); | |
}, | |
), | |
); | |
} | |
/// | |
/// | |
/// | |
@override | |
void dispose() { | |
_controller.dispose(); | |
super.dispose(); | |
} | |
/// | |
/// | |
/// | |
@override | |
void didChangeAppLifecycleState(AppLifecycleState state) { | |
CameraController cameraController = _controller.cameraController!; | |
if (!cameraController.value.isInitialized) { | |
return; | |
} | |
if (state == AppLifecycleState.inactive) { | |
cameraController.dispose(); | |
} else if (state == AppLifecycleState.resumed) { | |
_controller.initializeCamera(cameraDescription: cameraController.description); | |
} | |
} | |
} |
This file contains hidden or 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 'package:camera/camera.dart'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
/// | |
/// | |
/// | |
class CameraScreenController { | |
final BuildContext context; | |
CameraController? cameraController; | |
List<CameraDescription>? _availableCameras; | |
final ValueNotifier<CameraLensDirection> activeCamera = ValueNotifier<CameraLensDirection>(CameraLensDirection.back); | |
final ValueNotifier<Duration> recordDuration = ValueNotifier<Duration>(Duration.zero); | |
final ValueNotifier<bool> recording = ValueNotifier<bool>(false); | |
Timer? _timerGravacao; | |
final ValueNotifier<FlashMode> flashMode = ValueNotifier<FlashMode>(FlashMode.off); | |
/// | |
/// | |
/// | |
CameraScreenController({ | |
required this.context, | |
}); | |
/// | |
/// | |
/// | |
Future<void> initializeCamera({CameraDescription? cameraDescription}) async { | |
_availableCameras ??= await availableCameras(); | |
cameraController = CameraController( | |
cameraDescription ?? _availableCameras!.first, | |
ResolutionPreset.medium, | |
imageFormatGroup: ImageFormatGroup.jpeg, | |
); | |
await cameraController!.initialize(); | |
if (!kIsWeb) { | |
await cameraController!.setFlashMode(FlashMode.off); | |
} | |
cameraController!.addListener(() { | |
recording.value = cameraController!.value.isRecordingVideo; | |
}); | |
} | |
/// | |
/// | |
/// | |
Future<void> toggleFlash() async { | |
if (cameraController!.value.flashMode == FlashMode.always) { | |
await cameraController!.setFlashMode(FlashMode.off); | |
} else { | |
await cameraController!.setFlashMode(FlashMode.always); | |
} | |
flashMode.value = cameraController!.value.flashMode; | |
} | |
/// | |
/// | |
/// | |
Future<void> toggleCamera() async { | |
if (!recording.value) { | |
CameraLensDirection camera = activeCamera.value; | |
if (activeCamera.value == CameraLensDirection.back) { | |
await initializeCamera(cameraDescription: _availableCameras!.last); | |
camera = _availableCameras!.last.lensDirection; | |
} else { | |
await initializeCamera(cameraDescription: _availableCameras!.first); | |
camera = _availableCameras!.first.lensDirection; | |
} | |
activeCamera.value = camera; | |
} | |
} | |
/// | |
/// | |
/// | |
Future<void> startStopRecord() async { | |
recording.value = cameraController!.value.isRecordingVideo; | |
if (recording.value) { | |
XFile xfile = await cameraController!.stopVideoRecording(); | |
_timerGravacao?.cancel(); | |
Navigator.of(context).pop(xfile); | |
} else { | |
await cameraController!.startVideoRecording(); | |
int miliSegundos = 0; | |
_timerGravacao = Timer.periodic(const Duration(milliseconds: 100), (Timer timer) { | |
miliSegundos += 100; | |
recordDuration.value = Duration(milliseconds: miliSegundos); | |
}); | |
} | |
} | |
/// | |
/// | |
/// | |
void dispose() { | |
cameraController?.dispose(); | |
_timerGravacao?.cancel(); | |
recording.dispose(); | |
recordDuration.dispose(); | |
flashMode.dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment