flutter初心者による位置情報取得

#はじめに
今回は位置情報を取得してそれを表示します。こんな感じのアプリです。個人情報の関係で座標は隠してありますが、筆者の環境ではちゃんと動作してるのでご心配なく・・・
![to1ja-za1c6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532483/da0e7885-7aa2-c68f-8737-7d1c884d6680.gif)

geolocatorとpermission_handlerを使いました。バージョンは[flutter初心者が地図snsアプリを作るまで　その１](https://qiita.com/tomo_ikebukuro/items/af4fec6cf13adde45840)の記事の覇権バージョンの項に記載してあります。位置情報を利用するには権限を取得する必要があります。permission_handlerで権限を取得し、geolocatorで位置情報を取得します。geolocator単体で位置情報も取得できますが、今後カメラの権限を取得することを見越してpermission_handlerを使いました。
　詳しい使い方を知りたい方は公式ページを見るといいでしょう。以前にも書きましたが、公式ページが一番詳しいです。
[geolocator](https://pub.dev/packages/geolocator)
[permission_handler](https://pub.dev/packages/permission_handler)
#BLocパターン
今回のコードはBlocパターンを採用しています。読みやすくするためではありません。個人的な意見ですが、Blocパターンを採用するとできることが増え、融通が利きます。~~読みやすくなるだけだったら筆者は採用してないです。~~今回では恩恵が全くないですが、この先間違いなく必要になるので紹介しておきます。新しく覚えるのは２つだけです。



```dart
Provider<Bloc>(
      create:(context){
        return Bloc();
      },
      dispose:(context,bloc)=> bloc.dispose(),
      child: //MaterialApp(...などのWidget,
  )
```
と

```dart
Provider.of<Bloc>(context)
```

です。上が送り手で、下が受け手です。

　送り手の処理：まずcreate:が実行されます。create:が呼ばれるのは最初の一度だけです。ここで作られたインスタンスがchild:以下のWidgetに送られます。最後？に処理を行いたいときはdispose:下に書きます。今回のケースではcreate:で作られたインスタンスのdisposeを実行しています。
　
　受け手の処理：送り手のcreate:で作ったインスタンスを手に入れることができます。

この２つを理解していれば今回のコードを簡単に理解することができます。

#StreamBuilder
位置情報を取得するには少し時間が必要です。取得するまでの間はCircularProgressIndicatorを表示しておきます。ぐるぐるしたロード画面のアレです。StreamBuilder以外でこういった処理を実装しようとするとちょっとめんどうだったりします。これを使わない手はないでしょう。

#コード
```dart:main.dart
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:geolocator/geolocator.dart';
import 'package:rxdart/rxdart.dart';
import 'package:provider/provider.dart';




class Bloc{

  final locationSubject = BehaviorSubject<LatLng>();

  Bloc(){
    _checkLocationPermission();
    _listenLocation();
  }

  void dispose(){
    locationSubject.close();
  }

  void _checkLocationPermission(){
    //ここで位置情報の権限を確認する
    PermissionHandler().checkPermissionStatus(PermissionGroup.location)
        .then((status) {
      if(status != PermissionStatus.granted){
        //拒否されたら権限を要求する
        _requestLocationPermission();
      }
    });
  }

  void _requestLocationPermission(){
    //ここで位置情報を許可しますか？みたいな画面が出る。
    PermissionHandler().requestPermissions([PermissionGroup.location]).
    then((statuses){
      if(statuses[PermissionGroup.location]!=PermissionStatus.granted){
        //ここで設定画面を出して許可してもらえるように促す。
        PermissionHandler().openAppSettings();
      }
    });
  }

  void _listenLocation(){
    Geolocator().getPositionStream(LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 0))
        .listen((Position position){
      locationSubject.add(LatLng(position.latitude, position.longitude));
    });
  }
}

void main() {
  runApp(Provider<Bloc>(
      create:(context){
        return Bloc();
      },
      dispose:(context,bloc)=> bloc.dispose(),
      child: MaterialApp(
        initialRoute: '/',
        routes: {
          '/': (context) => MainPage(),
        },
        theme: ThemeData(
            primarySwatch: Colors.deepOrange,
            accentColor: Colors.deepOrangeAccent
        ),
      )
  ));
}

class MainPage extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return Scaffold(
      body: Center(
        child: StreamBuilder<LatLng>(
          stream: Provider.of<Bloc>(context,listen: false).locationSubject.stream,
          builder: (context,snapshot){
            if(snapshot.hasError || snapshot.connectionState==ConnectionState.none){
              return Text('ERROR!!');
            }
            else if(snapshot.data!=null){
              return Text('latitude:${snapshot.data.latitude}\nlongitude:${snapshot.data.longitude}');
            }
            else{
              return CircularProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}
```