11/11/2022
Flutter: Hent enhedsdetaljer med native kode
I den moderne app-udvikling er det ofte nødvendigt at kende til den specifikke enhed, som din app kører på. Dette kan være alt fra modelnavn, operativsystemversion til mere specifikke identifikatorer. Mens Flutter tilbyder pakker som device_info_plus til nemt at opnå denne information, er der situationer, hvor du ønsker at dykke dybere og implementere denne funktionalitet direkte i den native kode for enten Android eller iOS. Denne artikel vil guide dig trin for trin gennem processen med at opnå netop dette ved hjælp af Flutter's platformkanaler.

- Hvad er Platformkanaler?
- Forudsætninger
- Trin 1: Opret et nyt Flutter-projekt
- Trin 2: Opret Flutter Platform-klienten (Dart-kode)
- Trin 3: Tilføj Android Platform-specifik Implementering (Kotlin)
- Trin 4: Tilføj iOS Platform-specifik Implementering (Swift)
- Sammenligning: Native vs. Plugin
- Hvornår skal man bruge Native Kode?
- Ofte Stillede Spørgsmål (FAQ)
- Konklusion
Hvad er Platformkanaler?
Platformkanaler er Flutter's mekanisme til at kommunikere mellem din Dart-kode og den native kode på platformen. De fungerer som en bro, der tillader data at flyde i begge retninger. Dette er afgørende, når du har brug for at tilgå platformspecifikke API'er, som ikke er direkte tilgængelige fra Flutter's eget UI-toolkit.
Forudsætninger
Før vi går i gang, skal du sikre dig, at du har følgende installeret og konfigureret:
- Flutter SDK
- Android Studio (til Android-udvikling)
- Xcode (til iOS-udvikling)
Trin 1: Opret et nyt Flutter-projekt
Start med at oprette et nyt Flutter-projekt. Åbn din terminal og kør følgende kommando:
flutter create deviceinfo_native cd deviceinfo_nativeSom standard understøtter Flutter Kotlin til Android og Swift til iOS. Hvis du foretrækker Java eller Objective-C, kan du bruge flag som -i objc -a java:
flutter create -i objc -a java deviceinfo_nativeTrin 2: Opret Flutter Platform-klienten (Dart-kode)
Nu skal vi definere kommunikationskanalen i vores Dart-kode. Vi vil bruge en MethodChannel til at sende en anmodning om enhedsinformation til den native side og modtage resultatet tilbage.
Åbn filen lib/main.dart og erstat indholdet med følgende kode:
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Device Info Native', theme: ThemeData( primarySwatch: Colors.blue, ), home: const DeviceInfoScreen(), ); } } class DeviceInfoScreen extends StatefulWidget { const DeviceInfoScreen({super.key}); @override State createState() => _DeviceInfoScreenState(); } class _DeviceInfoScreenState extends State { static const platform = MethodChannel('flutter.native/helper'); String _deviceInfo = 'Henter enhedsinformation...'; Future _getDeviceInfo() async { String result; try { // Invokerer den native metode 'getDeviceInfo' final Map deviceInfoMap = await platform.invokeMethod('getDeviceInfo'); // Formaterer informationen til visning result = _formatDeviceInfo(deviceInfoMap); } on PlatformException catch (e) { result = "Kunne ikke hente enhedsinformation: ${e.message}"; print("Fejl ved hentning af enhedsinformation: ${e.message}"); } setState(() { _deviceInfo = result; }); } // Hjælpefunktion til at formatere den modtagne information String _formatDeviceInfo(Map info) { StringBuffer buffer = StringBuffer(); buffer.write('Enhedsinformation:\n\n'); info.forEach((key, value) { buffer.write('${key.toString().capitalize()}: $value\n'); }); return buffer.toString(); } @override void initState() { super.initState(); _getDeviceInfo(); // Henter informationen, når skærmen initialiseres } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Enhedsinformation'), ), body: Center( child: Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: Text(_deviceInfo), ), ), ), ); } } // Hjælpeudvidelse til at kapitalisere strenge extension StringExtension on String { String capitalize() { if (isEmpty) return ""; return this[0].toUpperCase() + substring(1); } } I denne kode:
- Vi definerer en
MethodChannelmed navnetflutter.native/helper. Dette navn skal matche det, vi bruger i den native kode. _getDeviceInfo()-metoden kalder den native metodegetDeviceInfoviaplatform.invokeMethod('getDeviceInfo').- Vi bruger en
try-catchblok til at håndtere potentiellePlatformExceptions, som kan opstå, hvis den native metode ikke er implementeret eller fejler. - Resultatet fra den native side opdaterer
_deviceInfo-variablen ved hjælp afsetState(). - I
initState()kalder vi_getDeviceInfo()for at hente data, så snart skærmen vises. - En simpel UI viser den hentede information.
Trin 3: Tilføj Android Platform-specifik Implementering (Kotlin)
Åbn dit Flutter-projekt i Android Studio. Naviger til mappen android og åbn filen app/src/main/kotlin/com/your_app_name/deviceinfo_native/MainActivity.kt.
Erstat indholdet af MainActivity.kt med følgende kode:
package com.your_app_name.deviceinfo_native import android.os.Build import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity: FlutterActivity() { private val CHANNEL = "flutter.native/helper" override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getDeviceInfo") { val deviceInfo = getDeviceInfo() if (deviceInfo.isNotEmpty()) { result.success(deviceInfo) } else { result.error("UNAVAILABLE", "Device info not available.", null) } } else { result.notImplemented() } } } private fun getDeviceInfo(): HashMap { val deviceInfo = HashMap() deviceInfo["Board"] = Build.BOARD deviceInfo["Bootloader"] = Build.BOOTLOADER deviceInfo["Brand"] = Build.BRAND deviceInfo["CPU ABI"] = Build.CPU_ABI deviceInfo["Device"] = Build.DEVICE deviceInfo["Display"] = Build.DISPLAY deviceInfo["Fingerprint"] = Build.FINGERPRINT deviceInfo["Hardware"] = Build.HARDWARE deviceInfo["Host"] = Build.HOST deviceInfo["ID"] = Build.ID deviceInfo["Manufacturer"] = Build.MANUFACTURER deviceInfo["Model"] = Build.MODEL deviceInfo["Product"] = Build.PRODUCT deviceInfo["Radio Version"] = Build.getRadioVersion() ?: "N/A" deviceInfo["SDK Int"] = Build.VERSION.SDK_INT.toString() deviceInfo["Version Release"] = Build.VERSION.RELEASE deviceInfo["Version Incremental"] = Build.VERSION.INCREMENTAL deviceInfo["Version Codename"] = Build.VERSION.CODENAME deviceInfo["Version SDK"] = Build.VERSION.SDK deviceInfo["Tags"] = Build.TAGS deviceInfo["Type"] = Build.TYPE deviceInfo["User"] = Build.USER return deviceInfo } } I denne kode:
- Vi opretter en
MethodChannelmed samme navn som i Dart-koden. setMethodCallHandlerbruges til at lytte efter kald fra Flutter.- Når metoden
getDeviceInfokaldes, kalder vi vores nativegetDeviceInfo()-funktion. getDeviceInfo()-funktionen indsamler forskellige oplysninger ved hjælp afandroid.os.Buildklassen og returnerer dem som enHashMap.- Resultatet sendes tilbage til Flutter via
result.success().
Vigtigt: For at få adgang til visse enhedsoplysninger, såsom det unikke serienummer, kan din app kræve specielle tilladelser eller opfylde bestemte krav fra Google Play. Pakken device_info_plus håndterer disse nuancer automatisk.
Trin 4: Tilføj iOS Platform-specifik Implementering (Swift)
Åbn dit Flutter-projekt i Xcode. Naviger til mappen ios og åbn filen Runner/AppDelegate.swift.
Erstat indholdet af AppDelegate.swift med følgende kode:
import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Hent FlutterViewController for at få adgang til binaryMessenger guard let controller = window?.rootViewController as? FlutterViewController else { fatalError("window.rootViewController is not FlutterViewController") } // Opret MethodChannel med samme navn som i Dart let deviceInfoChannel = FlutterMethodChannel(name: "flutter.native/helper", binaryMessenger: controller.binaryMessenger) // Sæt handler til at lytte efter kald fra Flutter deviceInfoChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in // Tjek om det er den korrekte metode, der kaldes guard call.method == "getDeviceInfo" else { result(FlutterMethodNotImplemented) return } // Kald den native funktion til at hente enhedsinformation self?.getDeviceInfo(result: result) }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } // Privat funktion til at hente enhedsinformation private func getDeviceInfo(result: @escaping FlutterResult) { let device = UIDevice.current var deviceInfo: [String: Any] = [: ] deviceInfo["Name"] = device.name deviceInfo["System Name"] = device.systemName deviceInfo["System Version"] = device.systemVersion deviceInfo["Model"] = device.model deviceInfo["Localized Model"] = device.localizedModel deviceInfo["User Interface Idiom"] = device.userInterfaceIdiom.rawValue // For at få adgang til 'identifierForVendor' på iOS 16+ kræves en specifik entitlement. // Ellers returneres en dummy-værdi. if #available(iOS 16.0, *) { // For at få adgang til enhedens navn kræves en entitlement: "com.apple.developer.device-information.user-assigned-device-name" // Uden den vil 'device.name' altid være 'iPhone' eller 'iPad'. deviceInfo["Identifier For Vendor"] = device.identifierForVendor?.uuidString ?? "N/A (Entitlement mangler?)" } else { // Fallback til ældre versioner deviceInfo["Identifier For Vendor"] = device.identifierForVendor?.uuidString ?? "N/A" } // Send resultatet tilbage til Flutter result(deviceInfo) } } I denne Swift-kode:
- Vi opretter en
FlutterMethodChannelmed det samme navn. setMethodCallHandlerregistrerer en callback, der udføres, når en metode kaldes fra Flutter.- Vi tjekker, om kaldet er til
getDeviceInfo. getDeviceInfo(result:)-funktionen brugerUIDevice.currenttil at indsamle platformspecifikke oplysninger som navn, systemversion og model.- Resultatet returneres til Flutter via
result(deviceInfo).
Bemærk: På iOS 16 og nyere kræver adgang til enhedens navn (UIDevice.current.name) en speciel entitlement kaldet com.apple.developer.device-information.user-assigned-device-name. Uden denne vil navnet altid være 'iPhone' eller 'iPad'. device_info_plus håndterer typisk dette ved at returnere en standardværdi, hvis entitlement'en mangler.

Sammenligning: Native vs. Plugin
Selvom det er lærerigt at implementere enhedsdetektering via native kode, er det værd at overveje fordele og ulemper i forhold til at bruge et plugin som device_info_plus.
| Aspekt | Native Implementering | device_info_plus Plugin |
|---|---|---|
| Kompleksitet | Højere. Kræver kendskab til native SDK'er (Kotlin/Java, Swift/Objective-C) og platformkanaler. | Lav. Kræver kun kendskab til Flutter/Dart og plugin-API'et. |
| Vedligeholdelse | Kræver opdateringer for både native kode og Dart-kode, når platformene ændres. | Plugin-udvikleren står for vedligeholdelsen. Du skal blot opdatere plugin'et. |
| Funktionalitet | Fuld kontrol. Kan tilgå alle native API'er. | Afhænger af plugin'ets implementering. Generelt dækker det de mest almindelige behov. |
| Hastighed | Kan være marginalt hurtigere, hvis det er optimeret korrekt. | Kan have et lille overhead pga. plugin-laget. |
| Platformsupport | Du skal selv implementere for hver platform. | Understøtter typisk flere platforme (Android, iOS, Web, Desktop) med én kodebase. |
Hvornår skal man bruge Native Kode?
Selvom device_info_plus er fremragende til de fleste brugsscenarier, kan der være situationer, hvor en native implementering er at foretrække:
- Avanceret Platformspecifik Logik: Når du har brug for at tilgå meget specifikke eller nyere native API'er, som endnu ikke er understøttet af et plugin.
- Performance-kritiske Operationer: I sjældne tilfælde, hvor den absolutte ydeevne er kritisk, og du kan optimere den native kode yderligere.
- Integration med Eksisterende Native Kode: Hvis du allerede har en betydelig mængde native kode i dit projekt og ønsker at udnytte den direkte.
- Læring og Forståelse: For at få en dybere forståelse af, hvordan Flutter interagerer med den underliggende platform.
Ofte Stillede Spørgsmål (FAQ)
Q1: Kan jeg få serienummeret på enheden?
A1: På Android kan du få serienummeret via Build.getSerial(), men din app skal opfylde visse krav for at få adgang til denne information. På iOS er serienummeret ikke direkte tilgængeligt af privatlivsårsager. device_info_plus returnerer typisk "unknown" for serienummeret, hvis det ikke kan opnås.
Q2: Hvad er forskellen mellem device.name og device.model på iOS?
A2: device.model giver den generelle modelbetegnelse (f.eks. "iPhone13,3"), mens device.name er det navn, brugeren selv har tildelt enheden (f.eks. "Johns iPhone"). Som nævnt kræver device.name en specifik entitlement på nyere iOS-versioner.
Q3: Hvordan sender jeg mere kompleks data tilbage fra native til Flutter?
A3: Du kan sende JSON-serialiserbare datastrukturer (som Maps og Lists) frem og tilbage mellem Dart og native kode. Sørg for, at datatyperne er kompatible.
Q4: Hvad hvis jeg vil have information om batteristatus eller netværksforbindelse?
A4: Disse oplysninger kræver også platformspecifik kode. Du kan oprette yderligere metoder på din MethodChannel for at anmode om disse data fra den native side.
Konklusion
At hente enhedsinformation programmatisk via native kode i Flutter er en kraftfuld teknik, der giver dig fuld kontrol over interaktionen med platformens API'er. Ved at mestre brugen af platformkanaler kan du udvide funktionaliteten af dine Flutter-apps ud over, hvad der er direkte tilgængeligt gennem Dart. Mens plugins som device_info_plus tilbyder en nem og hurtig løsning for de fleste tilfælde, giver denne native tilgang dig mulighed for at håndtere mere komplekse eller specialiserede scenarier. Husk altid at overveje vedligeholdelse og kompleksitet, når du vælger din implementeringsstrategi.
Hvis du vil læse andre artikler, der ligner Flutter: Hent enhedsdetaljer med native kode, kan du besøge kategorien Teknologi.
