1.添加依赖:image_picker

image_picker更多参考在https://pub.dev/packages/image_picker
在配置文件pubspec.yaml添加如下配置:

dependencies:flutter:sdk: flutterimage_picker: ^0.6.7

2.开发拍照功能(完整例子)

import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:video_player/video_player.dart';void main() => runApp(DemoApp());class DemoApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return new MaterialApp(title: 'Image Picker Demo',home: new MyHomePage(title: "Image Picker Example",),);}
}class MyHomePage extends StatefulWidget {MyHomePage({Key key, this.title}) : super(key: key);final String title;@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {/// 图片选择器final ImagePicker _picker = ImagePicker();/// 选择的图片PickedFile _imageFile;/// 是否是视频bool isVideo = false;/// 定义错误信息String _retrieveDataError;/// 选择图片时的错误信息dynamic _pickImageError;/// 视频播放器控制器VideoPlayerController _controller;VideoPlayerController _toBeDisposed;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),),body: Center(child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android? FutureBuilder(future: retrieveLostData(),builder:(BuildContext context, AsyncSnapshot<void> snapshot) {switch (snapshot.connectionState) {case ConnectionState.none:case ConnectionState.waiting:return const Text("You have not yet picked an image",textAlign: TextAlign.center,);case ConnectionState.done:return isVideo ? _previewVideo() : _previewImage();default:if (snapshot.hasError) {return Text("Pick image/video error:${snapshot.error}",textAlign: TextAlign.center,);} else {return const Text("You have not yet picked an image",textAlign: TextAlign.center,);}}}): (isVideo ? _previewVideo() : _previewImage())),floatingActionButton: Column(mainAxisAlignment: MainAxisAlignment.end,children: <Widget>[FloatingActionButton(onPressed: () {/// 选择图片isVideo = false;_onImageButtonPressed(ImageSource.gallery, context: context);},child: const Icon(Icons.photo_library),),Padding(padding: const EdgeInsets.only(top: 16.0),child: FloatingActionButton(onPressed: () {/// 拍照isVideo = false;_onImageButtonPressed(ImageSource.camera, context: context);},child: const Icon(Icons.camera_alt),),),Padding(padding: const EdgeInsets.only(top: 16.0),child: FloatingActionButton(onPressed: () {// 选择视频文件isVideo = true;_onImageButtonPressed(ImageSource.gallery);},backgroundColor: Colors.red,child: const Icon(Icons.video_library),),),Padding(padding: const EdgeInsets.only(top: 16.0),child: FloatingActionButton(onPressed: () {// 拍摄isVideo = true;_onImageButtonPressed(ImageSource.camera);},backgroundColor: Colors.red,child: const Icon(Icons.videocam),),),],),);}/// 获得丢失的数据Future<void> retrieveLostData() async {final LostData response = await _picker.getLostData();if (response.isEmpty) {return;}if (response.file != null) {if (response.type == RetrieveType.video) {/// videoisVideo = true;/// 播放视频await _playVideo(response.file);} else {/// 图片isVideo = false;setState(() {/// 更新UI,把图片显示出来_imageFile = response.file;});}} else {_retrieveDataError = response.exception.code;}}/// 播放视频Future<void> _playVideo(PickedFile file) async {/// mounted true 表示当前state仍然在widget tree上if (file != null && mounted) {/// 重置视频播放器控制器await _disposeVideoController();if (kIsWeb) {/// kIsWeb true表示是web应用_controller = VideoPlayerController.network(file.path);/// 设置音量为0await _controller.setVolume(0.0);} else {/// 非web应用_controller = VideoPlayerController.file(File(file.path));/// 设置音量为1.0await _controller.setVolume(1.0);}await _controller.initialize();await _controller.setLooping(true);await _controller.play();/// 更新UIsetState(() {});}}/// 重置视频播放器控制器Future<void> _disposeVideoController() async {if (_toBeDisposed != null) {await _toBeDisposed.dispose();}_toBeDisposed = _controller;_controller = null;}Text _getRetrieveErrorWidget() {if (_retrieveDataError != null) {final Text result = Text(_retrieveDataError);_retrieveDataError = null;return result;}return null;}/// 预览视频Widget _previewVideo() {/// 获取错语信息final Text retrieveError = _getRetrieveErrorWidget();if (retrieveError != null) {/// 有错误信息则显示错误信息return retrieveError;}if (_controller == null) {/// 如果视频播放器控制器为空,则提示用户选择视频return const Text("You have not yet picked a vide",textAlign: TextAlign.center,);}return Padding(padding: const EdgeInsets.all(10.0),child: AspectRatioVideo(_controller),);}/// 预览图片Widget _previewImage() {/// 获取错语信息final Text retrieveError = _getRetrieveErrorWidget();if (retrieveError != null) {/// 有错误信息则显示错误信息return retrieveError;}if (_imageFile != null) {if (kIsWeb) {/// web应用return Image.network(_imageFile.path);} else {/// 非web应用return Image.file(File(_imageFile.path));}} else if (_pickImageError != null) {return Text("Pick image error: $_pickImageError",textAlign: TextAlign.center,);} else {return const Text("You have not yet picked an image",textAlign: TextAlign.center,);}}/// 选择图片、视频,拍摄视频、照片void _onImageButtonPressed(ImageSource source, {BuildContext context}) async {if (_controller != null) {/// 让其静音await _controller.setVolume(0.0);}if (isVideo) {/// 选择或拍摄视频的处理/// 限制视频为10秒final PickedFile file = await _picker.getVideo(source: source, maxDuration: Duration(seconds: 10));/// 返回到此界面时就播放await _playVideo(file);} else {/// 选择或拍摄图片/// 图片的参数设置对话框await _displayPickImageDialog(context,(double maxWidth, double maxHeight, int quality) async {try {final pickedFile = await _picker.getImage(source: source,maxWidth: maxWidth,maxHeight: maxHeight,imageQuality: quality);setState(() {_imageFile = pickedFile;});} catch (e) {_pickImageError = e;}});}}final TextEditingController maxWidthController = TextEditingController();final TextEditingController maxHeightController = TextEditingController();final TextEditingController qualityController = TextEditingController();/// 图片的参数设置对话框Future<void> _displayPickImageDialog(BuildContext context, OnPickImageCallback onPick) async {return showDialog(context: context,builder: (context) {return AlertDialog(title: Text("Add optional parameters"),content: Column(children: <Widget>[TextField(controller: maxWidthController,keyboardType: TextInputType.numberWithOptions(decimal: true),decoration:InputDecoration(hintText: "Enter maxWidth if desired"),),TextField(controller: maxHeightController,keyboardType: TextInputType.numberWithOptions(decimal: true),decoration:InputDecoration(hintText: "Enter maxHeight if desired"),),TextField(controller: qualityController,keyboardType: TextInputType.number,decoration:InputDecoration(hintText: "Enter quality if desired"),),],),actions: <Widget>[FlatButton(onPressed: () {Navigator.of(context).pop();},child: Text("CANCEL"),),FlatButton(onPressed: () {double width = maxWidthController.text.isNotEmpty? double.parse(maxWidthController.text): null;double height = maxHeightController.text.isNotEmpty? double.parse(maxHeightController.text): null;int quality = qualityController.text.isNotEmpty? int.parse(qualityController.text): null;onPick(width, height, quality);Navigator.of(context).pop();},child: Text("PICK"),),],);},);}@overridevoid deactivate() {if (_controller != null) {_controller.setVolume(0.0);_controller.pause();}super.deactivate();}@overridevoid dispose() {_disposeVideoController();maxWidthController.dispose();maxHeightController.dispose();qualityController.dispose();super.dispose();}
}typedef void OnPickImageCallback(double maxWidth, double maxHeight, int quality);/// 自定义一个Widget,用于播放视频
class AspectRatioVideo extends StatefulWidget {AspectRatioVideo(this.controller);final VideoPlayerController controller;@override_AspectRatioVideoState createState() => _AspectRatioVideoState();
}class _AspectRatioVideoState extends State<AspectRatioVideo> {/// 只读VideoPlayerController get controller => widget.controller;bool initialized = false;@overridevoid initState() {super.initState();controller.addListener(_onVideoControllerUpdate);}/// 更新void _onVideoControllerUpdate() {if (!mounted) {return;}if (initialized != controller.value.initialized) {initialized = controller.value.initialized;/// 更新UIsetState(() {});}}@overrideWidget build(BuildContext context) {if (initialized) {return Center(child: AspectRatio(child: VideoPlayer(controller),aspectRatio: controller.value?.aspectRatio,),);} else {return Container();}}/// 关闭时,清理工作@overridevoid dispose() {controller.removeListener(_onVideoControllerUpdate);super.dispose();}
}

2.1.屏幕主体

如果有图片显示,有视频则播放视频,否则提示用户选择图片:

body:Center(child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android? FutureBuilder(future: retrieveLostData(),builder:(BuildContext context, AsyncSnapshot<void> snapshot) {switch (snapshot.connectionState) {case ConnectionState.none:case ConnectionState.waiting:return const Text("You have not yet picked an image",textAlign: TextAlign.center,);case ConnectionState.done:return isVideo ? _previewVideo() : _previewImage();default:if (snapshot.hasError) {return Text("Pick image/video error:${snapshot.error}",textAlign: TextAlign.center,);} else {return const Text("You have not yet picked an image",textAlign: TextAlign.center,);}}}): (isVideo ? _previewVideo() : _previewImage())),

2.2.右下角四个按钮

floatingActionButton: Column(mainAxisAlignment: MainAxisAlignment.end,children: <Widget>[FloatingActionButton(onPressed: () {/// 选择图片isVideo = false;_onImageButtonPressed(ImageSource.gallery, context: context);},child: const Icon(Icons.photo_library),),Padding(padding: const EdgeInsets.only(top: 16.0),child: FloatingActionButton(onPressed: () {/// 拍照isVideo = false;_onImageButtonPressed(ImageSource.camera, context: context);},child: const Icon(Icons.camera_alt),),),Padding(padding: const EdgeInsets.only(top: 16.0),child: FloatingActionButton(onPressed: () {// 选择视频文件isVideo = true;_onImageButtonPressed(ImageSource.gallery);},backgroundColor: Colors.red,child: const Icon(Icons.video_library),),),Padding(padding: const EdgeInsets.only(top: 16.0),child: FloatingActionButton(onPressed: () {// 拍摄isVideo = true;_onImageButtonPressed(ImageSource.camera);},backgroundColor: Colors.red,child: const Icon(Icons.videocam),),),],),

2.3.每个按钮都触发一个事件

/// 选择图片、视频,拍摄视频、照片void _onImageButtonPressed(ImageSource source, {BuildContext context}) async {if (_controller != null) {/// 让其静音await _controller.setVolume(0.0);}if (isVideo) {/// 选择或拍摄视频的处理/// 限制视频为10秒final PickedFile file = await _picker.getVideo(source: source, maxDuration: Duration(seconds: 10));/// 返回到此界面时就播放await _playVideo(file);} else {/// 选择或拍摄图片/// 图片的参数设置对话框await _displayPickImageDialog(context,(double maxWidth, double maxHeight, int quality) async {try {final pickedFile = await _picker.getImage(source: source,maxWidth: maxWidth,maxHeight: maxHeight,imageQuality: quality);setState(() {_imageFile = pickedFile;});} catch (e) {_pickImageError = e;}});}}

2.4.弹窗设置宽、高、质量

  final TextEditingController maxWidthController = TextEditingController();final TextEditingController maxHeightController = TextEditingController();final TextEditingController qualityController = TextEditingController();/// 图片的参数设置对话框Future<void> _displayPickImageDialog(BuildContext context, OnPickImageCallback onPick) async {return showDialog(context: context,builder: (context) {return AlertDialog(title: Text("Add optional parameters"),content: Column(children: <Widget>[TextField(controller: maxWidthController,keyboardType: TextInputType.numberWithOptions(decimal: true),decoration:InputDecoration(hintText: "Enter maxWidth if desired"),),TextField(controller: maxHeightController,keyboardType: TextInputType.numberWithOptions(decimal: true),decoration:InputDecoration(hintText: "Enter maxHeight if desired"),),TextField(controller: qualityController,keyboardType: TextInputType.number,decoration:InputDecoration(hintText: "Enter quality if desired"),),],),actions: <Widget>[FlatButton(onPressed: () {Navigator.of(context).pop();},child: Text("CANCEL"),),FlatButton(onPressed: () {double width = maxWidthController.text.isNotEmpty? double.parse(maxWidthController.text): null;double height = maxHeightController.text.isNotEmpty? double.parse(maxHeightController.text): null;int quality = qualityController.text.isNotEmpty? int.parse(qualityController.text): null;onPick(width, height, quality);Navigator.of(context).pop();},child: Text("PICK"),),],);},);}

2.5.返回的结果是视频的话,就播放。

 /// 播放视频Future<void> _playVideo(PickedFile file) async {/// mounted true 表示当前state仍然在widget tree上if (file != null && mounted) {/// 重置视频播放器控制器await _disposeVideoController();if (kIsWeb) {/// kIsWeb true表示是web应用_controller = VideoPlayerController.network(file.path);/// 设置音量为0await _controller.setVolume(0.0);} else {/// 非web应用_controller = VideoPlayerController.file(File(file.path));/// 设置音量为1.0await _controller.setVolume(1.0);}await _controller.initialize();await _controller.setLooping(true);await _controller.play();/// 更新UIsetState(() {});}}

实际上,上面这个方法只是提前设置好视播放器控制器的信息,然后刷新UI,最终在build方法里通过_previewVideo来将视频显示出来:

  /// 预览视频Widget _previewVideo() {/// 获取错语信息final Text retrieveError = _getRetrieveErrorWidget();if (retrieveError != null) {/// 有错误信息则显示错误信息return retrieveError;}if (_controller == null) {/// 如果视频播放器控制器为空,则提示用户选择视频return const Text("You have not yet picked a vide",textAlign: TextAlign.center,);}return Padding(padding: const EdgeInsets.all(10.0),child: AspectRatioVideo(_controller),);}

为了显示视频,专门写了一个Widget控件AspectRatioVideo来显示:

/// 自定义一个Widget,用于播放视频
class AspectRatioVideo extends StatefulWidget {AspectRatioVideo(this.controller);final VideoPlayerController controller;@override_AspectRatioVideoState createState() => _AspectRatioVideoState();
}class _AspectRatioVideoState extends State<AspectRatioVideo> {/// 只读VideoPlayerController get controller => widget.controller;bool initialized = false;@overridevoid initState() {super.initState();controller.addListener(_onVideoControllerUpdate);}/// 更新void _onVideoControllerUpdate() {if (!mounted) {return;}if (initialized != controller.value.initialized) {initialized = controller.value.initialized;/// 更新UIsetState(() {});}}@overrideWidget build(BuildContext context) {if (initialized) {return Center(child: AspectRatio(child: VideoPlayer(controller),aspectRatio: controller.value?.aspectRatio,),);} else {return Container();}}/// 关闭时,清理工作@overridevoid dispose() {controller.removeListener(_onVideoControllerUpdate);super.dispose();}
}

2.5.如果选择的是图片,则也是通过刷新UI,执行build方法来显示出来的,图片显示的方法:

  /// 预览图片Widget _previewImage() {/// 获取错语信息final Text retrieveError = _getRetrieveErrorWidget();if (retrieveError != null) {/// 有错误信息则显示错误信息return retrieveError;}if (_imageFile != null) {if (kIsWeb) {/// web应用return Image.network(_imageFile.path);} else {/// 非web应用return Image.file(File(_imageFile.path));}} else if (_pickImageError != null) {return Text("Pick image error: $_pickImageError",textAlign: TextAlign.center,);} else {return const Text("You have not yet picked an image",textAlign: TextAlign.center,);}}

3.总结

总的来说,使用flutter来写拍照、录视频等功能的体验真是太棒了!

flutter拍照、拍摄短视频、选择图片相关推荐

  1. 手机超广角拍摄软件_桂林好的拍摄短视频手机软件

    桂林好的拍摄短视频手机软件 F1j0c1m9 桂林好的拍摄短视频手机软件 因此一个谨慎的文案策划,可以很大程度上保证后期制作流程的稳健运行.很多时候为了更好的宣传一款产品都会选择制作产品广告片,它可以 ...

  2. 短视频和图片去水印的三种方法

    短视频和图片去水印怎么去?近些年来,随着短视频的崛起,现在越来越多人加入到拍短视频的行列当中去,当然也有很多人会在自己的空闲时间里刷刷短视频,消磨消磨自己的时间.那当我们刷到好看又好玩的视频想要保存下 ...

  3. 拍摄短视频需要哪些工具?一部手机还不够,简单的设备你得有

    拍摄短视频需要哪些工具?一部手机还不够,简单的设备你得有 正所谓,工欲善其事必先利其器,拍摄短视频亦是如此,跟专业的短视频拍摄团队比不了,我们如果只是个人做短视频,又想要做出来一些成绩,那么一部手机其 ...

  4. Android拍照及从相册选择图片传详解(终极版)

    Android 拍照及从相册选择图片传详解 先上图 新知识点速览 URI(统一资源标识符)是标识逻辑或物理资源的字符序列,与URL类似,也是一串字符.通过使用位置,名称或两者来标识Internet上的 ...

  5. iOS上传头像, 相册权限,相册权限,拍照上传,相册选择图片,拍照页面语言设置,保存到相册...

    2019独角兽企业重金招聘Python工程师标准>>> 1. 权限 在打开相机拍照或者打开相册选择图片之前, 有必要先判断先是否有权限, 如果没有权限应该给个提示, 让用户自己去设置 ...

  6. Android开发之调用相机拍照与本地图库选择图片

    引用链接 Android开发之调用相机拍照与本地图库选择图片 Android调用相机实现拍照功能 部分截图 引言 小项目有一个访问相册的需求,在网上查找得到两位大神博客指点,但博客发布时间过旧,难免因 ...

  7. 抖音这样拍摄短视频,能让你轻松上热门涨粉丨国仁网络资讯

    哪些动辄几十万.上百万点赞的抖音小视频怎么拍的?为什么同样是十几秒小视频,他们拍的又好看又酷炫,而自己拍的--一言难尽? 抖音最开始是以热潮的音乐加上炫酷的特效起家的,发展到现在依旧还是有很多的特效视 ...

  8. 背景随意更换,同时改变多个竖屏短视频背景图片的两种方法

    怎么在电脑上批量剪辑多个短视频呢?比如改变多个竖屏短视频的背景图,下面可以随小编一起用视频剪辑高手来试试. 一.直接添加上下图片 运行视频剪辑高手,在"批量剪辑视频"功能上,单击& ...

  9. Android 拍照、从相册选择图片

    在做Android图片上传功能的时候,获取图片的途径一般都有两种:拍照.从相册选择. 一.拍照 调用相机拍照有两种方法: 直接返回图片. 在调用相机的时候,传入uri,拍照后通过该uri来获取图片. ...

最新文章

  1. 生产上第一使用线程池后的总结与反思
  2. 关于如何生成随机记录
  3. rhel 4/oracle linux 4/centos linux 4 配置本地yum资源库
  4. 计算机还是数学竞赛内容吗,除了AMC,数学牛娃还能参加什么高含金量的数学竞赛...
  5. oppo手机计算机,OPPO手机助手
  6. gromacs 安装_GROMACS:粗粒化力场建立和模拟上线!
  7. 以Debug模式启动JBoss
  8. 时钟偏移(Skew)和时钟抖动(Jitter)
  9. 用SPSS搞定问卷调查中的决断值
  10. java中控指纹仪_java 中控URU4500指纹仪开发
  11. html css 画梯形,css怎么画梯形?
  12. SpringBoot进阶(十)整合Shiro上篇
  13. 蓝牙成为物联网市场中的有力竞争者
  14. JVM:JVM常见参数配置
  15. 软件测试以bug数来考核,软件测试能力提升及其思考
  16. 先进半导体材料与器件Chapter4
  17. 小米4c android版本号,小米4c的手机系统是什么?能升级安卓5.0吗?
  18. Electron和Vue
  19. C语言用if语句判断规定字符串
  20. pcie扰码的作用_CDMA中扰码的作用

热门文章

  1. windows 7下利用双网卡加快多任务下载的速度
  2. 文件夹变成计算机程序,电脑中毒,文件夹变成应用程序,该怎么处理?
  3. 联想固态硬盘一年两坏,联想,拿什么去爱你?
  4. 为什么众多IT名人都在悼念金庸?
  5. 使用9Patch图片作为背景
  6. 程序员被公司辞退12天后,前领导要求回公司讲清代码,结果懵了
  7. Android websocket长连接+点对点订阅
  8. 工业控制应用程序二进制的秘密
  9. 服务器时间设置及同步
  10. 固态硬盘连接在SATA2、SATA3接口上,使用不同SATA线的速度对比