Watch OSアプリと連携するFlutterアプリの作り方

Flutterアプリ側でボタンを押すと、Watch OSアプリ側のカウンターが増加するアプリを作成します。

ソースコードはこちら。
github.com


<----------------------宣伝------------------------->
Flutterアプリ制作・バグ修正承ります!
coconala.com <----------------------宣伝------------------------->




実行用シミュレーターの準備

Xcode->Window->Devices and Simulatorsを選択し、左下の+ボタンをクリックしてシミュレーターの新規作成ダイアログを表示する。

上記のように設定してNext

ペア設定するウォッチの設定をしてCreateする。

Flutterアプリを新規作成

flutter pub add flutter_watch_os_connectivity

を実行してflutter_watch_os_connectivityをプロジェクトに追加する。
下記のコードを入力してビルドする。(Build->Flutter->Build iOS)

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_watch_os_connectivity/flutter_watch_os_connectivity.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int counter = 0;
  final FlutterWatchOsConnectivity _flutterWatchOsConnectivity =
      FlutterWatchOsConnectivity();

  void incrementCounter() {
    counter++;
    sendMessage("$counter");
  }

  Future<void> sendMessage(String txt) async {
    bool isReachable = await _flutterWatchOsConnectivity.getReachability();
    if (isReachable) {
      await _flutterWatchOsConnectivity.sendMessage({"COUNTER": txt});
    } else {
      if (kDebugMode) {
        print("No reachable watches.");
      }
    }
  }

  @override
  void initState() {
    super.initState();
    _flutterWatchOsConnectivity.configureAndActivateSession();
    _flutterWatchOsConnectivity.activationStateChanged
        .listen((activationState) {
      if (activationState == ActivationState.activated) {
        if (kDebugMode) {
          print("activationDidCompleteWith state= ${activationState.name}");
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[
            Text(
              'Sample App',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          incrementCounter();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

ビルドは失敗するが、iOS用のコードは生成されているはずなので次へ進む。

ウォッチアプリの作成

Xcodeを開きFlutterから生成されたiOSのプロジェクトを開く。
Flutterプロジェクト->ios->Runner.xcworkspace


Xcode上でFile->New->Targetとしてダイアログを開き、watchOSタブのAppを選択してnext

任意のProduct Nameを入力して、Watch App for Existing iOS Appを選択してfinish

新しくディレクトリが作成されるので、ContentViewファィルにウォッチ側のコードを記入する。

//
//  ContentView.swift
//


import SwiftUI
import WatchConnectivity
import AVFoundation


struct ContentView: View {
    
    @ObservedObject var connector = PhoneConnector()
    
    var body: some View {
        VStack {
            VStack {
                
                Text(String(connector.counter))
                    .font(.largeTitle)
                    .foregroundColor(Color.gray)
                
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class PhoneConnector: NSObject, ObservableObject, WCSessionDelegate {
    @Published var counter = 0
    
    override init() {
        super.init()
        if WCSession.isSupported() {
            WCSession.default.delegate = self
            
            WCSession.default.activate()
        }
    }
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("activationDidCompleteWith state= \(activationState.rawValue)")
    }
    
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
        print("didReceiveMessage: \(message)")
        
        //受け取ったメッセージから解析結果を取り出す。
        let result = message["COUNTER"] as! String
        
        if let c = Int(result) {
            //結果を画面に反映
            debugPrint(c)
            DispatchQueue.main.async {
                self.counter = c
            }
        }
        
    }
}

ビルドの設定

Embedded Contentの設定

ファイルツリーのRunnerをダブルクリックして設定を表示する。

左側のツリーでRunnerを選択した状態でGeneralのFrameworks,Libraries and Embedded Contentの追加ボタンをクリック

作成済みのウォッチアプリが表示されるので選択してAdd

WKCompanionBundleIdentifierの設定

続いてCounting Watch Appを選択し、InfoタブのWKCompanionBundleIdentifierにRunnerのBundleIdentierを入力する。
RunnerのBundleIdentierは、Runner->General->Identifierから確認できる。

ウォッチアプリのBundleIDを修正する

そのままSigning & Capabilitiesタブへ移動し、Bundle Identifierを修正する。

アプリの実行

ターゲットにWatchCountingSampleを選択する。

実行環境に先ほど作成したシミュレーターを指定する。

ビルドが成功すれば、シミュレーターが起動する。

その後、ターゲットにRunnerを指定し同じシミュレーターを指定してこちらも実行すると、こちらのように両方のアプリが起動する。