Mobilutveckling med Flutter · Lektion

Skriv anpassade plattformspluginer för iOS och Android

Skriv federerade pluginer som exponerar inbyggda Kotlin- och Swift-API:er för Dart.

Lektion 3 av 413 steg

Skriv anpassade plattformspluginer för iOS och Android är en gratis lektion i Mobilutveckling med Flutter på CoddyKit. Detta är lektion 3 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Mobilutveckling med Flutter, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.

Varför plattformsplugin?

Dart kan inte anropa native iOS- eller Android-API:er direkt. Ett plattformsplugin överbryggar detta avstånd genom att exponera funktioner i Swift/Objective-C och Kotlin/Java för er Dart-kod via en typad kanal.

  • Package: ren Dart-kod, ingen native kod.
  • Plugin: Dart-API samt plattformsspecifika native-implementationer.

Ni skriver ett plugin när ni behöver hårdvara (sensorer, BLE), operativsystemstjänster (notiser, nyckelring) eller ett native SDK som saknar en motsvarighet i Dart. I den här lektionen skapar vi ett federerat plugin som exponerar native API:er i Kotlin och Swift för Dart.

Arkitektur för federerade plugin

Ett federerat plugin delar upp ansvaret mellan flera paket, så att olika parter kan ansvara för olika plattformar:

  • paket mot appen (battery): det offentliga Dart-API som utvecklare importerar.
  • plattformsgränssnitt (battery_platform_interface): ett abstrakt kontrakt som alla implementationer måste uppfylla.
  • plattformspaket (battery_android, battery_ios): konkreta native-implementationer som registreras via pubspec.yaml.

Denna frikoppling gör att en tredje part kan lägga till exempelvis en Windows-implementation utan att ändra paketet mot appen. Gränssnittspaketet är det centrala kontraktet.

Kontraktet för plattformsgränssnittet

Plattformsgränssnittet använder paketet plugin_platform_interface. Det deklarerar en abstrakt basklass med ett kontrakt utan MethodChannel, samt en standardinstans som andra paket kan ersätta.

PlatformInterfaces tokenverifiering förhindrar att implementerare använder extends och bryter kontraktet; de måste använda implements, skyddat av verifyToken.

import 'package:plugin_platform_interface/plugin_platform_interface.dart';

abstract class BatteryPlatform extends PlatformInterface {
  BatteryPlatform() : super(token: _token);

  static final Object _token = Object();
  static BatteryPlatform _instance = MethodChannelBattery();

  static BatteryPlatform get instance => _instance;

  static set instance(BatteryPlatform value) {
    PlatformInterface.verifyToken(value, _token);
    _instance = value;
  }

  Future<int> getBatteryLevel() {
    throw UnimplementedError('getBatteryLevel() is not implemented.');
  }
}

MethodChannel: standardimplementationen

Standardimplementationen kommunicerar med native kod via en MethodChannel. Varje kanal har ett unikt namn som delas av Dart-sidan och den native sidan. invokeMethod serialiserar argumenten med den standardiserade meddelandekodaren och väntar på ett svar från native kod.

Använd namnområden för kanalnamn (omvänd DNS) för att undvika kollisioner mellan plugin.

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

class MethodChannelBattery extends BatteryPlatform {
  final MethodChannel _channel =
      const MethodChannel('com.example.battery/methods');

  @override
  Future<int> getBatteryLevel() async {
    final level = await _channel.invokeMethod<int>('getBatteryLevel');
    if (level == null) {
      throw PlatformException(code: 'NO_LEVEL', message: 'Level unavailable');
    }
    return level;
  }
}

Android: registrering av Kotlin-plugin

På Android implementerar den native sidan FlutterPlugin och MethodChannel.MethodCallHandler. I onAttachedToEngine kopplar ni en MethodChannel med samma namn som används i Dart och dirigerar sedan inkommande anrop i onMethodCall.

  • result.success(value) slutför Dart-Future:n.
  • result.error(code, msg, details) kastar en PlatformException i Dart.
  • result.notImplemented() signalerar en okänd metod.

Detta är Kotlin, inte Dart, och fungerar därför endast som illustration.

iOS: registrering av Swift-plugin

På iOS följer ni FlutterPlugin-protokollet och registrerar pluginet i register(with:) genom att skapa en FlutterMethodChannel med motsvarande namn. handle(_:result:) dirigerar anrop och svarar via callbacken FlutterResult.

För federerade plugin deklarerar plattformspaketet sin native startpunkt under flutter.plugin.platforms.ios i pubspec.yaml och pekar på Swift-klassen. Dart-registreringen kopplas in automatiskt.

Deklarera den federerade pubspec-filen

Plattformsimplementationerna tillkännager sig via flutter.plugin.platforms. dartPluginClass registreras med plattformsgränssnittet vid start, och pluginClass anger namnet på den native klassen.

Det viktiga är att paketet mot appen listar varje plattformspaket under default_package, så att en ny plattform läggs till genom en ändring i pubspec-filen, inte genom en kodändring.

Godkänna plattformsimplementationer

Paketet mot appen godkänner implementationer genom att ange beroenden till dem i sin pubspec.yaml. Godkända paket hämtas transitivt, så apputvecklare behöver bara lägga till ett beroende och får alla plattformar.

Vid körning anger varje plattformspakets registerWith() dess egen implementation som gränssnittets instance. Dart-API:t anropar sedan via BatteryPlatform.instance utan att känna till vilken plattform som svarade.

// battery_android registers itself at startup
import 'battery_platform_interface.dart';

class BatteryAndroid extends BatteryPlatform {
  /// Registered via dartPluginClass in pubspec.yaml.
  static void registerWith() {
    BatteryPlatform.instance = BatteryAndroid();
  }

  @override
  Future<int> getBatteryLevel() {
    // Delegates to the MethodChannel under the hood.
    return MethodChannelBattery().getBatteryLevel();
  }
}

Dart-API:t mot appen

Det offentliga paketet kapslar in gränssnittet i ett lättanvänt och väldokumenterat API. Apputvecklare behöver aldrig hantera kanaler eller plattformsklasser — de anropar tydliga Dart-metoder.

Håll detta lager tunt: validering, bekvämlighetsöverlagringar och dokumentation. Allt verkligt arbete sker bakom BatteryPlatform.instance.

import 'battery_platform_interface.dart';

class Battery {
  /// Returns the current battery level as a percentage (0-100).
  Future<int> get batteryLevel => BatteryPlatform.instance.getBatteryLevel();

  /// Convenience: true when the device is critically low.
  Future<bool> get isCritical async => (await batteryLevel) <= 15;
}

Strömma native-händelser med EventChannel

För kontinuerliga data (laddningsstatus, sensorströmmar) räcker inte ett enda metodanrop. Använd en EventChannel: den native sidan skickar händelser via en StreamHandler/FlutterEventSink, och Dart exponerar dem som en Stream.

receiveBroadcastStream startar den native lyssnaren först när den första prenumerationen görs och avslutar den när prenumerationen avbryts.

import 'package:flutter/services.dart';

class BatteryStream {
  final EventChannel _events =
      const EventChannel('com.example.battery/charging');

  Stream<bool> get onChargingChanged => _events
      .receiveBroadcastStream()
      .map((event) => event == 'charging');
}

Typsäkra kanaler med Pigeon

Handskrivna kanaler är strängbaserade och felbenägna. Pigeon genererar typsäkra bindningar för Dart, Kotlin och Swift från en enda Dart-definitionsfil, vilket eliminerar felstavningar i metodnamn och skillnader mellan codec-implementationer.

Ni annoterar en abstrakt klass med @HostApi() (Dart anropar native kod) eller @FlutterApi() (native kod anropar Dart), kör Pigeon-generatorn och kopplar in de genererade klasserna — inga manuella invokeMethod-strängar.

import 'package:pigeon/pigeon.dart';

class BatteryInfo {
  int? level;
  bool? isCharging;
}

@HostApi()
abstract class BatteryHostApi {
  BatteryInfo getBatteryInfo();
}

@FlutterApi()
abstract class BatteryFlutterApi {
  void onLevelChanged(int level);
}

Snabbkontroll

Ni publicerar ett Flutter-plugin och vill att tredje parter ska kunna lägga till nya plattformsimplementationer (till exempel Windows) utan att ändra eller skapa en fork av paketet mot appen. Vilken arkitektur och mekanism gör detta möjligt?

Sammanfattning

Ni har lärt er att skapa ett federerat plattformsplugin som exponerar native API:er i Kotlin och Swift för Dart:

  • Arkitektur: paket mot appen, plattformsgränssnitt och implementationer per plattform.
  • Kontrakt: ett abstrakt PlatformInterface med tokenverifiering och en utbytbar instance.
  • Kanaler: MethodChannel för begäran/svar och EventChannel för native-händelseströmmar, med matchande namn på båda sidor.
  • Native sidor: Kotlin onMethodCall och Swift handle(_:result:) som slutför anrop via success/error.
  • Godkännande: pubspec kopplar dartPluginClass och pluginClass så att plattformarna registrerar sig själva.
  • Pigeon: genererar typsäkra bindningar som ersätter bräckliga strängbaserade kanaler.

Med detta kan ni kapsla in valfritt native SDK bakom ett rent och testbart Dart-API.

Gratis att börja

Lär dig Dart med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
88

Vanliga frågor

Är lektionen ”Skriv anpassade plattformspluginer för iOS och Android” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Mobilutveckling med Flutter, inklusive ”Skriv anpassade plattformspluginer för iOS och Android”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.

Vad lär jag mig i ”Skriv anpassade plattformspluginer för iOS och Android”?

Skriv federerade pluginer som exponerar inbyggda Kotlin- och Swift-API:er för Dart. Ni övar på Mobilutveckling med Flutter med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Mobilutveckling med Flutter?

Du behöver inga förkunskaper. Utbildningen i Mobilutveckling med Flutter på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.

Hur lång tid tar lektionen ”Skriv anpassade plattformspluginer för iOS och Android”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Mobilutveckling med Flutter-lektionen?

Ja. Varje Mobilutveckling med Flutter-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Anropa C-bibliotek med dart:ffi
  2. Typsäkra plattformskanaler med Pigeon
  3. Skriv anpassade plattformspluginer för iOS och Android
  4. Bakgrundsisolate och hantering av inbyggt minne
← Tillbaka till Mobilutveckling med Flutter