# Flutter 好用package - camera 介紹
## camera
使用手機內建相機功能
```
flutter pub add camera
```
[官網](https://pub.dev/packages/camera)
### 程式範例
#### 畫面
```dart
class CaptureImagePage extends StatefulWidget {
static MaterialPage page() {
return MaterialPage(
name: APPPages.captureImagePage,
key: ValueKey(APPPages.captureImagePage),
child: const CaptureImagePage());
}
const CaptureImagePage({Key? key}) : super(key: key);
@override
State<SCaptureImagePage> createState() {
return _CaptureImagePageState();
}
}
class _CaptureImagePageState
extends State<CaptureImagePage>
with WidgetsBindingObserver, TickerProviderStateMixin {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
Provider.of<CameraManager>(context, listen: false).init();
}
@override
void deactivate() {
super.deactivate();
Provider.of<CameraManager>(context, listen: false).reset();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController =
Provider.of<CameraManager>(context, listen: false).controller;
if (cameraController == null || !cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
Provider.of<CameraManager>(context, listen: false).isInit = false;
} else if (state == AppLifecycleState.resumed) {
Provider.of<CameraManager>(context, listen: false)
.onNewCameraSelected(cameraDescription: cameraController.description);
}
}
@override
Widget build(BuildContext context) {
return BasicPage(
safeArea: false,
widget: Provider.of<CameraManager>(context, listen: true).isInit
? Stack(
children: [
Transform.scale(
scale: 1 /
(Provider.of<CameraManager>(context, listen: false)
.controller!
.value
.aspectRatio *
MediaQuery.of(context).size.aspectRatio),
alignment: Alignment.topCenter,
child: CameraPreview(
Provider.of<CameraManager>(context, listen: false)
.controller!,
),
),
const Align(
alignment: Alignment.bottomCenter, child: MainFunBtns()),
const CaptureImageMessage()
],
)
: const Center(child: CircularProgressIndicator()));
}
}
class MainFunBtn extends StatelessWidget {
const MainFunBtn(
{super.key, required this.text, required this.filePath, this.onTap});
final String text;
final String filePath;
final Function()? onTap;
@override
Widget build(BuildContext context) {
return CircleImageBtn(
text: text,
fontSize: 16,
textColor: Colors.white,
onTap: onTap,
height: 70,
width: 70,
filePath: filePath,
);
}
}
class MainFunBtns extends StatelessWidget {
const MainFunBtns({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 16, left: 16, bottom: 34),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MainFunBtn(
text: '拍照',
onTap: () {
Provider.of<CameraManager>(context, listen: false)
.onTakePictureButtonPressed(
account: LocalStorage.get('account'));
},
filePath:
'assets/images/self_medication_capture_image_page/icon_dslr-camera.png',
),
MainFunBtn(
text: '鏡頭反轉',
onTap: () {
Provider.of<CameraManager>(context, listen: false)
.onNewCameraSelected();
},
filePath:
'assets/images/self_medication_capture_image_page/icon_flip_lens.png',
),
],
),
);
}
}
class GalleryBtn extends StatelessWidget {
const GalleryBtn({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: [
MainFunBtn(
text: '檢視照片',
onTap: () {
Provider.of<AppStateManager>(context, listen: false)
.showSelfMedicationScreenshotListPage();
},
filePath:
'assets/images/self_medication_capture_image_page/icon_album.png',
),
const GalleryNum()
],
);
}
}
class CaptureImageMessage extends StatelessWidget {
const CaptureImageMessage({super.key});
@override
Widget build(BuildContext context) {
return Consumer<CameraManager>(builder: (context, manager, child) {
return CaptureImage(
isCaptureFrame: manager.isCaptureImage,
text: '已拍攝照片',
);
});
}
}
```
#### 狀態管理
camera_manager.dart
```dart
import 'dart:async';
import 'package:camera/camera.dart';
import 'package:catcatcat/enums/file_type.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../helper/fileHelper.dart';
import '../../utils/fake_encryption.dart';
class CameraManager extends ChangeNotifier {
List<CameraDescription> _cameras = <CameraDescription>[];
CameraController? _controller;
bool isInit = false; //相機是否初始化完成
bool _isFrontCamera = true; //是否使用前鏡頭
bool _isCaptureImage = false; //拍攝照片動畫用
List<CameraDescription> get cameras => _cameras;
CameraController? get controller => _controller;
bool get isCaptureImage => _isCaptureImage;
//取得所有可以使用相機,通常[0]:後鏡頭 [1]:前鏡頭
Future<void> getAvailableCameras() async {
_cameras = await availableCameras();
}
//將相機初始化
void init() {
_controller = CameraController(_cameras.first, ResolutionPreset.medium,
imageFormatGroup: ImageFormatGroup.jpeg);
_controller!.initialize().then((value) {
isInit = true;
notifyListeners();
});
_isFrontCamera = true;
}
//重置回原始狀況
void reset() {
isInit = false;
}
//鏡頭切換
Future<void> onNewCameraSelected(
{CameraDescription? cameraDescription}) async {
if (cameraDescription == null) {
if (_isFrontCamera) {
cameraDescription = _cameras[1];
} else {
cameraDescription = _cameras[0];
}
_isFrontCamera = !_isFrontCamera;
}
final CameraController? oldController = _controller;
if (oldController != null) {
_controller = null;
await oldController.dispose();
}
final CameraController cameraController = CameraController(
cameraDescription,
ResolutionPreset.medium,
enableAudio: false,
imageFormatGroup: ImageFormatGroup.jpeg,
);
_controller = cameraController;
cameraController.addListener(() {
if (cameraController.value.hasError) {
debugPrint('Camera error ${cameraController.value.errorDescription}');
}
});
try {
await cameraController.initialize();
} on CameraException catch (e) {
switch (e.code) {
case 'CameraAccessDenied':
debugPrint('You have denied camera access.');
break;
case 'CameraAccessDeniedWithoutPrompt':
// iOS only
debugPrint('Please go to Settings app to enable camera access.');
break;
case 'CameraAccessRestricted':
// iOS only
debugPrint('Camera access is restricted.');
break;
case 'AudioAccessDenied':
debugPrint('You have denied audio access.');
break;
case 'AudioAccessDeniedWithoutPrompt':
// iOS only
debugPrint('Please go to Settings app to enable audio access.');
break;
case 'AudioAccessRestricted':
// iOS only
debugPrint('Audio access is restricted.');
break;
default:
debugPrint('Error: ${e.code}\n${e.description}');
break;
}
}
notifyListeners();
}
//拍照
void onTakePictureButtonPressed({required String account}) {
if (!_isCaptureImage) {
_isCaptureImage = true;
notifyListeners();
_takePicture().then((XFile? file) async {
DateTime now = DateTime.now().toUtc();
String formattedDate = DateFormat('yyyyMMddkkmmss').format(now);
String storagePath = await FileHelper.getFilePath(
account: account, fileType: FileType.selfMedication);
if (file != null) {
FakeEncryption.moveAndEncodeFile(
filePath: '$storagePath$formattedDate.jpg',
oriFilePath: file.path,
);
Timer(const Duration(milliseconds: 1000), () {
_isCaptureImage = false;
notifyListeners();
});
}
});
}
}
//呼叫camera套件拍照功能
Future<XFile?> _takePicture() async {
final CameraController? cameraController = _controller;
if (cameraController == null || !cameraController.value.isInitialized) {
debugPrint('Error: select a camera first.');
return null;
}
if (cameraController.value.isTakingPicture) {
return null;
}
try {
final XFile file = await cameraController.takePicture();
return file;
} on CameraException catch (e) {
debugPrint('Error: ${e.code}\n${e.description}');
return null;
}
}
}
```
###### tags: `flutter`