以身份證識(shí)別(Face++的API接口實(shí)現(xiàn)方式:https://console.faceplusplus.com.cn/documents/5671702)為例苟弛,實(shí)現(xiàn)一個(gè)自定義相機(jī)的功能魄咕,實(shí)現(xiàn)效果:自定義相機(jī)頁(yè)面、拍照、照片預(yù)覽。
自定義相機(jī)頁(yè)面效果展示
代碼實(shí)現(xiàn)
GitHub地址(https://github.com/Lightforest/FlutterVideo)
1.在pubspec.yaml中添加插件:
camera(flutter官方插件:https://pub.dev/packages/camera)
2.圖片資源準(zhǔn)備
將身份證框圖放在項(xiàng)目根目錄下的assets目錄中備用,并在pubspec.yaml中導(dǎo)入目錄
3.頁(yè)面布局實(shí)現(xiàn)
頁(yè)面總布局代碼如下:
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: new Container(
color: Colors.black,
child:new Stack(children: <Widget>[
new Column(children: <Widget>[
Expanded(
flex: 3,//flex用來(lái)設(shè)置當(dāng)前可用空間的占優(yōu)比
child: new Stack(children: <Widget>[
_cameraPreviewWidget(),//相機(jī)視圖
_cameraFloatImage(),//懸浮的身份證框圖
]),
),
Expanded(
flex: 1,//flex用來(lái)設(shè)置當(dāng)前可用空間的占優(yōu)比
child: _takePictureLayout(),//拍照操作區(qū)域布局
),
],),
getPhotoPreview(),//圖片預(yù)覽布局
getProgressDialog(),//數(shù)據(jù)加載中提示框
getRestartAlert(),// 身份證識(shí)別失敗,重新拍照的提示按鈕
]),
)
);
}
頁(yè)面總布局共包括6部分的內(nèi)容:相機(jī)預(yù)覽、身份證框圖陶贼、拍照按鈕區(qū)域、拍照后的圖片預(yù)覽待秃、識(shí)別身份證時(shí)的加載框拜秧、身份證識(shí)別失敗的提示信息。
相機(jī)預(yù)覽(cameraPreviewWidget())的代碼如下:
Widget _cameraPreviewWidget() {
if (controller == null || !controller.value.isInitialized) {
return const Text(
'Tap a camera',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
fontWeight: FontWeight.w900,
),
);
} else {
return new Container(
width:double.infinity,
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
),
);
}
}
身份證框圖(_cameraFloatImage())的代碼如下:
Widget _cameraFloatImage(){
return new Positioned(
child: new Container(
alignment: Alignment.center,
margin: const EdgeInsets.fromLTRB(50, 50, 50, 50),
child:new Image.asset('assets/images/bg_identify_idcard.png'),
));
}
拍照區(qū)域(_takePictureLayout())的代碼如下:
Widget _takePictureLayout(){
return new Align(
alignment: Alignment.bottomCenter,
child: new Container(
color: Colors.blueAccent,
alignment: Alignment.center,
child: new IconButton(
iconSize: 50.0,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onTakePictureButtonPressed
: null,
icon: Icon(
Icons.photo_camera,
color: Colors.white,
),
),
));
}
拍照后的圖片預(yù)覽(getPhotoPreview())代碼如下:
Widget getPhotoPreview(){
if( null != photoPath){
return new Container(
width:double.infinity,
height: double.infinity,
color: Colors.black,
alignment: Alignment.center,
child: Image.file(File(photoPath)),
);
}else{
return new Container(
height: 1.0,
width: 1.0,
color: Colors.black,
alignment: Alignment.bottomLeft,
);
}
}
識(shí)別身份證時(shí)的加載框(getProgressDialog())代碼如下:
Widget getProgressDialog(){
if(showProgressDialog){
return new Container(
color: Colors.black12,
alignment: Alignment.center,
child: SpinKitFadingCircle(color: Colors.blueAccent),
);
}else{
return new Container(
height: 1.0,
width: 1.0,
color: Colors.black,
alignment: Alignment.bottomLeft,
);
}
}
加載框用的是flutter的官方插件:flutter_spinkit(https://pub.dev/packages/flutter_spinkit)章郁,可自由選擇加載框樣式枉氮。
身份證識(shí)別失敗的提示信息(getRestartAlert())代碼如下:
Widget getRestartAlert(){
if(restart){
return new Container(
color: Colors.white,
alignment: Alignment.center,
child: Column(
children: <Widget>[
new Text(
"身份證識(shí)別失敗,重新采集識(shí)別?",
style: TextStyle(color: Colors.black26,fontSize: 18.0),
),
IconButton(
icon: Icon(
Icons.subdirectory_arrow_right,
color: Colors.blueAccent,
),
onPressed:toRestartIdentify() ,),
]
),
);
}else{
return new Container(
height: 1.0,
width: 1.0,
color: Colors.black,
alignment: Alignment.bottomLeft,
);
}
}
4.相機(jī)拍照
異步獲取相機(jī)
List<CameraDescription> cameras;
Future<void> getCameras() async {
// Fetch the available cameras before initializing the app.
try {
cameras = await availableCameras();
//FlutterImageCompress.showNativeLog = true;
} on CameraException catch (e) {
print(e.toString());
}
}
相機(jī)獲取成功后,初始化CameraController嘲恍,可選擇攝像頭(成功后才可展示相機(jī)布局足画,否則相機(jī)無(wú)法正常工作)
@override
void initState() {
super.initState();
if(cameras != null && !cameras.isEmpty){
onNewCameraSelected(cameras[0]);// 后置攝像頭
// onNewCameraSelected(cameras[1]);// 前置攝像頭
}
}
void onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) {
await controller.dispose();
}
controller = CameraController(cameraDescription, ResolutionPreset.high);
// If the controller is updated then update the UI.
controller.addListener(() {
if (mounted) setState(() {});
if (controller.value.hasError) {
showInSnackBar('Camera error ${controller.value.errorDescription}');
}
});
try {
await controller.initialize();
} on CameraException catch (e) {
_showCameraException(e);
}
if (mounted) {
setState(() {});
}
}
拍照按鈕點(diǎn)擊事件實(shí)現(xiàn)
void onTakePictureButtonPressed() {
takePicture().then((String filePath) {
if (mounted) {
setState(() {
videoController = null;
videoController?.dispose();
});
if (filePath != null) {
//showInSnackBar('Picture saved to $filePath');
photoPath = filePath;
setState(() { });// 通過(guò)設(shè)置photoPath 的狀態(tài)展示圖片預(yù)覽頁(yè)
getIDCardInfo(File(filePath));//進(jìn)行身份證識(shí)別
}
}
});
}
Future<String> takePicture() async {
if (!controller.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
return null;
}
final Directory extDir = await getApplicationDocumentsDirectory();
final String dirPath = '${extDir.path}/Pictures/flutter_test';
await Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';
if (controller.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
try {
await controller.takePicture(filePath);
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
return filePath;
}
識(shí)別身份證信息
Future getIDCardInfo(File file) async {
showProgressDialog =true;//展示加載框
Dio dio = new Dio();
dio.options.contentType=ContentType.parse("multipart/form-data");
var baseUrl = "https://api-cn.faceplusplus.com/cardpp/v1/ocridcard";
final String targetPath = await getTempDir();
File compressFile =await getCompressImage(file, targetPath);
FormData formData = new FormData.from({
"api_key": APPApiKey.Face_api_key,
"api_secret": APPApiKey.Face_api_secret,
"image_file": new UploadFileInfo(compressFile, "image_file")
});
Response<Map> response;
try {
response =await dio.post<Map>(baseUrl,data:formData);
} catch (e) {
print(e);
showProgressDialog =false;//隱藏加載框
setState(() {});
}
showProgressDialog =false;//隱藏加載框
setState(() {});
if(response != null){
print(response.data);
Map<String,Object> resultMap = response.data;
List<dynamic> cards =resultMap['cards'];
if(cards != null && !cards.isEmpty){
Map<String,dynamic> card = cards[0];
Navigator.pop(context, card);
var idcard = card[IDCardIdentifyResult.id_card_number];
var name = card[IDCardIdentifyResult.name];
var birth = card[IDCardIdentifyResult.birthday];
var address = card[IDCardIdentifyResult.address];
print('身份證號(hào): ${idcard}');
print('姓名: ${name}');
print('出生日期: ${birth}');
print('地址: ${address}');
}else{
restart = true;//展示重新拍照的提示
setState(() {});
}
}else{
restart = true;//展示重新拍照的提示
setState(() {});
}
}
end