12. 拍摄、保存、显示
演示如何将 RICOH THETA X 相机与 Android 和 IOS 手机配合使用。
特点
- 拍照
- 将图片保存到相册
- 从相册选择图片并显示
- 以 360 度视图查看图片
资源
此应用程序是根据以下教程构建的。
- THETA 概念 3 (使用 Bloc 管理拍照)
- THETA 概念 6 (在继续之前检查相机状态)
- THETA 概念 9 (将图片保存到相册)
- THETA 概念 11 (从相册选择图片)
关键 Flutter 包
- panorama – 查看带导航的 360 度图片
- image_picker – 从相册选择图片
- gallery_saver – 将图片保存到相册
- flutter_bloc – 管理状态
360 度查看图片
该项目使用 panorama 包以 360 度视图查看图片。当用户点击图片时,Navigator.push 会将其全屏显示。
class PanoramaScreen extends StatefulWidget {
File myFile;
PanoramaScreen({Key? key, required this.myFile}) : super(key: key);
@override
State<PanoramaScreen> createState() => _PanoramaScreenState();
}
class _PanoramaScreenState extends State<PanoramaScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Panorama(child: Image.file(widget.myFile)),
));
}
}
拍照
有关该过程的更多描述,请参阅 教程 6. RICOH THETA API 命令序列。
该项目执行 RICOH THETA API 中的 camera.takePicture 命令。它会持续检查 takePicture 过程是否通过 commands/status 完成。当图片处理完成后,项目将进入下一步:将图片保存到相册。
on<PictureEvent>((event, emit) async {
var url = Uri.parse('http://192.168.1.1/osc/commands/execute');
var header = {'Content-Type': 'application/json;charset=utf-8'};
var bodyMap = {'name': 'camera.takePicture'};
var bodyJson = jsonEncode(bodyMap);
var response = await http.post(url, headers: header, body: bodyJson);
var convertResponse = jsonDecode(response.body);
var id = convertResponse['id'];
if (convertResponse != null && id != null) {
emit(ThetaState(message: response.body, id: id));
while (state.cameraState != "done") {
add(CameraStatusEvent());
await Future.delayed(Duration(milliseconds: 200));
print(state.cameraState);
}
}
add(GetFileEvent());
});
将图片保存到相册
GetFileEvent 使用 camera.listFiles 从相机检索最后一个文件 URL。它从响应中解析出 URL,并使用文件 URL 更新状态。
on<GetFileEvent>((event, emit) async {
...
var fileUrl = convertResponse['results']['entries'][0]['fileUrl'];
emit(state.copyWith(fileUrl: fileUrl));
...
}
将 gallery_saver 包导入到项目中,并在 AndroidManifest.xml 文件中添加权限。
android.permission.WRITE_EXTERNAL_STORAGE
使用 SaveFileEvent 中的 GallerySaver.saveImage 保存图片,并通知状态图片已保存完成。
on<SaveFileEvent>((event, emit) async {
await GallerySaver.saveImage(state.fileUrl).then((value) {
emit(state.copyWith(finishedSaving: true));
});
});
从相册选择图片
该应用程序的这一部分遵循了 Smirty 的 Flutter 学习 教程。
theta_event.dart
...
class ImagePickerEvent extends ThetaEvent {
final XFile image;
ImagePickerEvent(this.image);
}
theta_bloc.dart
on<ImagePickerEvent>((event, emit) async {
emit(state.copyWith(images: event.image));
});
main.dart
IconButton(
onPressed: () async {
final image = await ImagePicker().pickImage(
source: ImageSource.gallery,
);
if (image == null) return;
context
.read<ThetaBloc>()
.add(ImagePickerEvent(image));
},
icon: Icon(Icons.image)),
当按下 IconButton 时,它会使用 ImagePicker 中的文件添加 ImagePickerEvent。在 Bloc 文件中,ImagePickerEvent 会使用文件更新状态。
Bloc 结构
该项目使用 flutter_bloc 包来处理状态管理。事件与发生的每个操作相关联。状态在参数和主构造函数中保存信息。在 Bloc 文件中,有 on 方法来处理每个事件的调用。
状态构造函数的示例
class ThetaState extends Equatable {
final String message;
final String fileUrl;
final String cameraState;
final String id;
final bool finishedSaving;
final XFile? images;
ThetaState(
{required this.message,
this.fileUrl = "",
this.cameraState = "initial",
this.id = "",
this.finishedSaving = false,
this.images});}

