撮影して保存するだけ
はいタイトルの通り写真を撮影してスマートフォンの写真アプリに保存するだけです。
Udemyの講座のカメラアプリを作ろうとしたのですがバージョンのせいか上手くできない。なぜだ😅
なので公式を参考にしつつ機能を省いて作ってみた。
必要なパッケージはこちら⬇️
https://pub.dev/packages/camera/example
https://pub.dev/packages/image_gallery_saver
iOSの設定
カメラへのアクセスを許可する必要があるのでInfo.plistに設定を追加。といっても動画教材のものをそのまま使ってます笑
公式の設定はこちら
<key>NSCameraUsageDescription</key>
<string>your usage description here</string>
<key>NSMicrophoneUsageDescription</key>
<string>your usage description here</string>
自分の設定
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Udemy Camera App</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>udemy_camera_app</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>写真を撮影するためにカメラのアクセスが必要です。</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>写真を保存するためにフォトライブラリへのアクセスが必要です。</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>写真をフォトライブラリに追加するためのアクセスが必要です。</string>
<key>NSMicrophoneUsageDescription</key>
<string>動画撮影時に音声をキャプチャするためにマイクアクセスが必要です。</string>
</dict>
</plist>
Android
画像の位置にパーミッションの設定を追加すればOKです。
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:label="udemy_camera_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:requestLegacyExternalStorage="true">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>
example
こちらのソースコードをそのまま使えばボタンを押せば撮影できる単純なアプリは作れます。
拡大縮小などカメラを操作する機能はありません🙇
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final cameras = await availableCameras();
runApp(CameraApp(cameras: cameras));
}
class CameraApp extends StatelessWidget {
final List<CameraDescription> cameras;
const CameraApp({super.key, required this.cameras});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CameraScreen(cameras: cameras),
);
}
}
class CameraScreen extends StatefulWidget {
final List<CameraDescription> cameras;
const CameraScreen({super.key, required this.cameras});
@override
State<CameraScreen> createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
late CameraController _controller;
bool _isInitialized = false;
@override
void initState() {
super.initState();
_initializeCamera();
}
Future<void> _initializeCamera() async {
if (widget.cameras.isEmpty) {
return;
}
_controller = CameraController(
widget.cameras[0],
ResolutionPreset.medium,
);
try {
await _controller.initialize();
setState(() {
_isInitialized = true;
});
} catch (e) {
debugPrint('カメラの初期化エラー: $e');
}
}
Future<void> _takePicture() async {
if (!_isInitialized) {
return;
}
try {
final XFile image = await _controller.takePicture();
// 写真を端末のギャラリーに保存
final result = await ImageGallerySaver.saveFile(image.path);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('写真を保存しました')),
);
}
} catch (e) {
debugPrint('写真撮影エラー: $e');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('写真の撮影に失敗しました')),
);
}
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!_isInitialized) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
body: Stack(
children: [
CameraPreview(_controller),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FloatingActionButton(
onPressed: _takePicture,
child: const Icon(Icons.camera_alt),
),
),
),
],
),
);
}
}
最後に
今回はカメラ機能を使うだけの単純なアプリを作ってみました。しかしpub.devのExampleのコードはわかりづらかった😅
もっと簡単なサンプルないかなと思い記事を書いてみました。