健康診断記録
「健康診断の結果ちゃんと管理してないな」
↓
「スキャンしてGoogle Driveに保存すると良いよ!」 ← 神の声
↓
「そういえばGoogle Drive + Flutterやったこと無いな」
↓
「Flutterで健康診断結果をGoogle Driveに保存するアプリを作ろう」
ということで作ってみました
開発は帰宅してから寝るまでという制約で短時間で出来ることのみに絞って開発しました。求める機能は「簡単にGoogle Driveにアップロード」と「簡単にアップした画像を閲覧」という2点です。
設計図
まずはイラストに落としてみると頭の整理がしやすいので自分さえ理解できれば良いのでアナログですが絵を書いてみることをオススメします。
完成したアプリ
ログイン直後
一覧画面
詳細画面
上の一覧画面からレポートをタップして詳細を開いた画面。画像の拡大縮小をすることが出来ます
※サンプル用画像なので私の健康状態とは関係ありません
必要な機能の一覧
次に設計図を元に必要な実装すべき機能を洗い出します。ここも人に伝えるわけではないので、自分が粒度は適当。
- Google Login
- FABアイコン(スピードダイアル)
- 写真選択
- 写真撮影
- Google Driveに画像保存
- Google Driveからファイル読み込み
- ListViewの画像をタップすると画像詳細画面
- 写真は拡縮できるようにする
最低限この辺りの機能はアプリを便利に使うために必要。
やろうと思ったけどやめたこと
- Firebaseにファイル名、パス、日付タイトルを保存
- AppBarに編集ボタン
- 編集画面(フォーム入力)
- スワイプでファイル削除
リストをしっかりと管理(編集、削除)出来ないようにしました。
Firebaseでデータを管理するほど扱う情報が無いため、画像+ファイル名で管理
ファイル名は撮影時刻で保存することにしてアプリからの変更は無し(Google Drive上でファイル名の変更等を行えば反映できる)
編集画面も同様に無し(Google Drive上でファイル名の変更等を行えば反映できる)
ファイル削除機能も無し(Google Drive上で削除すれば一覧から消すことが出来る)
という事で、アップロードと閲覧を簡単に出来るようにする以外の機能はGoogle Driveの持つ機能をそのまま利用することで実装を回避しました
Flutterでのgoogle-services.jsonの置き場所(Android)
Flutterの記事なのでGoogle API Console等の使い方は割愛
Google Login
google_sign_in 3.0.4
https://pub.dartlang.org/packages/google_sign_in
googleapis 0.51.0
https://pub.dartlang.org/packages/googleapis
google_sign_in: "^3.0.4"
googleapis: "^0.51.0"
import 'package:google_sign_in/google_sign_in.dart';
import 'package:googleapis/drive/v3.dart';
GoogleSignInAccount _currentUser;
// ログイン時に要求するパーミッションを指定
GoogleSignIn _googleSignIn = new GoogleSignIn(
scopes: <String>[
DriveApi.DriveFileScope,
],
);
@override
void initState() {
super.initState();
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) {
setState(() {
_currentUser = account;
});
if (_currentUser != null) {
_handleGetFiles();
}
});
_googleSignIn.signInSilently();
}
// google sign in
Future<Null> _handleSignIn() async {
try {
await _googleSignIn.signIn();
} catch (error) {
print(error);
}
}
のよう必要なパーミッションの列挙、ログイン済みだった場合の自動ログイン処理、ログイン済みユーザーのアカウント情報を取得します。ログイン後は、_currentUser.authHeaders
から認証用のHTTPヘッダー等と取得することが出来るので通信時に使用したり、
ListTile(
leading: GoogleUserCircleAvatar(
identity: _currentUser,
),
title: Text(_currentUser.displayName),
subtitle: Text(_currentUser.email),
)
のようにしてログインユーザーの表示に利用することが出来ます
スピードダイアル
flutter_speed_dial 1.0.4
https://pub.dartlang.org/packages/flutter_speed_dial
こちらの記事でも紹介したFABメニューです。今回やりたいことが簡単に実現できるので採用
Flutterウィークリー #26
https://qiita.com/aoinakanishi/items/27f3d60440dc3caf9158#flutter-speed-dial
flutter_speed_dial: "^1.0.4"
return SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
animatedIconTheme: IconThemeData(size: 22.0),
curve: Curves.bounceIn,
children: [
// Select file
SpeedDialChild(
child: Icon(Icons.add_photo_alternate),
backgroundColor: Colors.green,
onTap: () {
getImage(ImageSource.gallery);
},
label: 'Select',
labelStyle: TextStyle(fontWeight: FontWeight.w500),
),
// Take a picture
SpeedDialChild(
child: Icon(Icons.add_a_photo),
backgroundColor: Colors.deepOrangeAccent,
onTap: () {
getImage(ImageSource.camera);
},
label: 'Camera',
labelStyle: TextStyle(fontWeight: FontWeight.w500),
),
],
);
写真撮影か、ファイル選択を選ぶメニューとして使いました
写真選択・写真撮影
image_picker 0.4.5
https://pub.dartlang.org/packages/image_picker
image_picker: "^0.4.5"
// ギャラリーからファイルを選択
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
// カメラで写真撮影
var image = await ImagePicker.pickImage(source: ImageSource.camera);
これを呼ぶだけでファイルのパスが入ってきてあとの処理はライブラリ側で処理してくれます
Google Driveに画像保存
var client = GoogleHttpClient(await _googleSignIn.currentUser.authHeaders);
var api = DriveApi(client);
// ファイル名を日付+時刻にする
uploadFile(api, image,
DateTime.now().toIso8601String().substring(0, 19) + ".jpg")
.whenComplete(() => client.close());
// upload file to Google drive
Future uploadFile(DriveApi api, io.File file, String filename) {
var media = Media(file.openRead(), file.lengthSync());
return api.files
.create(File.fromJson({"name": filename}), uploadMedia: media)
.then((File f) {
print('Uploaded $file. Id: ${f.id}');
}).whenComplete(() {
// reload content after upload the file
_handleGetFiles();
});
}
Google Driveからファイル読み込み
// Google DriveのAPIを叩く
final Response response = await get(
'https://www.googleapis.com/drive/v3/files',
headers: await _currentUser.authHeaders,
);
// jsonデコード処理
final Map<String, dynamic> data = json.decode(response.body);
// ファイル情報を取得
for (var i = 0; i < data['files'].length; i++) {
print(data['files'][i]['name']);
print(data['files'][i]['id']);
}
ListViewに画像を表示
FadeInImage(
// 読み込み中の画像を指定
placeholder: AssetImage('images/placeholder.png'),
// Google Driveからファイルを取得するURLを指定
image: NetworkImage(
"https://www.googleapis.com/drive/v3/files/" +
data['files'][i]['id'] +
"?alt=media",
// ヘッダーを指定
headers: await _googleSignIn.currentUser.authHeaders),
fadeOutDuration: new Duration(milliseconds: 300),
fadeOutCurve: Curves.decelerate,
height: photoHeight,
width: photoWidth,
fit: BoxFit.fitWidth,
)
という書き方をすることで、ローディング画像を表示しておいてダウンロードが完了すると画像がフェードして入れ替わります。
タップすると詳細画面
GestureDetector(
child: // 画像表示は上で解説したので省略
onTap: () async {
var headers = await _googleSignIn.currentUser.authHeaders;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailScreen(data['files'][i], headers)),
);
}
),
呼び出されるWidgetは以下の表にしました
// Photo viewer
class DetailScreen extends StatelessWidget {
var _data;
Map<String, String> _headers;
DetailScreen(this._data, this._headers);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_data['name']),
),
body: // 下のZoomableImageを使用
);
}
}
詳細画面では拡縮できるようにする
zoomable_image 1.2.0
https://pub.dartlang.org/packages/zoomable_image
zoomable_image: "^1.2.0"
ZoomableImage(
NetworkImage(
"https://www.googleapis.com/drive/v3/files/" +
_data['id'] +
"?alt=media",
headers: _headers
),
placeholder: Center(child: CircularProgressIndicator()),
backgroundColor: Colors.white
)
ソース
まとめ
苦労した点は特になく、スムーズに作業をすることが出来ました。あえて書くとすれば、FlutterからGoogle Drive APIを使用した例が無く、プラグインを使用した例を探すことが出来なかった点がありますが、プラグインもオープンソースで提供されているのでソースを読めば大丈夫。
良かった点はFlutterからGoogle Driveのファイルを扱う方法がわかったこと。改めてFlutterは便利なので今後も色々な自分用アプリを作っていこうかと思えたことです。やりたいことは大体標準のAPIとして組み込まれていたり既に誰かがプラグインを公開していることが多いです。
最新のFlutter情報に触れたい場合は
https://qiita.com/tags/flutterweekly
とかをウォッチしておくと毎週Flutterに関する新しいレポートが読めるかも?
ちなみに、普段メモ用途のアプリに「 Google Keep 」というサービスを使っているのですが、そこに写真を保存しておけば画像にメモも残せるし、検索も出来るし便利なのでオレオレアプリを時間かけて作っるよりもオススメです!!!