質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Q&A

解決済

1回答

1510閲覧

Flutter : geolocatorパッケージのgetCurrentPositionメソッドでの位置情報取得に関して

moriman

総合スコア615

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

0グッド

0クリップ

投稿2020/08/06 17:02

Flutterなんですが、デバイスの位置情報が必要で、geolocatorパッケージを試しに使ってみました。

//main.dart import 'package:geolocator/geolocator.dart'; import 'package:flutter/material.dart'; void main(){ runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { //print("tp---===---===>>>${tp['items'].keys}"); return MaterialApp( title: 'Flutter Storage Example', home: SixthRoute(title:"六番目の画面"), ); } } //https://pub.dev/packages/firebase_auth/example class SixthRoute extends StatefulWidget { SixthRoute({Key key,this.title}) : super(key: key); final String title; @override _SixthRouteState createState() => _SixthRouteState(); } class _SixthRouteState extends State<SixthRoute> { Position cp=Position(latitude:10.0,accuracy:20.0); @override Widget build(BuildContext context) { Future<Position> position = Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high); position.then( (p){ setState((){ cp=p; }); } ); return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text("現在地は$cpです。"), Container( child: RaisedButton( child: const Text('Test registration'), onPressed: (){}//() => _pushPage(context, RegisterPage()), ), padding: const EdgeInsets.all(16), alignment: Alignment.center, ), Container( child: RaisedButton( child: const Text('Test SignIn/SignOut'), onPressed: (){}//() => _pushPage(context, SignInPage()), ), padding: const EdgeInsets.all(16), alignment: Alignment.center, ), ], ), ); } void _pushPage(BuildContext context, Widget page) { Navigator.of(context).push( MaterialPageRoute<void>(builder: (_) => page), ); } }

とりあえずfirebase_authのサンプルに埋め込んで試してみただけでfirebase_authの部分はどうでもいいのですが、
上記のコードで緯度経度が取得できました。できたのですが、出来過ぎというか、予想した挙動と少し違いました。
上記のコードで、スマホを持って移動するとその都度緯度経度の(画面上の)表示が変化します。
上記の書き方で、変数cpがSixthRouteウィジェットのstateになると思います。
なので、setState関数によりstateであるcpに、getCurrentPositionメソッドで取得した位置情報がセットされると、
Text("現在地は$cpです。")
のテキストウィジェットがリビルドされると思うのですが、getCurrentPositionの返り値はFuture型なので、コンプリートするのは一度だけ。それは位置情報が取得できた時、と予想するのが自然だと思います。
つまり_SixthRouteStateクラスのbuild()メソッドが実行され、その中でgetCurrentPositionメソッド実行
→Futureがコンプリート(位置情報取得完了)
→cpに位置情報セット
→Text("現在地は$cpです。")の部分を書き換え。
上記が一度行われてそれだけ、という挙動を予想していたのですが、実際は動くたびに(動くたびというより一定時間ごとに)
Text("現在地は$cpです。")
の部分が変化しています。(動くたびにというよりもよく見ると動いても動かなくても一定時間ごとに更新されている挙動)
これってStreamを使って位置が変わるたびにそれをリッスンしてstateにセットし直す、ということをする必要が有ると思うのですが、なぜStreamを使っていないのに、表示が継続的に更新されるのでしょうか?
Flutterに関して考えていくとこういう推測になると思うのですが、getCurrentPositionの中身を見てみると

Future<Position> getCurrentPosition({ LocationAccuracy desiredAccuracy = LocationAccuracy.best, GeolocationPermission locationPermissionLevel = GeolocationPermission.location, }) async { final PermissionStatus permission = await _getLocationPermission( toPermissionLevel(locationPermissionLevel)); if (permission == PermissionStatus.granted) { final LocationOptions locationOptions = LocationOptions( accuracy: desiredAccuracy, distanceFilter: 0, forceAndroidLocationManager: await _shouldForceAndroidLocationManager()); final Map<dynamic, dynamic> positionMap = await _methodChannel.invokeMethod('getCurrentPosition', Codec.encodeLocationOptions(locationOptions)); try { return Position.fromMap(positionMap); } on ArgumentError { return null; } } else { _handleInvalidPermissions(permission); } return null; }

_methodChannelが使われているので、Swiftの処理を呼び出しているみたいです。結局Swift側でそういう風に実装されているから、みたいな話なんでしょうか?
それでもgetCurrentPositionメソッドの返り値の型がFuture型なのは変わりようがないので、どうにも辻褄が合わないような気がします。

あともう一つ、このコード(位置情報を利用するコード)の場合iOSシミュレータだとうまく動かない(いつまで経っても位置情報が表示されない)ので、iphoneの実機(6s)に繋いで実行すると

Launching lib/main.dart on Owner の iPhone in debug mode... Automatically signing iOS for device deployment using specified development team in Xcode project: 54N944Z87S Running Xcode build... Xcode build done. 21.1s Installing and launching... Installing and launching... 9.1s ═══════════════════════════════════════════════════════════════════════════════════ Your device is locked. Unlock your device first before running. ═══════════════════════════════════════════════════════════════════════════════════ 2020-08-07 01:46:43.301 ios-deploy[91069:691397] [ !! ] Unable to mount developer disk image. (e80000e2) Could not run build/ios/iphoneos/Runner.app on e735eacdced5c5f325b378c703dbd6b60d65eb8c. Try launching Xcode and selecting "Product > Run" to fix the problem: open ios/Runner.xcworkspace Error launching application on Owner の iPhone.

上記のエラーが出て、指示通りリンク(Xcode)を開いてproducu>runを実行すると実機のアプリが起動します。
これって何が原因なんでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

setState() 非同期で実行

SixthRoute リビルド

setState() 非同期で実行

SixthRoute リビルド

....

の繰り返しなだけかと思います。

setState() が非同期で実行されていることがポイントですね。

投稿2020/09/10 17:39

編集2020/09/10 17:40
satokei

総合スコア1204

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

moriman

2020/09/11 00:16

回答を頂きましてありがとうございます。 buildメソッドが実行されるたびにfuture(Geolocator().getCurrentPositionの返り値)が生成されるとは思うのですが、上記のサンプルコードでbuildメソッドがそんなに頻繁に呼び出されるのでしょうか? 私が「buildメソッドが呼び出されるタイミング」についてあやふやなのかもしれません。 移動する度に画面を再読み込みさせるなりすれば、そのつどbuildメソッドが呼び出されるので、表示される位置情報もその都度変化するのはわかるのですが、全く再読み込みなどしてないのに、移動すると位置情報が変化しているので、buildメソッドは呼び出されていないような気がするのですが。 まさにstreambuilderを使用しているような挙動で、streambuilderを使用しているのなら何の疑問もないのですが、Futureを使った上記コードでこの挙動になりますでしょうか。
moriman

2020/09/14 06:14

そのデバッグ方法で試してみようと思います。ありがとうございました。
satokei

2020/09/14 08:04

ご自身でデバッグした方が理解が進むと思います。 (私も理解があやふやな部分がありますし...)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問