Last Updated on 2021-10-20 by Clay
在 Flutter 專案中,若是想要調用手機照相機的鏡頭拍照、並展示拍出的相片並不是件難事。對我而言,比較困難的則是把相片儲存在手機的相簿中、甚至是寫一個裁切相片的功能;當然,目前我還在研究當中。
本篇文章則根據 Flutter 官方教學文件:Take a picture using the camera 來紀錄如何在 Flutter 專案中調用照相機鏡頭。
準備工作
Step 1: 添加相關依賴套件 (dependencies)
在調用照相機前,我們會需要將下列三個相關依賴添加於 pubspec.yaml 當中。
- camera: 提供與設備照相機相關工具
- path_provider: 查詢正確路徑儲存照片
- path: 建立在任何平台上都可運作的路徑
dependencies:
flutter:
sdk: flutter
camera:
path_provider:
path:
Step 2: 不同平台的設定
如果在 Android 平台上,需要確認 minSdkVersion 至少為 21。(相關設定請參考:[已解決][Flutter] Manifest merger failed : uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared in library [:camera])
在 iOS 平台上,需要在 ios/Runner/Info.plist 中添加:
<key>NSCameraUsageDescription</key>
<string>Explanation on why the camera access is needed.</string>
範例程式碼
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Obtain a list of available cameras
final cameras = await availableCameras();
// Get a specific camera (first one)
final firstCamera = cameras.first;
runApp(
MaterialApp(
home: TakePicturePage(
camera: firstCamera,
)
)
);
}
// Take picture page
class TakePicturePage extends StatefulWidget {
final CameraDescription camera;
const TakePicturePage({
Key key,
@required this.camera,
}) : super(key: key);
@override
TakePicturePageState createState() => TakePicturePageState();
}
// Take picture page state
class TakePicturePageState extends State<TakePicturePage> {
CameraController _controller;
Future<void> _initializeControllerFuture;
@override
void initState() {
// Init
super.initState();
_controller = CameraController(
// Camera settings
widget.camera,
ResolutionPreset.max,
);
// Initialize the controller
_initializeControllerFuture = _controller.initialize();
}
// Dispose of the controller when the widget is disposed
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Take a picture demo")
),
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(_controller);
}
else {
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: Container(
height: 75.0,
width: 75.0,
child: FittedBox(
child: FloatingActionButton(
backgroundColor: Colors.white,
child: Icon(
Icons.camera_alt_rounded,
color: Colors.black,
),
onPressed: () async {
try {
await _initializeControllerFuture;
final image = await _controller.takePicture();
// If the picture was taken
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DisplayPicturePage(
imagePath: image?.path,
),
),
);
print("My Image Path:");
print(image?.path);
}
catch (error) {
print(error);
}
}
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
// A new page for displaying the picture
class DisplayPicturePage extends StatelessWidget {
final String imagePath;
const DisplayPicturePage({Key key, this.imagePath}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Display the picture")
),
body: Center(
child: Column(
children: <Widget>[
Text("$imagePath"),
Container(
width: MediaQuery.of(context).size.width/2,
height: MediaQuery.of(context).size.height/2,
child: Image.file(File(imagePath)),
),
],
)
)
);
}
}
Output:
可以展示圖片上方的路徑看出,這種方法拍攝的相片並不是儲存在手機的相片庫之類的地方,而是放置於快取(cache)當中。
References
- https://flutter.dev/docs/cookbook/plugins/picture-using-camera
- https://betterprogramming.pub/capturing-photos-using-camera-in-flutter-585656cd3a69