Flutterのlocationライブラリを使って現在地機能を追加する
どうも、前回作った天気予報アプリに、現在地の天気を取得する機能を追加します。
さくっと設計します。
さくっと実装していきます。
どうやら位置情報を取得する(主要な)ライブラリは2種類あるらしく、
それぞれにしかできない機能は、ざっくり以下のとおりです。
前者の機能は最悪自分で書ける気がしますが、後者の機能はめんどくさそうなので今回は後者を使うことにしました。
使い方です。まず普通にpubspec.ymlに追加し、Pub getします。
dependencies:
location: ^3.0.2
Androidは特に何も設定しないで取得できるらしい
Android
With Flutter 1.12, all the dependencies are automatically added to your project. If your project was created before Flutter 1.12, you may need to follow this.
iOSはios/Runner/Info.plistに項目二つ追加するらしい。
iOS
And to use it in iOS, you have to add this permission in Info.plist :NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
を参考にさせてもらい、ios/Runner/Info.plistに4行追記。
<key>NSLocationAlwaysUsageDescription</key> // 追加
<string>Your location is required for this app</string> // 追加
<key>NSLocationWhenInUseUsageDescription</key> // 追加
<string>Your location is required for this app</string> // 追加
これで準備完了。
位置情報を取得するlocation.getLocation関数を呼ぶ前には、スマホの位置情報がオンになっているか、アプリが位置情報にアクセスできるか、の2点を確認する。
Future<bool> checkLocation() async {
Location location = new Location();
bool _serviceEnabled;
PermissionStatus _permissionGranted;
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
return false;
}
}
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return false;
}
}
return true;
}
チェックするのは、地域設定画面で現在位置ボタンを押下した時(1)と、現在位置設定後、アプリを再度起動した時にデフォルトの設定が現在位置になっている時(2)の2カ所で良さそう。
- (1)の場合で、位置情報が取得できなかった場合、選択地域は変更せず、地域設定画面に止まる。
- (2)の場合で、位置情報が取得できなかった場合、とりあえず東京にして天気を返す。
というロジックにします。
では、まず選択肢を追加します。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("地域設定"),
),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return _areaItem(context,
AreaEntity(area: CurrentLocation.area, lon: null, lat: null));
} else {
return _areaItem(context, areas[index - 1]);
}
},
itemCount: 48,
));
}
あんまりよくない気しかしないですが、ListView.builderでindexが0の時を現在位置とし、itemCount を勝手に+1しちゃいます。
_areaItemはこんな感じ。
Widget _areaItem(BuildContext context, AreaEntity area) {
final areaModel = Provider.of<AreaModel>(context, listen: true);
return GestureDetector(
child: Container(
padding: EdgeInsets.all(8.0),
decoration: new BoxDecoration(
border: new Border(
bottom: BorderSide(width: 1.0, color: Colors.grey))),
child: Row(
children: <Widget>[
Container(
margin: EdgeInsets.all(10.0),
child: area.area == areaModel.areaEntity.area
? Icon(Icons.check)
: Icon(null),
),
Text(
area.area ?? "",
),
],
)),
onTap: () async {
bool isPop = true;
if (area.area == CurrentLocation.area) {
bool isGetLocation = await areaModel.checkLocation();
if (!isGetLocation) isPop = false;
}
if (isPop) {
areaModel.update(area);
Navigator.pop(context);
} else {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text("位置情報エラー"),
content: Text("設定から位置情報の取得を許可してください"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () => Navigator.pop(context),
),
],
));
}
},
);
}
CurrentLocationというクラスを作り、areaを比較することでタップされた項目が現在位置か否かを調べてます。
class CurrentLocation {
// 二回目以降の起動フラグ
static const String area = '現在位置';
}
権限を確かめ、もし権限に不備があればisPopがfalseになり、AlertDialogが表示されることになります。情報取得できる場合はupdateで情報更新します。
Future<bool> update(AreaEntity areaEntity) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (areaEntity.area == CurrentLocation.area) {
List<String> loc = await getLocation();
areaEntity.lon = loc[0];
areaEntity.lat = loc[1];
}
prefs.setString(SharedPreferencesKeys.areaEntity, json.encode(areaEntity));
_areaEntity = areaEntity;
_weatherEntity = await getWeather();
notifyListeners();
}
Future<List<String>> getLocation() async {
Location location = new Location();
LocationData _locationData = await location.getLocation();
return [
_locationData.longitude.toString(),
_locationData.latitude.toString()
];
}
これで、現在位置を取得できるようになりました!
動作はこんな感じです。
はてなブログに投稿しました #はてなブログ
— かれきゃす (@karekyasu) 2020年9月26日
Flutterの天気予報アプリに現在地機能を追加する - かれきゃぶろぐ https://t.co/fHDSsZuw5r pic.twitter.com/MV4NeMH3v8
コードは次のリポジトリに置いています!