Safely gating native OS version APIs in a custom Flutter plugin
Tests platform channel contracts and defensive native coding. Gate native calls with OS version checks, return result.error with an UNSUPPORTED code, handle PlatformException in Dart, and avoid Dart-only checks.
WHAT THIS TESTS: This question evaluates whether you understand the full contract across a Flutter MethodChannel, specifically how to write defensive native code that avoids runtime crashes on older operating systems and how to communicate three distinct outcomes, success, failure, and feature unavailable, back to Dart in a structured way. It also tests whether you know that the native side must be the final authority on API availability.
A GOOD ANSWER COVERS: A good answer hits four things in order. First, on Android, wrap the call in a check for Build.VERSION.SDK_INT >= Build.VERSION_CODES.R, which is API 30, and on iOS use if #available(iOS 15, *) in Swift or @available checks in Objective-C before calling the newer API. Second, design a result contract: if the version gate fails, return result.error with a well-defined error code such as UNSUPPORTED_OS and a clear message, or alternatively return result.success with a structured map that includes a status field so Dart can parse it consistently. Third, on the Dart side, wrap the invokeMethod call in a try-catch block that catches PlatformException and inspects the error code to distinguish between unsupported OS, missing permissions, and runtime failures. Fourth, explicitly state that Dart-side version checks using Platform.operatingSystemVersion are useful for UI hints but must not be the only guard, because the native implementation is the source of truth and Dart checks can drift or be spoofed.
COMMON WRONG ANSWERS: The biggest red flag is calling the native API unconditionally and letting the app crash on older devices with a NoSuchMethodError on Android or an unrecognized selector on iOS. Another red flag is returning result.success with an error string inside the payload, which forces Dart to parse text instead of using typed error codes. Some candidates suggest checking the OS version only in Dart and skipping the MethodChannel call from there; this is incorrect because it creates a race condition and duplicates logic that belongs in native code. Finally, throwing a Kotlin or Swift exception without catching it inside the platform channel method will cause an unhandled channel error that surfaces in Dart as a generic platform exception with no useful code.
LIKELY FOLLOW-UPS: An interviewer might ask how you would unit test the unsupported path without maintaining physical devices running old OS versions. The answer is to mock the MethodChannel in Dart widget tests and to write native unit tests that mock Build.VERSION.SDK_INT or the iOS system version before invoking the plugin method. Another follow-up is how you expose this cleanly in your public Dart API; the best practice is to define a sealed class or a custom exception hierarchy so consumers receive strongly typed results instead of raw PlatformException objects. You might also be asked what happens if the OS version is sufficient but the hardware lacks support, in which case you add a second runtime capability check after the version gate and return a different error code such as UNSUPPORTED_HARDWARE.
ONE CONCRETE EXAMPLE: Suppose your plugin exposes a scanNearbyWifi method. In Kotlin, you write if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val results = wifiManager.scanResults; result.success(results) } else { result.error("UNSUPPORTED_OS", "Requires Android 11 or higher", null) }. In Swift, you write if #available(iOS 15, *) { NEHotspotConfigurationManager.shared.getConfiguredSSIDs { ssids in result(ssids) } } else { result(FlutterError(code: "UNSUPPORTED_OS", message: "Requires iOS 15 or higher", details: nil)) }. In Dart, you define a WifiScanResult class with subclasses Available, Failed, and Unsupported, and you map PlatformException(code: "UNSUPPORTED_OS") to WifiScanResult.unsupported(). This keeps the native side authoritative and the Dart side predictable.
Read the original → docs.flutter.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.