Sobes.tech
Middle

How does Flutter adapt to the iOS operating system?

sobes.tech AI

Answer from AI

Flutter uses the Skia engine for UI rendering, which interacts directly with the GPU. Native iOS components (buttons, scrolls, etc.) are not used. Their appearance and behavior are mimicked by Flutter widgets styled according to Human Interface Guidelines.

For example, to create an iOS-style button, CupertinoButton is used:

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

class MyCupertinoButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoButton(
      child: Text('Press me'),
      onPressed: () {
        // Action on press
      },
    );
  }
}

Flutter interacts with native APIs through its plugins using platform channels. This allows calling native iOS code from Dart and vice versa.

import 'package:flutter/services.dart';

class NativeApi {
  static const platform = MethodChannel('com.example.myapp/battery'); // Channel name

  Future<String> getBatteryLevel() async {
    try {
      final String result = await platform.invokeMethod('getBatteryLevel'); // Call native method
      return 'Battery level: $result%.';
    } on PlatformException catch (e) {
      return "Failed to get battery level: '${e.message}'.";
    }
  }
}

On the iOS side, native code handles calls via MethodChannel.

// Swift code in iOS project
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let batteryChannel = FlutterMethodChannel(name: "com.example.myapp/battery",
                                              binaryMessenger: controller.binaryMessenger)
    batteryChannel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
      guard call.method == "getBatteryLevel" else {
        result(FlutterMethodNotImplemented)
        return
      }
      self.receiveBatteryLevel(result: result)
    })

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  private func receiveBatteryLevel(result: FlutterResult) {
    let device = UIDevice.current
    device.isBatteryMonitoringEnabled = true
    if device.batteryState == .unknown {
      result(FlutterError(code: "UNAVAILABLE",
                          message: "Battery level unavailable.",
                          details: nil))
    } else {
      result(Int(device.batteryLevel * 100))
    }
  }
}

For design adaptation, widgets from the cupertino library are used to mimic the iOS style. Access to specific iOS functions is also done through plugins. The app build for iOS is done via Xcode. Flutter generates an iOS project, which is then compiled into a native executable (.ipa).