Tương tác với Camera và Thư viện ảnh (Gallery) trong Flutter
Chào mọi người lại là mình Coder tập sự đây, sau thời gian bận chút việc cá nhân thì hôm nay mình và các bạn lại tiếp tục cùng nhau tìm hiểu flutter từ con số 0 nhé.
Khi phát triển ứng dụng di động, việc cho phép người dùng chụp ảnh hoặc chọn ảnh/video từ thư viện là một tính năng cực kỳ phổ biến và quan trọng. Từ ứng dụng mạng xã hội, ứng dụng ghi chú có hình ảnh, đến các ứng dụng mua sắm yêu cầu tải ảnh sản phẩm, khả năng tương tác với camera và gallery của thiết bị là không thể thiếu.
Trong Flutter, chúng ta sẽ thực hiện điều này một cách dễ dàng nhờ vào một package mạnh mẽ và được cộng đồng tin dùng.
Package image_picker
Để tương tác với camera và thư viện ảnh, chúng ta sẽ sử dụng package image_picker. Đây là package chính thức và được duy trì tốt, cung cấp một API đơn giản để truy cập các tính năng này trên cả Android và iOS.
Cài đặt image_picker:
Đầu tiên, hãy thêm dependency sau vào file pubspec.yaml
của dự án chúng ta:
dependencies:
flutter:
sdk: flutter
image_picker: ^1.1.0
Sau đó, chạy flutter pub get
trong terminal để tải package.
Cấu hình Native (Quan trọng ⚠️):
Để image_picker hoạt động đúng, chúng ta cần thêm một số cấu hình liên quan đến quyền truy cập vào file AndroidManifest.xml
(Android) và Info.plist
(iOS).
Cho Android:
Mở file android/app/src/main/AndroidManifest.xml
và đảm bảo rằng bạn có các quyền sau bên trong thẻ <manifest>
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<application ...>
...
</application>
</manifest>
Lưu ý cho Android 13 (API 33) trở lên:
Với Android 13 trở lên, quyền READ_EXTERNAL_STORAGE
và WRITE_EXTERNAL_STORAGE
đã được thay thế bằng các quyền truy cập cụ thể hơn vào loại media. Nếu chúng ta đang nhắm mục tiêu SDK 33+, thì nên thay thế chúng bằng:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
Nếu chúng ta vẫn muốn hỗ trợ các phiên bản Android cũ hơn, hãy giữ cả hai bộ quyền.
Cho iOS:
Mở file ios/Runner/Info.plist
và thêm các khóa sau vào trong thẻ <dict>
:
<dict>
...
<key>NSPhotoLibraryUsageDescription</key>
<string>Ứng dụng cần truy cập thư viện ảnh để cho phép bạn chọn ảnh.</string>
<key>NSCameraUsageDescription</key>
<string>Ứng dụng cần truy cập camera để chụp ảnh.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Ứng dụng cần truy cập microphone để quay video.</string>
...
</dict>
Đây là các chuỗi mô tả lý do ứng dụng của chúng ta cần các quyền này, và sẽ được hiển thị cho người dùng khi ứng dụng yêu cầu cấp quyền lần đầu.
Chụp ảnh/Chọn ảnh/video từ Camera và Gallery
Sau khi đã cài đặt và cấu hình, việc sử dụng image_picker
rất đơn giản:
Bước 1: Import thư viện
import 'package:image_picker/image_picker.dart';
Bước 2: Tạo instance của ImagePicker
final ImagePicker _picker = ImagePicker();
Bước 3: Thực hiện thao tác chọn/chụp
- Chọn ảnh từ Gallery:
Future<void> _pickImageFromGallery() async {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
// Xử lý ảnh đã chọn: hiển thị, upload, v.v.
print('Đường dẫn ảnh từ Gallery: ${image.path}');
// Ví dụ: setState(() { _imageFile = File(image.path); });
}
}
- Chụp ảnh bằng Camera:
Future<void> _captureImageFromCamera() async {
final XFile? image = await _picker.pickImage(source: ImageSource.camera);
if (image != null) {
// Xử lý ảnh đã chụp
print('Đường dẫn ảnh từ Camera: ${image.path}');
// Ví dụ: setState(() { _imageFile = File(image.path); });
}
}
- Chọn video từ Gallery:
Future<void> _pickVideoFromGallery() async {
final XFile? video = await _picker.pickVideo(source: ImageSource.gallery);
if (video != null) {
// Xử lý video đã chọn
print('Đường dẫn video từ Gallery: ${video.path}');
}
}
Bước 4: Xử lý và hiển thị file media
picker.pickImage()
và picker.pickVideo()
đều trả về một đối tượng XFile
(cross-platform file). XFile
có thuộc tính path chứa đường dẫn đến file media đã chọn hoặc chụp. Chúng ta có thể dùng đường dẫn này để hiển thị ảnh bằng Image.file()
hoặc video bằng các package video player.
Tuyệt vời! Để bạn có thể chạy thử và thấy ngay kết quả, đây là ví dụ đầy đủ của một ứng dụng Flutter sử dụng image_picker để chụp ảnh hoặc chọn ảnh từ thư viện và hiển thị chúng.
Đảm bảo bạn đã thêm image_picker: ^1.1.0 (hoặc phiên bản mới nhất) vào pubspec.yaml và chạy flutter pub get. Đồng thời, đừng quên cấu hình quyền truy cập cho Android và iOS như đã hướng dẫn ở bài viết trước nhé!
Dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: ImagePickerDemo(),
debugShowCheckedModeBanner: false,
);
}
}
class ImagePickerDemo extends StatefulWidget {
const ImagePickerDemo({super.key});
State<ImagePickerDemo> createState() => _ImagePickerDemoState();
}
class _ImagePickerDemoState extends State<ImagePickerDemo> {
File? _selectedImage;
final ImagePicker _picker = ImagePicker();
// Phương thức để chọn ảnh từ Gallery
Future<void> _pickImageFromGallery() async {
// Gọi pickImage với source là ImageSource.gallery
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
setState(() {
_selectedImage = File(image.path);
});
// Hiển thị thông báo hoặc thực hiện hành động khác với ảnh đã chọn
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Đã chọn ảnh từ Gallery: ${image.path.split('/').last}')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Chưa chọn ảnh nào từ Gallery')),
);
}
}
// Phương thức để chụp ảnh bằng Camera
Future<void> _captureImageFromCamera() async {
final XFile? image = await _picker.pickImage(source: ImageSource.camera);
if (image != null) {
setState(() {
_selectedImage = File(image.path);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Đã chụp ảnh từ Camera: ${image.path.split('/').last}')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Chưa chụp ảnh nào bằng Camera')),
);
}
}
void _clearImage() {
setState(() {
_selectedImage = null;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Đã xóa ảnh hiện tại')),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Image Picker Demo'),
centerTitle: true,
backgroundColor: Colors.blueAccent,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 300,
height: 200,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1.0),
borderRadius: BorderRadius.circular(8.0),
),
child: _selectedImage == null
? const Center(
child: Text(
'Chưa có ảnh nào được chọn.',
style: TextStyle(color: Colors.grey),
),
)
: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.file(
_selectedImage!,
fit: BoxFit.cover,
),
),
),
const SizedBox(height: 30),
ElevatedButton.icon(
onPressed: _pickImageFromGallery,
icon: const Icon(Icons.photo_library),
label: const Text('Chọn ảnh từ Gallery'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
textStyle: const TextStyle(fontSize: 16),
),
),
const SizedBox(height: 15),
ElevatedButton.icon(
onPressed: _captureImageFromCamera,
icon: const Icon(Icons.camera_alt),
label: const Text('Chụp ảnh bằng Camera'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
textStyle: const TextStyle(fontSize: 16),
),
),
const SizedBox(height: 15),
if (_selectedImage != null)
ElevatedButton.icon(
onPressed: _clearImage,
icon: const Icon(Icons.clear),
label: const Text('Xóa ảnh'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
textStyle: const TextStyle(fontSize: 16),
),
),
],
),
),
);
}
}
Kết luận
Package image_picker
là một công cụ mạnh mẽ và dễ sử dụng để tương tác với camera và thư viện ảnh trên thiết bị. Bằng cách làm theo các bước cài đặt, cấu hình quyền và sử dụng API đơn giản, chúng ta có thể dễ dàng thêm khả năng chụp ảnh hoặc chọn file media vào ứng dụng Flutter của mình.
Đây là một bước quan trọng trong việc xây dựng các ứng dụng di động thực tế. Hãy thực hành với ví dụ trên để nắm vững cách hoạt động của image_picker
nhé!
Tham khảo
- image_picker package trên Pub.dev
All rights reserved