https://d226lax1qjow5r.cloudfront.net/blog/blogposts/make-app-to-phone-call-using-ios-and-flutter/flutter_inapp-call-2_1200x600.png

App-To-Phone-Anrufe mit iOS und Flutter

Zuletzt aktualisiert am April 1, 2021

Lesedauer: 11 Minuten

Heute werden wir eine iOS Anwendung mit Flutter und nutzen das Vonage Client SDK um einen Anruf von einer mobilen Anwendung auf das Telefon zu tätigen. Vonage Gesprächs-API. Die Anwendung wird 3 Bildschirme (3 UI-Zustände) haben:

UI states: logon, make a call, and end callUI states: logon, make a call, and end call

Voraussetzungen

Der Quellcode für unsere Flutter iOS Anwendung ist verfügbar auf GitHub.

Bevor wir mit der Erstellung der Flutter Anwendung für das iOS Gerät zu erstellen, müssen wir die folgenden Voraussetzungen erfüllen:

  • Erstellen Sie ein Call Control Object (NCCO)

  • Installieren Sie die Vonage CLI (zuvor Nexmo CLI)

  • Einrichten der Vonage application

  • Installieren Sie die Flutter SDK

  • Erstellen Sie das Flutter Projekt

Vonage-Anwendung

Ein NCCO erstellen

A Anrufsteuerungsobjekt (NCCO) ist ein JSON Array, das wir zur Steuerung des Ablaufs einer Voice API call.

Die NCCO muss öffentlich und über das Internet zugänglich sein. Um dies zu erreichen, werden wir in diesem Tutorial Folgendes verwenden GitHub Gist das eine bequeme Möglichkeit bietet, die Konfiguration zu hosten. Fügen wir einen neuen Gist hinzu:

  1. Besuchen Sie https://gist.github.com/ (wir müssen bei Github angemeldet sein)

  2. Erstellen Sie einen neuen Gist mit ncco.json als Dateiname

  3. Kopieren und fügen Sie das folgende JSON Objekt in den Gist ein:

[
    {
        "action": "talk",
        "text": "Please wait while we connect you."
    },
    {
        "action": "connect",
        "endpoint": [
            {
                "type": "phone",
                "number": "PHONE_NUMBER"
            }
        ]
    }
]
  1. Ersetzen Sie PHONE_NUMBER durch Ihre Rufnummer (Vonage-Nummern sind im E.164-Format, + und - sind nicht gültig. Stellen Sie sicher, dass Sie bei der Eingabe der Nummer die Landesvorwahl angeben, z. B. US: 14155550100 und UK: 447700900001)

  2. Klicken Sie auf die Create secret gist Schaltfläche

  3. Klicken Sie auf die Raw Schaltfläche

  4. Notieren Sie sich die im Browser angezeigte URL, wir werden sie im nächsten Schritt verwenden

Vonage CLI installieren

Die Vonage CLI ermöglicht es uns, viele Vorgänge über die Befehlszeile auszuführen. Wenn wir Aufgaben wie das Erstellen von Anwendungen, das Anlegen von Gesprächen, den Kauf von Vonage-Nummern usw. ausführen möchten, müssen wir die Vonage CLI installieren.

Vonage CLI erfordert Node.jsalso müssen wir zuerst Node.js installieren.

Um die Beta-Version des CLIs mit npm zu installieren, führen Sie diesen Befehl aus:

npm install nexmo-cli@beta -g

Richten Sie die Vonage CLI zur Verwendung der Vonage API Key und API Secret. Diese können wir auf der Einstellungsseite im Dashboard.

Führen Sie den folgenden Befehl im Terminal aus und ersetzen Sie dabei API_KEY und API_SECRET durch Werte aus der Datei Dashboard:

nexmo setup API_KEY API_SECRET

Vonage-Anwendung einrichten

  1. Erstellen Sie das Projektverzeichnis. Führen Sie den folgenden Befehl im Terminal aus:

mkdir vonage-tutorial
  1. Wechseln Sie in das Projektverzeichnis:

cd vonage-tutorial
  1. Erstellen Sie eine Vonage-Anwendung, indem Sie den unten stehenden Befehl kopieren und in das Terminal einfügen. Vergewissern Sie sich, dass Sie den Wert von --voice-answer-url Argument durch Ersetzen von GIST-URL durch die Gist-URL aus dem vorherigen Schritt ersetzen.

nexmo app:create "App to Phone Tutorial" --capabilities=voice --keyfile=private.key --voice-event-url=https://example.com/ --voice-answer-url=GIST-URL

Notieren Sie sich die Application ID die bei der Erstellung der Anwendung im Terminal ausgegeben wird.

HINWEIS: Eine versteckte Datei namens .nexmo-app wird im Projektverzeichnis erstellt und enthält den neu erstellten Vonage Application ID und den privaten Schlüssel. Eine private Schlüsseldatei mit dem Namen private.key wird ebenfalls im aktuellen Ordner erstellt.

Benutzer erstellen

Jeder Teilnehmer wird durch einen Benutzer Objekt repräsentiert und muss durch das Client SDK. In einer Produktionsanwendung würden wir diese Benutzerinformationen normalerweise in einer Datenbank speichern.

Führen Sie den folgenden Befehl aus, um einen Benutzer namens Alice:

nexmo user:create name="Alice"

JWT generieren

Die JWT wird zur Authentifizierung des Benutzers verwendet. Führen Sie den folgenden Befehl im Terminal aus, um eine JWT für den Benutzer Alice. Ersetzen Sie im folgenden Befehl die APPLICATION_ID durch die ID der Anwendung:

nexmo jwt:generate sub=Alice exp=$(($(date +%s)+86400)) acl='{"paths":{"/*/users/**":{},"/*/conversations/**":{},"/*/sessions/**":{},"/*/devices/**":{},"/*/image/**":{},"/*/media/**":{},"/*/push/**":{},"/*/knocking/**":{},"/*/legs/**":{}}}' application_id=APPLICATION_ID

Mit dem obigen Befehl wird der Ablauf der JWT auf einen Tag ab jetzt, was das Maximum ist.

Notieren Sie sich die JWT die wir für Alice.

HINWEIS: In einer Produktionsumgebung sollte die Anwendung einen Endpunkt bereitstellen, der für jede Client-Anfrage eine JWT für jede Client-Anfrage erzeugt.

Xcode installieren

AppStore öffnen und installieren Xcode.

Flatter-Einrichtung

Flutter SDK installieren

Herunterladen und installieren Flutter SDK.

Dieser Schritt variiert je nach MacOS, Win, und Linuxvariieren, aber im Allgemeinen läuft es auf das Herunterladen von Flutter SDK für ein bestimmtes Betriebssystem herunter, extrahiert die Flutter SDK Datei zu extrahieren und den sdk\bin Ordner zur System PATH Variable hinzuzufügen. Eine detaillierte Anleitung für alle Plattformen finden Sie hier.

Zum Glück, Flutter ein Werkzeug zur Verfügung, mit dem wir überprüfen können, ob SDK und alle erforderlichen "Komponenten" vorhanden und korrekt konfiguriert sind. Führen Sie diesen Befehl aus:

flutter doctor

Flutter Doctor prüft, ob Flutter SDK installiert ist und andere Komponenten korrekt installiert und konfiguriert sind.

Flutter-Projekt erstellen

Wir erstellen ein Flutter Projekt mit Hilfe des Terminals:

flutter create app_to_phone_flutter

Der obige Befehl erstellt app_to_phone_flutter Ordner, der das Flutter Projekt.

Flutter Projekt enthält ios Ordner, der das iOS Projekt; android Ordner, der das Android Projekt enthält; und web Ordner, der das web Projekt.

Öffnen Sie die pubspec.yaml Datei, und fügen Sie permission_handler Abhängigkeit (direkt unter sdk: flutter):

dependencies:
  flutter:
    sdk: flutter
  
  permission_handler: ^6.0.1+1

Die Einrückung ist wichtig in yaml Dateien wichtig, stellen Sie also sicher, dass permission_handler auf der gleichen Einrückungsebene liegt wie der flutter: Element.

Führen Sie nun diesen Befehl aus (der Pfad ist das Stammverzeichnis des Flutter Projekts), um die oben genannte Abhängigkeit herunterzuladen:

flutter pub get

Der obige Befehl erstellt auch Podfile in ios Unterordner. Öffnen Sie ios\Podfile unkommentiert platform Zeile und aktualisieren Sie die Plattformversion auf 11:

platform :ios, '11.0'

Am Ende der gleichen Datei hinzufügen pod 'NexmoClient':

target 'Runner' do
  use_frameworks!
  use_modular_headers!
  pod 'NexmoClient'

Öffnen Sie app_to_phone_flutter/ios Ordner im Terminal und installieren Sie Pods:

pod install

Mit dem obigen Befehl werden alle erforderlichen Abhängigkeiten heruntergeladen, einschließlich Flutter, Berechtigungshandler, und Client SDK.

Öffnen Sie Runner.xcworkspace in Xcode und führen Sie die Anwendung aus, um zu überprüfen, ob die obige Einrichtung korrekt durchgeführt wurde.

Zwei-Wege-Flutter/iOS-Kommunikation

Derzeit, Client SDK ist nicht als Flutter Paket verfügbar, daher müssen wir das Android natives Client SDK verwenden und die Kommunikation zwischen iOS und Flutter über Methoden-Kanal - auf diese Weise, Flutter werden Android-Methoden aufgerufen, iOS ruft Flutter Methoden.

Der Flutter-Code wird in der Datei lib/main.dart Datei gespeichert, während iOS nativer Code wird in der Datei ios/Runner/AppDelegate.swift Datei gespeichert wird.

Init Flutter Anwendung

Flutter-Anwendungen werden mit einer Programmiersprache namens Dart.

Öffnen Sie die Datei lib/main.dart Datei und ersetzen Sie den gesamten Inhalt durch den folgenden Code:

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: CallWidget(title: 'app-to-phone-flutter'),
    );
  }
}

class CallWidget extends StatefulWidget {
  CallWidget({Key key, this.title}) : super(key: key);
  final String title;

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

class _CallWidgetState extends State<CallWidget> {
  SdkState _sdkState = SdkState.LOGGED_OUT;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SizedBox(height: 64),
            _updateView()
          ],
        ),
      ),
    );
  }

  Widget _updateView() {
    if (_sdkState == SdkState.LOGGED_OUT) {
      return ElevatedButton(
          child: Text("LOGIN AS ALICE")
      );
    }
  }

  Future<void> _loginUser() async {
      // Login user
  }

  Future<void> _makeCall() async {
      // Make call
  }

  Future<void> _endCall() async {
      // End call
  }
}

enum SdkState {
  LOGGED_OUT,
  LOGGED_IN,
  WAIT,
  ON_CALL,
  ERROR
}

Der obige Code enthält eine benutzerdefinierte CallWidget die für die Verwaltung des Anwendungsstatus (Protokollierung des Benutzers und Verwaltung des Anrufs) zuständig ist. Die SdkState enum repräsentiert mögliche Zustände von Vonage Client SDK. Dieses Enum wird zweimal definiert - einmal für Flutter mit Dart und eine für iOS Verwendung von Swift. Das Widget enthält die _updateView Methode, die das UI basierend auf dem SdkState Wert.

Führen Sie die Anwendung vom Xcode:

Running the application from xcodeRunning the application from xcode

Die Schaltfläche Login as Alice Schaltfläche sollte angezeigt werden:

Logged out screen showing Login as Alice buttonLogged out screen showing Login as Alice button

Anmeldebildschirm

Die Schaltfläche Login as Alice Schaltfläche ist deaktiviert, also fügen Sie jetzt onPressed Handler zu der ElevatedButton um die Anmeldung zu ermöglichen:

Widget _updateView() {
    if (_sdkState == SdkState.LOGGED_OUT) {
      return ElevatedButton(
          onPressed: () { _loginUser(); },
          child: Text("LOGIN AS ALICE")
      );
    }
  }

Aktualisieren Sie den Körper der _loginUser Methode, um mit dem nativen Code zu kommunizieren und den Benutzer anzumelden:

Future<void> _loginUser() async {
    String token = "ALICE_TOKEN";

    try {
      await platformMethodChannel.invokeMethod('loginUser', <String, dynamic>{'token': token});
    } on PlatformException catch (e) {
      print(e);
    }
  }

Ersetzen Sie die ALICE_TOKEN durch das JWT Token, das wir zuvor von Vonage CLI zur Authentifizierung des Benutzers Alice für den Zugriff auf die Konversation zu authentifizieren. Flutter ruft die loginUser Methode auf und übergibt die token als Argument übergeben. Die Methode loginUser Methode ist definiert in der MainActivity Klasse definiert (dazu kommen wir gleich noch). Um diese Methode von Flutter aufzurufen, müssen wir eine MethodChannel. hinzufügen. platformMethodChannel Feld am Anfang der _CallWidgetState Klasse:

hinzufügen platformMethodChannel Feld am Anfang von _CallWidgetState Klasse:

class _CallWidgetState extends State<CallWidget> {
  SdkState _sdkState = SdkState.LOGGED_OUT;
  static const platformMethodChannel = const MethodChannel('com.vonage');

Die com.vonage steht für die eindeutige Kanal-ID, die wir auch dem nativen iOS Code (AppDelegate Klasse). Nun müssen wir diesen Methodenaufruf auf der nativen iOS Seite behandeln.

Öffnen Sie ios/Runner/AppDelegate Klasse und vonageChannel Eigenschaft, die den Verweis auf die FlutterMethodChannel:

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  var vonageChannel: FlutterMethodChannel?
    
...

Um auf Methodenaufrufe zu warten, die von Flutter hinzufügen addFlutterChannelListener Methode innerhalb der AppDelegate Klasse (gleiche Ebene wie obige application Methode):

func addFlutterChannelListener() {
        let controller = window?.rootViewController as! FlutterViewController
        
        vonageChannel = FlutterMethodChannel(name: "com.vonage",
                                             binaryMessenger: controller.binaryMessenger)
        vonageChannel?.setMethodCallHandler({ [weak self]
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            guard let self = self else { return }
            
            switch(call.method) {
            case "loginUser":
                if let arguments = call.arguments as? [String: String],
                   let token = arguments["token"] {
                    self.loginUser(token: token)
                }
                result("")
            default:
                result(FlutterMethodNotImplemented)
            }
        })
    }

Die obige Methode "übersetzt" die Flutter Methodenaufrufe in Methoden, die in der AppDelegate Klasse (die loginUser für jetzt).

Und es fehlen die loginUser Methoden innerhalb derselben Klasse (wir werden den Körper bald füllen):

func loginUser(token: String) {

}

Jetzt hinzufügen addFlutterChannelListener Methodenaufruf innerhalb der application Methode ein:

override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        addFlutterChannelListener()
        
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

Der Code ist vorhanden - nach dem Drücken des Login As Alice Schaltfläche ruft die Flutter-App die _loginUser Methode auf. Über den Flutter Plattformkanal ruft die Methode die loginUser Methode auf, die in der AppDelegate Klasse definiert ist.

Führen Sie die Anwendung von Xcode um sicherzustellen, dass sie kompiliert wird.

Bevor wir uns als Benutzer anmelden können, müssen wir die Initialisierung der Vonage SDK Client.

Client initialisieren

Öffnen Sie AppDelegate Klasse und fügen Sie den NexmoClient Import am Anfang der Datei hinzu:

import NexmoClient

In der gleichen Datei fügen Sie client Eigenschaft, die einen Verweis auf Vonage Client.

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    var vonageChannel: FlutterMethodChannel?
    let client = NXMClient.shared

...

Jetzt hinzufügen initClient Methode hinzu, um den Client zu initialisieren:

func initClient() {
        client.setDelegate(self)
    }

Zum Aufruf der initClient Methode von der bestehenden application Methode aufzurufen, müssen wir die initClient() Zeile hinzufügen, wie im folgenden Beispiel gezeigt:

override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        initClient()
        addFlutterChannelListener()
        
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

Bevor wir eine Konversation zulassen, müssen wir wissen, dass der Benutzer korrekt angemeldet ist. In der AppDelegate Datei einen Delegaten hinzufügen, der auf Vonage Client SDK Änderungen des Verbindungsstatus:

extension AppDelegate: NXMClientDelegate {
    func client(_ client: NXMClient, didChange status: NXMConnectionStatus, reason: NXMConnectionStatusReason) {
        switch status {
        case .connected:
            notifyFlutter(state: .loggedIn)
        case .disconnected:
            notifyFlutter(state: .loggedOut)
        case .connecting:
            notifyFlutter(state: .wait)
        @unknown default:
            notifyFlutter(state: .error)
        }
    }
}

Schließlich muss die notifyFlutter Methode zur gleichen Klasse hinzugefügt werden:

func client(_ client: NXMClient, didReceiveError error: Error) {
        notifyFlutter(state: .error)
    }
}

Anmeldung des Benutzers

Ändern Sie loginUser Methodenkörper zum Aufruf login auf der Client-Instanz aufzurufen:

func loginUser(token: String) {
        self.client.login(withAuthToken: token)
    }

Diese Methode ermöglicht es uns, den Benutzer (Alice) unter Verwendung der Client SDK auf die Konversation zuzugreifen.

Flutter über Client SDK Statusänderung benachrichtigen

Zur Mitteilung Flutter über alle Änderungen des Zustands in der Client SDKzu benachrichtigen, müssen wir ein enum hinzufügen, das die Zustände der Client SDK. Wir haben bereits das entsprechende SdkState Aufzählung in der main.dart Datei hinzugefügt. Fügen Sie die folgende SdkState enum, am Ende der Datei MainActivity.kt Datei:

enum SdkState: String {
        case loggedOut = "LOGGED_OUT"
        case loggedIn = "LOGGED_IN"
        case wait = "WAIT"
        case onCall = "ON_CALL"
        case error = "ERROR"
    }

Um diese Zustände an Flutter (vom obigen Delegaten) zu senden, müssen wir die notifyFlutter Methode in der AppDelegate Klasse hinzufügen:

func notifyFlutter(state: SdkState) {
        vonageChannel?.invokeMethod("updateState", arguments: state.rawValue)
    }

Beachten Sie, dass wir den Status in der Aufzählung speichern, ihn aber als String senden.

SDK-Status-Update durch Flutter abrufen

Zum Abrufen von Statusaktualisierungen in Flutter abzurufen, müssen wir auf Aktualisierungen des Methodenkanals warten. Öffnen Sie main.dart Datei und fügen Sie _CallWidgetState Konstruktor mit benutzerdefiniertem Handler:

_CallWidgetState() {
    platformMethodChannel.setMethodCallHandler(methodCallHandler);
  }

Innerhalb der gleichen Klasse (_CallWidgetState) fügen Sie die Handler-Methode hinzu:

Future<dynamic> methodCallHandler(MethodCall methodCall) async {
    switch (methodCall.method) {
      case 'updateState':
        {
          setState(() {
            var arguments = 'SdkState.${methodCall.arguments}';
            _sdkState = SdkState.values.firstWhere((v) {return v.toString() == arguments;}
            );
          });
        }
        break;
      default:
        throw MissingPluginException('notImplemented');
    }
  }

Diese Methoden empfangen das "Signal" von Android und wandeln es in ein Enum um. Aktualisieren Sie nun den Inhalt der _updateView Methode zur Unterstützung von SdkState.WAIT und SdkState.LOGGED_IN Zustände, wie im folgenden Beispiel gezeigt:

Widget _updateView() {
    if (_sdkState == SdkState.LOGGED_OUT) {
      return ElevatedButton(
          onPressed: () { _loginUser(); },
          child: Text("LOGIN AS ALICE")
      );
    }  else if (_sdkState == SdkState.WAIT) {
      return Center(
        child: CircularProgressIndicator(),
      );
    } else if (_sdkState == SdkState.LOGGED_IN) {
      return ElevatedButton(
          onPressed: () { _makeCall(); },
          child: Text("MAKE PHONE CALL")
      );
    }
  }

Während SdkState.WAIT wird der Fortschrittsbalken angezeigt. Nach erfolgreicher Anmeldung zeigt die Anwendung die MAKE PHONE CALL Schaltfläche.

Starten Sie die Anwendung und klicken Sie auf die Schaltfläche mit der Aufschrift LOGIN AS ALICE. Die Schaltfläche MAKE PHONE CALL Schaltfläche sollte erscheinen, die einen anderen Zustand der Flutter App basierend auf der SdkState enum`). Ein Beispiel hierfür ist im folgenden Bild zu sehen:

Make a phone call UI stateMake a phone call UI state

Anrufen

Jetzt müssen wir eine Funktion zum Telefonieren hinzufügen. Öffnen Sie die main.dart Datei und aktualisieren Sie den Körper der _makeCall Methode wie unten gezeigt:

Future<void> _makeCall() async {
    try {
      await platformMethodChannel
          .invokeMethod('makeCall');
    } on PlatformException catch (e) {
      print(e);
    }
  }

Die obige Methode kommuniziert mit iOS kommunizieren, also müssen wir den Code in der AppDelegate Klasse ebenfalls aktualisieren. Fügen Sie makeCall Klauseln zu der switch Anweisung innerhalb der addFlutterChannelListener Methode:

func addFlutterChannelListener() {
        let controller = window?.rootViewController as! FlutterViewController
        
        vonageChannel = FlutterMethodChannel(name: "com.vonage",
                                             binaryMessenger: controller.binaryMessenger)
        vonageChannel?.setMethodCallHandler({ [weak self]
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            guard let self = self else { return }
            
            switch(call.method) {
            case "loginUser":
                if let arguments = call.arguments as? [String: String],
                   let token = arguments["token"] {
                    self.loginUser(token: token)
                }
                result("")
            case "makeCall":
                self.makeCall()
                result("")
            default:
                result(FlutterMethodNotImplemented)
            }
        })
    }

Fügen Sie nun in der gleichen Datei die Eigenschaft onGoingCall hinzu, die definiert, ob und wann ein Anruf läuft:

var onGoingCall: NXMCall?

HINWEIS: Derzeit speichert die Client SDK keine laufende Aufrufreferenz, daher müssen wir sie in der AppDelegate Klasse speichern. Wir werden sie später zum Beenden des Anrufs verwenden.

Fügen Sie nun in der gleichen Klasse makeCall Methode:

func makeCall() {
        client.call("IGNORED_NUMBER", callHandler: .server) { [weak self] (error, call) in
            guard let self = self else { return }
            
            if error != nil {
                self.notifyFlutter(state: .error)
                return
            }
            
            self.onGoingCall = call
            self.notifyFlutter(state: .onCall)
        }
    }

Die obige Methode setzt den Status der Flutter App auf SdkState.WAIT und wartet auf die Client SDK Antwort (Fehler oder Erfolg). Jetzt müssen wir die Unterstützung für beide Zustände hinzufügen (SdkState.ON_CALL und SdkState.ERROR) innerhalb der main.dart Datei hinzufügen. Aktualisieren Sie den Körper der _updateView Methode, um das Gleiche wie unten zu zeigen:

Widget _updateView() {
    if (_sdkState == SdkState.LOGGED_OUT) {
      return ElevatedButton(
          onPressed: () { _loginUser(); },
          child: Text("LOGIN AS ALICE")
      );
    } else if (_sdkState == SdkState.WAIT) {
      return Center(
        child: CircularProgressIndicator(),
      );
    } else if (_sdkState == SdkState.LOGGED_IN) {
      return ElevatedButton(
          onPressed: () { _makeCall(); },
          child: Text("MAKE PHONE CALL")
      );
    } else if (_sdkState == SdkState.ON_CALL) {
      return ElevatedButton(
          onPressed: () { _endCall(); },
          child: Text("END CALL")
      );
    } else {
      return Center(
        child: Text("ERROR")
      );
    }
  }

Jede Zustandsänderung führt zu einer Änderung der Benutzeroberfläche. Bevor ein Anruf getätigt wird, benötigt die Anwendung bestimmte Berechtigungen für die Nutzung des Mikrofons. Im nächsten Schritt werden wir die Funktionalität in das Projekt aufnehmen, um diese Berechtigungen anzufordern.

Berechtigungen anfordern

Die Anwendung muss auf das Mikrofon zugreifen können, also müssen wir den Zugriff auf das Mikrofon anfordern (Permission.microphone für Flutter ).

Öffnen Sie ios/Runner/info.plist Datei und fügen Sie Privacy - Microphone Usage Description Schlüssel mit Make a call Wert:

Setting add microphone permissionSetting add microphone permission

Wir haben bereits den permission_handler Paket zum Flutter Projekt hinzugefügt. Jetzt müssen wir am Anfang der main.dart Datei müssen wir das permission_handler Paket importieren, wie im folgenden Beispiel gezeigt:

import 'package:permission_handler/permission_handler.dart';

Um die Anfrage für bestimmte Berechtigungen auszulösen, müssen wir die requestPermissions() Methode innerhalb der _CallWidgetState Klasse innerhalb der main.dart Datei hinzufügen. Fügen Sie also diese neue Methode innerhalb der Klasse hinzu:

Future<void> requestPermissions() async {
    await [ Permission.microphone ].request();
  }

Die obige Methode wird die Berechtigungen mit permission_handler.

Ändern Sie in derselben Klasse den Körper der Klasse _makeCall Klasse so, dass vor dem Aufruf der Methode über den Methodenkanal Berechtigungen angefordert werden:

Future<void> _makeCall() async {
    try {
      await requestPermissions();
 
      ...
  }

Starten Sie die App und klicken Sie auf MAKE PHONE CALL um einen Anruf zu starten. Der Berechtigungsdialog wird angezeigt, und nachdem Sie die Berechtigungen erteilt haben, wird der Anruf gestartet.

Zur Erinnerung: Wir haben die Rufnummer bereits in der NCCO

Der Status der Anwendung wird aktualisiert auf SdkState.ON_CALL und die Benutzeroberfläche wird aktualisiert:

On call UIOn call UI

Anruf beenden

Um den Aufruf zu beenden, müssen wir die Methode in der nativen iOS Anwendung mit platformMethodChannel. Innerhalb der main.dart Datei aktualisieren wir den Körper der _endCall Methode:

Future<void> _endCall() async {
    try {
      await platformMethodChannel.invokeMethod('endCall');
    } on PlatformException catch (e) {}
  }

Die obige Methode kommuniziert mit iOSkommunizieren, also müssen wir den Code in der AppDelegate Klasse ebenfalls aktualisieren. Fügen Sie endCall Klauseln zu der switch Anweisung innerhalb der addFlutterChannelListener Methode:

func addFlutterChannelListener() {
        let controller = window?.rootViewController as! FlutterViewController
        
        vonageChannel = FlutterMethodChannel(name: "com.vonage",
                                             binaryMessenger: controller.binaryMessenger)
        vonageChannel?.setMethodCallHandler({ [weak self]
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            guard let self = self else { return }
            
            switch(call.method) {
            case "loginUser":
                if let arguments = call.arguments as? [String: String],
                   let token = arguments["token"] {
                    self.loginUser(token: token)
                }
                result("")
            case "makeCall":
                self.makeCall()
                result("")
            case "endCall":
                self.endCall()
                result("")
            default:
                result(FlutterMethodNotImplemented)
            }
        })
    }

Fügen Sie nun in der gleichen Klasse die endCall Methode hinzu:

func endCall() {
        onGoingCall?.hangup()
        onGoingCall = nil
        notifyFlutter(state: .loggedIn)
    }

Die obige Methode setzt den Status der Flutter App auf SdkState.WAIT und wartet auf die Antwort von Client SDKdie entweder ein Fehler oder ein Erfolg sein kann. Beide UI-Zustände werden bereits in der Flutter Anwendung (_updateView Methode).

Wir haben das Beenden des Anrufs durch Drücken der END CALL Schaltfläche in der Flutter Anwendungs-UI. Der Anruf kann jedoch auch außerhalb der Anwendung beendet werden. Flutter App beendet werden, z. B. indem der Anruf abgewiesen oder angenommen und später vom Anrufer (auf dem echten Telefon) beendet wird.

Um diese Fälle zu unterstützen, müssen wir den NexmoCallEventListener Listener zur Anrufinstanz hinzufügen und auf anrufspezifische Ereignisse warten.

In der Datei AppDelegares.swift Datei hinzufügen NXMCallDelegate:

extension AppDelegate: NXMCallDelegate {
    func call(_ call: NXMCall, didUpdate callMember: NXMCallMember, with status: NXMCallMemberStatus) {
        if (status == .completed || status == .cancelled) {
            onGoingCall = nil
            notifyFlutter(state: .loggedIn)
        }
    }
    
    func call(_ call: NXMCall, didUpdate callMember: NXMCallMember, isMuted muted: Bool) {
        
    }
    
    func call(_ call: NXMCall, didReceive error: Error) {
        notifyFlutter(state: .error)
    }
}

Um den obigen Hörer zu registrieren, ändern Sie onSuccess Callback innerhalb der makeCall Methode:

func makeCall() {
        client.call("IGNORED_NUMBER", callHandler: .server) { [weak self] (error, call) in
            guard let self = self else { return }
            
            if error != nil {
                self.notifyFlutter(state: .error)
                return
            }
            
            self.onGoingCall = call
            self.onGoingCall?.setDelegate(self)
            self.notifyFlutter(state: .onCall)
        }
    }

Führen Sie die App aus und rufen Sie von der mobilen Anwendung aus eine physische Telefonnummer an.

Zusammenfassung

Wir haben die Anwendung erfolgreich erstellt. Dabei haben wir gelernt, wie man einen Anruf von einer mobilen Anwendung auf das Telefon mit Vonage tätigt Client SDK. Das komplette Projekt finden Sie auf GitHub. Dieses Projekt enthält zusätzlich den nativen Android-Code (android Ordner), der es uns ermöglicht, die App auch auf Android laufen zu lassen.

Um sich mit anderen Funktionalitäten vertraut zu machen, lesen Sie bitte andere Tutorials und Vonage Entwicklerzentrum.

Referenzen

Share:

https://a.storyblok.com/f/270183/384x384/8ae5af43bb/igor-wojda.png
Igor WojdaVonage Ehemalige