前提・実現したいこと
Flutterでカメラアプリを作っているFlutter初心者の者です。
「カメラを起動」を押すと文字通りカメラが起動する機能を実装中にエラーメッセージが発生しました。
![]
こちらに記載されているソースコードを参考にさせて頂きました。
https://github.com/aftercider/camera-app-2018/blob/master/lib/main.dart
発生している問題・エラーメッセージ
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building CameraWidget(dirty, state: _CameraWidgetState#ae112):
The getter 'length' was called on null.
Receiver: null
Tried calling: length
main.dart
dart
1import 'dart:async'; 2import 'dart:io'; 3 4import 'package:flutter/material.dart'; 5import 'package:camera/camera.dart'; 6import 'package:path_provider/path_provider.dart'; 7import 'package:image_picker_saver/image_picker_saver.dart'; 8import 'package:simple_permissions/simple_permissions.dart'; 9import 'camera.dart'; 10 11 12void main() { 13 runApp(new MyApp()); 14} 15 16class MyApp extends StatelessWidget { 17 18 Widget build (BuildContext context){ 19 return MaterialApp( 20 title: 'Sort cam', 21 theme: ThemeData( 22 primarySwatch:Colors.blue, 23 ), 24 home: MyHomePage() 25 ); 26 } 27} 28class MyHomePage extends StatelessWidget{ 29 Widget build(BuildContext context){ 30 return Scaffold( 31 appBar:AppBar( 32 title: const Text('Sort cam'), 33 actions: <Widget>[ 34 IconButton(icon: Icon(Icons.settings), 35 onPressed:(){}, 36 ) 37 ] 38 ), 39 body: Center( 40 child: RaisedButton(onPressed: () { 41 Navigator.push( 42 context, 43 MaterialPageRoute( 44 builder: (context) => CameraApp()), 45 ); 46 }, child: Text('カメラを起動')) 47 ), 48 ); 49 } 50} 51 52
該当のソースコード(camera.dart)
dart
1import 'dart:async'; // 非同期処理(async/await) 2import 'dart:io'; // ファイルの入出力 3 4import 'package:camera/camera.dart'; 5import 'package:flutter/material.dart'; 6import 'package:path_provider/path_provider.dart'; 7import 'package:image_picker_saver/image_picker_saver.dart'; 8import 'package:simple_permissions/simple_permissions.dart'; 9import 'main.dart'; 10 11List<CameraDescription> cameras; // 使用できるカメラのリスト 12 13// ここから始まる 14Future<void> main() async { 15 cameras = await availableCameras(); 16 runApp(CameraApp()); 17} 18 19// 親玉のApp 20class CameraApp extends StatelessWidget { 21 22 Widget build(BuildContext context) { 23 return MaterialApp( 24 home: CameraWidget(), 25 ); 26 } 27} 28 29// 親玉の中身 30class CameraWidget extends StatefulWidget { 31 32 _CameraWidgetState createState() { 33 return _CameraWidgetState(); 34 } 35} 36 37// 実際はこれがやることやる 38class _CameraWidgetState extends State<CameraWidget> { 39 CameraController controller; 40 final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); 41 String timestamp() => 42 DateTime.now().millisecondsSinceEpoch.toString(); // ファイル名にはタイムスタンプ入れる。 43 void showInSnackBar(String message) => _scaffoldKey.currentState 44 .showSnackBar(SnackBar(content: Text(message))); // SnackBarでメッセージ表示 45 46 47 Widget build(BuildContext context) { 48 Scaffold sc = Scaffold( 49 key: _scaffoldKey, 50 body: Column( 51 children: <Widget>[ 52 Expanded( 53 child: Container( 54 child: Padding( 55 padding: const EdgeInsets.all(1.0), 56 child: Center( 57 child: _cameraPreviewWidget(), // カメラのプレビューを表示するWidget 58 ), 59 ), 60 ), 61 ), 62 Row( 63 mainAxisAlignment: MainAxisAlignment.spaceEvenly, 64 mainAxisSize: MainAxisSize.max, 65 children: <Widget>[ 66 IconButton( 67 // カメラの撮影ボタン 68 icon: const Icon(Icons.camera_alt), 69 onPressed: controller != null && controller.value.isInitialized 70 ? onTakePictureButtonPressed // 撮影ボタンを押された時にコールバックされる関数 71 : null, 72 ), 73 ], 74 ) 75 ], 76 ), 77 ); 78 79 // カメラのセットアップ。セットアップが終わったらもう一回buildが走るので、 80 // controllerがnullかどうかで処理実施有無を判定。 81 if (controller == null) { 82 if (cameras.length == 0) { 83 throw Exception("使用できるカメラがありません"); 84 } 85 setUpCamera(cameras[0]); 86 } 87 return sc; 88 } 89 90 /// カメラプレビューを表示するWidget 91 Widget _cameraPreviewWidget() { 92 if (controller == null || !controller.value.isInitialized) { 93 // カメラの準備ができるまではテキストを表示 94 return const Text('Tap a camera'); 95 } else { 96 // 準備ができたらプレビュー表示 97 return AspectRatio( 98 aspectRatio: controller.value.aspectRatio, 99 child: CameraPreview(controller), 100 ); 101 } 102 } 103 104 // カメラを準備する 105 void setUpCamera(CameraDescription cameraDescription) async { 106 if (controller != null) { 107 await controller.dispose(); 108 } 109 controller = CameraController(cameraDescription, ResolutionPreset.high); 110 111 // カメラの情報が更新されたら呼ばれるリスナー設定 112 controller.addListener(() { 113 if (mounted) setState(() {}); // 準備終わったらbuildし直す。 114 if (controller.value.hasError) { 115 showInSnackBar('Camera error ${controller.value.errorDescription}'); 116 } 117 }); 118 119 await controller.initialize(); 120 121 // パーミッションの確認・要求 122 if (Platform.isAndroid && 123 !await SimplePermissions.checkPermission(Permission.WriteExternalStorage)) { 124 SimplePermissions.requestPermission(Permission.WriteExternalStorage); 125 } else if (Platform.isIOS && 126 !await SimplePermissions.checkPermission(Permission.PhotoLibrary)) { 127 SimplePermissions.requestPermission(Permission.PhotoLibrary); 128 } 129 130 if (mounted) { 131 setState(() {}); 132 } 133 } 134 135 // 撮影ボタンが押されたら撮影して、画像を保存する 136 void onTakePictureButtonPressed() { 137 takePicture().then((String filePath) { 138 if (mounted) { 139 setState(() {}); 140 if (filePath != null) showInSnackBar('Picture saved to $filePath'); 141 } 142 }); 143 } 144 145 // 画像撮影・保存処理 146 Future<String> takePicture() async { 147 if (!controller.value.isInitialized) { 148 return null; 149 } 150 151 Directory dir; 152 if (Platform.isAndroid) { 153 dir = await getExternalStorageDirectory(); // 外部ストレージに保存 154 } else if (Platform.isIOS) { 155 dir = await getTemporaryDirectory(); // 一時ディレクトリに保存 156 } else { 157 return null; 158 } 159 160 final String dirPath = '${dir.path}/Pictures/flutter_test'; 161 await Directory(dirPath).create(recursive: true); 162 String filePath = '$dirPath/${timestamp()}.jpg'; 163 164 if (controller.value.isTakingPicture) { 165 return null; 166 } 167 168 await controller.takePicture(filePath); 169 170 // filePathに保存されたデータをiOSならPhotoLibrary領域にコピーする 171 if (Platform.isIOS) { 172 String tmpPath = filePath; 173 var savedFile = File.fromUri(Uri.file(tmpPath)); 174 filePath = await ImagePickerSaver.saveFile( 175 fileData: savedFile.readAsBytesSync()); 176 } 177 178 return filePath; 179 } 180}
試したこと
解決方法として"Get Upgrade"をしていなかったのでしてみたりしましたが、状況は変わりませんでした。
回答2件
あなたの回答
tips
プレビュー