This guide describes how to write custom platform-specific code.
As I read this article, I thought it would be confusing for a beginner.
In this post I took a simpler approach, for example opening the settings screen on Android and iOS from your app.
Overview
-
Scope: Android(Kotlin) and iOS(Swift 5)
-
Feature: open setting screen form your app.
iOS
Open Settings screen

Android
Open Settings screen

Flutter Channel
Create channel
All channel names used in a single app must be unique; prefix the channel name with a unique ‘domain prefix’, for example:
- Define App Id
This is application Id(Android) or bundle Id (iOS)
static const String appID = "com.example";
- Define Feature Name
featureName= "settings";
- Define Channel
final String channelName= "$appID/$featureName";
static const MethodChannel platform = MethodChannel(channelName);
Invoke a method on the method channel
Create method name
- Define method name
static const String methodOpenAppSettingScreen = "openAppSettingScreen";
- Define method invoke
invoke a method on the method channel, specifying the concrete method to call using the String identifier openAppSettingScreen.
The call might fail—for example if the platform does not support the platform API (such as when running in a simulator)—so wrap the invokeMethod call in a try-catch statement.
Future<bool> openAppSettingScreen({final Function? onError}) async {
try {
final bool result =
await platform.invokeMethod(methodOpenAppSettingScreen);
return result;
} on PlatformException catch (e) {
if (kDebugMode) {
print(e.toString());
}
return false;
}
}
The method await platform.invokeMethod(methodOpenAppSettingScreen) return data type bool.
When you calling platform-specific iOS and Android code using platform channels, you must return same data type.
Check the table shows how Dart values are received on the platform side and vice versa:
- Flutter(Dart): bool
- Android(Kotlin) : Boolean
- iOS(Swift): Bool
Add an Android(Kotlin) platform-specific implementation
- Create configure Flutter Engine
- open file
MainActivity.kt - create method
configureFlutterEngine

class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
}
}
- Add channel name and method name
The channel name and method name are must be same value as Flutter channel
companion object {
private const val APP_ID: String = "com.example"
private const val FEATURE_NAME: String = "settings"
private const val CHANNEL_NAME: String = "$APP_ID/$FEATURE_NAME"
private const val METHOD_OPEN_APP_SETTING_SCREEN: String = "openAppSettingScreen"
}
- Implement open setting screen
fun openSettingScreen(): Boolean {
val intentSetting = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
}
return try {
startActivity(intentSetting)
true
} catch (e: Exception) {
false
}
}
Remember method openSettingScreen will return Boolean type.
- Detect channel name on method
configureFlutterEngine
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NAME).setMethodCallHandler {
call, result ->
if (call.method == METHOD_OPEN_APP_SETTING_SCREEN) {
// TODO
return@setMethodCallHandler
}
}
}
- Implement method and handle result
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NAME).setMethodCallHandler {
call, result ->
if (call.method == METHOD_OPEN_APP_SETTING_SCREEN) {
result.success(openSettingScreen())
return@setMethodCallHandler
}
}
}
Add an iOS platform-specific implementation
- Create configure Flutter Engine
- open file
AppDelegate.swiftonXcode
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
- define
controller
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
- Add channel name and method name
The channel name and method name are must be same value as Flutter channel
let appID = "com.example"
let featureName = "settings"
let methodOpenAppSettingScreen = "openAppSettingScreen"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let channelName = "\(self.appID)/\(self.featureName)"
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
- Implement open setting screen
private func openSettingScreen() -> Bool {
if let url = URL(string:UIApplication.openSettingsURLString) {
if UIApplication.shared.canOpenURL(url) {
if #available(iOS 10.0, *) {
do {
try UIApplication.shared.open(url, options: [:], completionHandler: nil)
return true
} catch {
return false
}
}
return UIApplication.shared.openURL(url)
}
}
return false
}
Remember method openSettingScreen will return Bool type.
- Find Flutter channel on method
applicationand detect method name
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let channelName = "\(self.appID)/\(self.featureName)"
let settingChannel = FlutterMethodChannel(name: channelName,
binaryMessenger: controller.binaryMessenger)
settingChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if (call.method == self.methodOpenAppSettingScreen) {
// TODO
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
- Implement method and handle result
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let channelName = "\(self.appID)/\(self.featureName)"
let settingChannel = FlutterMethodChannel(name: channelName,
binaryMessenger: controller.binaryMessenger)
settingChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if (call.method == self.methodOpenAppSettingScreen) {
result(self.openSettingScreen())
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
Apply Channel Method
Future<void> _onSetting() async {
final SettingChannel settingChannel = SettingChannel();
await settingChannel.openAppSettingScreen();
}
Source code
https://github.com/ttpho/flutter-setting-app/blob/main/settings/lib/main.dart