【Flutter】ネイティブ連携

## 前提（バンドルIDについて）

組織名(Organization): com.example
プロジェクト名(Project name): myapp
の場合、
バンドルID: com.example.myapp

## MethodChannel(双方向通信)
Flutter側からNative側へメソッドの呼び出しを行い、Native側はそのメソッドを実行後、結果やデータをFlutter側へ返却する
"バンドルID/~"のMethodChannel文字列がFlutter側とNative側で一致している必要がある。

■用途
任意のタイミングでネイティブの機能を利用
このチャンネルを通じて、Flutterアプリケーションはネイティブプラットフォームの機能（カメラアクセス、位置情報サービスなど）を利用できる

■iOS側の操作

iOS/Runner/AppDelegate.swiftを開く
「Open iOS/macOS module in Xcode」というリンクがあるのでクリックするとXcodeで開ける
開いたXcodeでAppDlegateとinfo.plistを編集
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136142/37dd3fa1-dd99-a5c4-d28a-72d821489d56.png)


■Android側の操作
以下のMainActivity/AndroidManifest.xmlを編集

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136142/6275803a-99e5-3bf9-2a25-1b0d31ab447e.png)

■位置情報を取得する実装例

＜Flutter＞

<details><summary>gps_loation_repository.dart</summary><div>

```dart
import 'dart:async';

import 'package:flutter/services.dart';
import './location_repository.dart';

/// GPSを使った位置情報を取得するための仕組み。
class GpsLocationRepository extends LocationRepository {
  static const platform = MethodChannel('com.example.multios/location');

  // 2) ここに後ほどEventChannelの処置を追加

  /// 1) MethodChannelを使って位置情報を取得します。
  @override
  Future<Location> get() async {
    final String result = await platform.invokeMethod('getLocation');
    final splitted = result.split(',');
    return Location(double.parse(splitted[0]), double.parse(splitted[1]));
  }

/// 2) EventChannelを使って位置情報を監視します。(後ほど追加)
}
```

</div></details>

＜iOS＞

<details><summary>AppDelegate.swift</summary><div>

```swift AppDelegate.swift
import UIKit
import Flutter
import CoreLocation

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, CLLocationManagerDelegate {
    //    @objc class AppDelegate: FlutterAppDelegate, CLLocationManagerDelegate, FlutterStreamHandler  {
    private var locationChannel: FlutterMethodChannel?
    private var locationManager: CLLocationManager!
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        // ここから追加
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        locationChannel = FlutterMethodChannel(name: "com.example.multios/location",
                                               binaryMessenger: controller.binaryMessenger)
        locationChannel?.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            if call.method == "getLocation" {
                self.getLocation(result: result)
            }
            else {
                result(FlutterMethodNotImplemented)
            }
        })
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    // 位置情報取得ロジック
    private func getLocation(result: @escaping FlutterResult) {
        if CLLocationManager.authorizationStatus() == .authorizedWhenInUse || CLLocationManager.authorizationStatus() == .authorizedAlways {
            if let location = locationManager.location {
                result("\(location.coordinate.latitude),\(location.coordinate.longitude)")
            } else {
                result(FlutterError(code: "UNAVAILABLE", message: "Location not available.", details: nil))
            }
        } else {
            result(FlutterError(code: "PERMISSION_DENIED", message: "Location permission denied.", details: nil))
        }
    }
}

```

</div></details>

```plist info.plist
        <key>NSLocationWhenInUseUsageDescription</key>
        <string>位置情報を使用します</string>
<!-- ↑ファイル末尾</dict>タグの上に-->
```

＜Android＞

<details><summary>MainActivity.kt</summary><div>

```kotlin MainActivity.kt
package com.example.multios

import android.Manifest
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import androidx.annotation.NonNull
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
//import com.example.multios.map.GoogleMapViewFactory
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel

/**
 * Android Studio内のOpen for Editing in Android Studioをクリックすることで、
 * もう一つAndroid Studio開きましょう。そうすることで補完が効くようになります。
 */
class MainActivity : FlutterActivity() {
    // 追加
    private val CHANNEL = "com.example.multios/location"

    // 2) 追加
//    var locationListener: LocationListener? = null

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        // 1) 追加
        MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            CHANNEL
        ).setMethodCallHandler { call, result ->
            if (call.method == "getLocation") {
                val location = getLocation() // 実際の位置情報取得ロジックを実装
                if (location.isEmpty()) {
                    result.error("UNAVAILABLE", "Location not available.", null)
                } else {
                    result.success(location)
                }
            }
            // 2) 追加
            else {
                result.notImplemented()
            }
        }

        // 2) 追加
    }

    // 1) 位置情報取得ロジック
    private fun getLocation(): String {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                1
            )
            return ""
        }

        val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
        val location: Location? = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
        return if (location == null) {
            ""
        } else {
            "${location.latitude},${location.longitude}"
        }
    }

// 2) 位置情報の更新を開始するロジックを実装
}


```

</div></details>

```xml AndroidManifest.xml
<!-- <manifest xmlns:android="~">タグの↓に -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```

## EventChannel（一方向通信：Native -> Flutter）
Native側で発生したイベント（センサーデータの更新、位置情報の変更など）をFlutter側へ非同期で送信。
"バンドルID/~"のEventChannel文字列がFlutter側とNative側で一致している必要がある。

■用途
ネイティブの機能を常に監視する
このチャンネルは、特定のイベントやデータが更新された時に、それをFlutterアプリケーションでリッスンし、ハンドリングする

■位置情報を監視する実装例

＜Flutter＞

<details><summary>gps_loation_repository.dart</summary><div>

```dart

import 'dart:async';

import 'package:flutter/services.dart';
import './location_repository.dart';

/// GPSを使った位置情報を取得するための仕組み。
class GpsLocationRepository extends LocationRepository {
  static const platform = MethodChannel('com.example.multios/location');

  // 2) EventChannelの追加
  static const eventChannel =
  EventChannel('com.example.multios/locationUpdates');
  StreamSubscription<dynamic>? _locationSubscription;
  StreamController<Location>? _locationStreamController;

  /// 1) MethodChannelを使って位置情報を取得します。
  @override
  Future<Location> get() async {
    final String result = await platform.invokeMethod('getLocation');
    final splitted = result.split(',');
    return Location(double.parse(splitted[0]), double.parse(splitted[1]));
  }

  /// 2) EventChannelを使って位置情報を監視します。
  @override
  Stream<Location> watch() {
    if (_locationSubscription != null) {
      return _locationStreamController!.stream;
    }
    _locationSubscription = eventChannel.receiveBroadcastStream().listen(
          (event) {
        final splitted = (event as String).split(',');
        final location =
        Location(double.parse(splitted[0]), double.parse(splitted[1]));
        print("更新された位置情報: 緯度 ${location.latitude}, 経度 ${location.longitude}");
        _locationStreamController!.add(location);
      },
      onError: (dynamic error) {
        print('Received error: ${error.message}');
      },
    );
    platform.invokeMethod('watchLocation');

    _locationStreamController = StreamController<Location>();
    return _locationStreamController!.stream;
  }
}


```

</div></details>

＜iOS＞

<details><summary>AppDelegate.swift</summary><div>

```swift AppDelegate.swift

import UIKit
import Flutter
import CoreLocation

// Android Studio内のOpen iOS/macOS module in Xcodeをクリックすることで、
// もう一つXcode開きましょう。そうすることで補完が効くようになります。
// , CLLocationManagerDelegate を追加することを忘れずに。
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, CLLocationManagerDelegate, FlutterStreamHandler {
    // 追加
    private var locationChannel: FlutterMethodChannel?
    private var locationManager: CLLocationManager!
    
    // 2) 追加
    private var eventChannel: FlutterEventChannel?
    private var eventSink: FlutterEventSink?
    
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        
        // ここから追加
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
        
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        locationChannel = FlutterMethodChannel(name: "com.example.multios/location",
                                               binaryMessenger: controller.binaryMessenger)
        locationChannel?.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            if call.method == "getLocation" {
                self.getLocation(result: result)
            } else if call.method == "watchLocation" {// 2) 追加
                print("watchLocation")
                self.watchLocation()
            } else {
                result(FlutterMethodNotImplemented)
            }
        })
        // ここまで追加
        
        // 2) 追加
        eventChannel = FlutterEventChannel(name: "com.example.multios/locationUpdates", binaryMessenger: controller.binaryMessenger)
        eventChannel?.setStreamHandler(self)
        
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    // 位置情報取得ロジック
    private func getLocation(result: @escaping FlutterResult) {
        if CLLocationManager.authorizationStatus() == .authorizedWhenInUse || CLLocationManager.authorizationStatus() == .authorizedAlways {
            if let location = locationManager.location {
                result("\(location.coordinate.latitude),\(location.coordinate.longitude)")
            } else {
                result(FlutterError(code: "UNAVAILABLE", message: "Location not available.", details: nil))
            }
        } else {
            result(FlutterError(code: "PERMISSION_DENIED", message: "Location permission denied.", details: nil))
        }
    }
}

// 2) 追加：位置情報監視処理
extension AppDelegate {
    
    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        self.eventSink = events
        return nil
    }
    
    func onCancel(withArguments arguments: Any?) -> FlutterError? {
        locationManager.stopUpdatingLocation()
        return nil
    }
    
    private func watchLocation() {
        // 位置情報の更新を開始するロジックを実装
        locationManager.startUpdatingLocation()
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            eventSink?("\(location.coordinate.latitude),\(location.coordinate.longitude)")
        }
    }
}

```

</div></details>

```plist info.plist
MethodChannelの実装例と同様の設定
```

＜Android＞

<details><summary>MainActivity.kt</summary><div>

```kotlin MainActivity.kt

package com.example.multios

import android.Manifest
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import androidx.annotation.NonNull
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
//import com.example.multios.map.GoogleMapViewFactory
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel

/**
 * Android Studio内のOpen for Editing in Android Studioをクリックすることで、
 * もう一つAndroid Studio開きましょう。そうすることで補完が効くようになります。
 */
class MainActivity : FlutterActivity() {
    // 追加
    private val CHANNEL = "com.example.multios/location"

    // 2) 追加
    var locationListener: LocationListener? = null

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        // 1) 追加
        MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            CHANNEL
        ).setMethodCallHandler { call, result ->
            if (call.method == "getLocation") {
                val location = getLocation() // 実際の位置情報取得ロジックを実装
                if (location.isEmpty()) {
                    result.error("UNAVAILABLE", "Location not available.", null)
                } else {
                    result.success(location)
                }
                // 2) 追加
            } else if (call.method == "watchLocation") {
                if (watchLocation()) {
                    result.success(true)
                } else {
                    result.error("UNAVAILABLE", "Location not available.", null)
                }
            } else {
                result.notImplemented()
            }
        }

        // 2) 追加
        EventChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "com.example.multios/locationUpdates"
        ).setStreamHandler(
            object : EventChannel.StreamHandler {

                override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                    locationListener = LocationListener { location ->
                        events?.success("${location.latitude},${location.longitude}")
                    }
                }

                override fun onCancel(arguments: Any?) {
                    // 位置情報の更新を停止するロジックを実装
                }
            }
        )

        // 3) 追加
//        flutterEngine
//            .platformViewsController
//            .registry
//            .registerViewFactory("map", GoogleMapViewFactory(flutterEngine.dartExecutor.binaryMessenger))

    }

    // 1) 位置情報取得ロジック
    private fun getLocation(): String {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                1
            )
            return ""
        }

        val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
        val location: Location? = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
        return if (location == null) {
            ""
        } else {
            "${location.latitude},${location.longitude}"
        }
    }

    // 2) 位置情報の更新を開始するロジックを実装
    private fun watchLocation(): Boolean {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                1
            )
            return false
        }

        val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
        locationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER,
            10000,
            0f,
            locationListener!!
        )
        return true
    }
}

```

</div></details>

```xml AndroidManifest.xml
MethodChannelの実装例と同様の設定
```

## PlatformView

ネイティブの画面をFlutterアプリに投影する

■用途
地図（AndroidはGoogle Map、 iOSはApple標準地図）表示分けなど

＜Flutter＞

<details><summary>map_page.dart</summary><div>

```dart map_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../domain/locations/location_repository_provider.dart';
import '../../domain/locations/location.dart';
import '../../use_cases/locations/get_location_use_case.dart';
import '../../use_cases/locations/watch_location_use_case.dart';
import 'map_view.dart';

class MapPage extends StatelessWidget {
  const MapPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("真逆地図アプリ"),
      ),
      // 1) 位置情報を一度だけ取得します。
      // body: const _Body1(),
      // 2) 位置情報を常に更新できるようにします。
      body: const _Body2(),
    );
  }
}

class _Body1 extends ConsumerWidget {
  const _Body1({
    super.key,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    /// 1) 位置情報を一度だけ取得します。
    final location = ref.watch(getLocationUseCaseProvider);
    return switch (location) {
      AsyncData(:final value) => _Main(
        location: value,
      ),
      AsyncError(:final error, :final stackTrace) => Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("読み込みエラー： $error"),
            ElevatedButton(//ユーザーが位置情報を許容した後のリトライボタン
              onPressed: () {
                ref.refresh(getLocationUseCaseProvider);
              },
              child: Text('再試行'),
            ),
          ],
        ),
      ),
      _ => const Center(
        child: CircularProgressIndicator(),
      ),
    };
  }
}

class _Main extends StatefulWidget {
  final Location location;

  const _Main({Key? key, required this.location}) : super(key: key);

  @override
  _MainState createState() => _MainState();
}

class _MainState extends State<_Main> {
  // 現在表示している位置（デフォルトは現在位置）
  late Location displayedLocation;
  bool isOpposite = false; // 裏側の位置を表示しているかどうか

  @override
  void initState() {
    super.initState();
    displayedLocation = widget.location; // 初期状態では現在位置を表示
  }

  void switchLocation(bool toOpposite) {
    setState(() {
      isOpposite = toOpposite;
      if (toOpposite) {
        // 裏側位置に切り替え
        // 緯度は-90度から+90度までの値を取ります。赤道が0度、北極が+90度、南極が-90度
        // 緯度は符号を反転させるのみで逆側になります。
        double oppositeLatitude = -widget.location.latitude;
        // 経度は-180度から+180度までの値を取ります。本初子午線 を0度として、東方向に180度、西方向に-180度
        // 経度を反転させる には、 180を加算します。ただし、その値が 180度 を超えた場合 360度を引きます。
        double oppositeLongitude = widget.location.longitude + 180;
        if (oppositeLongitude > 180) {
          oppositeLongitude -= 360;
        }
        displayedLocation = Location(oppositeLatitude, oppositeLongitude);
      } else {
        // 現在位置に戻す
        displayedLocation = widget.location;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    // 現在位置と裏側位置の表示切り替え
    String locationText = isOpposite ?
    "裏側緯度：${displayedLocation.latitude.toStringAsFixed(2)} 裏側経度：${displayedLocation.longitude.toStringAsFixed(2)}" :
    "現在緯度：${widget.location.latitude.toStringAsFixed(2)} 現在経度：${widget.location.longitude.toStringAsFixed(2)}";

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Expanded(
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(locationText),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: () => switchLocation(!isOpposite),
                child: Text(isOpposite ? '現在の位置' : '裏側の位置'),
              ),
            ),
          ],
        ),
        Expanded(
          child: MapView(
            key: ValueKey(displayedLocation), // 表示位置が変わるたびにウィジェットを再構築
            latitude: displayedLocation.latitude,
            longitude: displayedLocation.longitude,
          ),
        ),
      ],
    );
  }
}


class _Body2 extends ConsumerWidget {
  const _Body2({
    super.key,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    /// 2) 位置情報を常に更新できるようにします。
    final location = ref.watch(watchLocationUseCaseProvider);



    return switch (location) {
      AsyncData(:final value) => _Main(
        location: value,
      ),
      AsyncError(:final error, :final stackTrace) => Center(
        // child: Text("読み込みエラー： $error"),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("読み込みエラー： $error"),
            ElevatedButton(//ユーザーが位置情報を許容した後のリトライボタン
              onPressed: () {
                ref.refresh(getLocationUseCaseProvider);
              },
              child: Text('再試行'),
            ),
          ],
        ),
      ),
      _ => const Center(
        child: CircularProgressIndicator(),
      ),
    };
  }
}


```

```kotlin
```

```kotlin
```
</div></details>

＜iOS＞

<details><summary>AppDelegate.swift</summary><div>

```swift AppDelegate.swift

import UIKit
import Flutter
import CoreLocation

// Android Studio内のOpen iOS/macOS module in Xcodeをクリックすることで、
// もう一つXcode開きましょう。そうすることで補完が効くようになります。
// , CLLocationManagerDelegate を追加することを忘れずに。
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, CLLocationManagerDelegate, FlutterStreamHandler {
    // 1) 追加
    private var locationChannel: FlutterMethodChannel?
    private var locationManager: CLLocationManager!
    
    // 2) 追加
    private var eventChannel: FlutterEventChannel?
    private var eventSink: FlutterEventSink?
    
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        // ここから追加
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
        
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        locationChannel = FlutterMethodChannel(name: "com.example.multios/location",
                                               binaryMessenger: controller.binaryMessenger)
        locationChannel?.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            if call.method == "getLocation" {
                self.getLocation(result: result)
            } else if call.method == "watchLocation" {
                print("watchLocation")
                self.watchLocation()
            } else {
                result(FlutterMethodNotImplemented)
            }
        })
        // ここまで追加
        
        // 2) 追加
        eventChannel = FlutterEventChannel(name: "com.example.multios/locationUpdates", binaryMessenger: controller.binaryMessenger)
        eventChannel?.setStreamHandler(self)
        
        // 3) 追加
        let registrar = self.registrar(forPlugin: "multios")
        let factory = FlutterMapKitFactory(messenger: (registrar!.messenger()))
        self.registrar(forPlugin: "<multios>")!.register(
            factory,
            withId: "map")
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    // 位置情報取得ロジック
    private func getLocation(result: @escaping FlutterResult) {
        if CLLocationManager.authorizationStatus() == .authorizedWhenInUse || CLLocationManager.authorizationStatus() == .authorizedAlways {
            if let location = locationManager.location {
                result("\(location.coordinate.latitude),\(location.coordinate.longitude)")
            } else {
                result(FlutterError(code: "UNAVAILABLE", message: "Location not available.", details: nil))
            }
        } else {
            result(FlutterError(code: "PERMISSION_DENIED", message: "Location permission denied.", details: nil))
        }
    }
}

// 2) 追加：位置情報監視処理
extension AppDelegate {
    
    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        self.eventSink = events
        return nil
    }
    
    func onCancel(withArguments arguments: Any?) -> FlutterError? {
        locationManager.stopUpdatingLocation()
        return nil
    }
    
    private func watchLocation() {
        // 位置情報の更新を開始するロジックを実装
        locationManager.startUpdatingLocation()
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            eventSink?("\(location.coordinate.latitude),\(location.coordinate.longitude)")
        }
    }
}

```


</div></details>

```plist info.plist
MethodChannelの実装例と同様の設定
```

＜Android＞

<details><summary>MainActivity.kt</summary><div>

```kotlin
```

</div></details>

<details><summary>map/GoogleMapView.kt</summary><div>

```kotlin
```

</div></details>

<details><summary>map/GoogleMapViewFactory.kt</summary><div>

```kotlin
```

</div></details>

```gradle android/app/build.gradle
// 末尾に↓追加
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.google.android.gms:play-services-maps:18.2.0'
}
```

```xml AndroidManifest.xml

MethodChannelの実装例と同様の設定に加えて↓

<!-- <application タグの↓に -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="Google MapのAPIキーを入れる"/>
```

Google MapのAPIキー取得は以下参照
https://developers.google.com/maps/documentation/android-sdk/get-api-key?hl=ja


## OSごとの分岐

以下の方法でOSを判別できるので、ネイティブ連携するときにつかえr
https://hiyoko-programming.com/1204/