Flutter: Writing platform specific code on macOS
Implementing method channels on macOS
Introduction
Since its first stable version in 2018, Flutter has evolved so much, including support of multiple platforms such as Web, Windows, Linux, and macOS (there's even support for Risk V Architectures!)
When using Flutter, you'll write mostly code in the Dart language. This means you cannot access native platform functionalities (except if you are using ffigen or jnigen). Despite the fact you have a lot of libraries on pub.dev covering most of your needs in terms of accessing native platform functionalities (Android storage, connectivity status, etc...) you'll probably need some time to write your platform-specific code to handle some tasks. Fortunately, Flutter provides an API to do that. These are called platform channels.
To learn more about platform channels, I'll recommend reading this article from Mais Alheraki, Open source Engineer at Invertase
I recently needed to write platform code for a pet project and realized there was no resource in the documentation about how to do this on macOS. So in this article, we are going to learn how to write platform-specific code for a macOS Application.
For this tutorial, we will write a simple macOS app. The app will show information about the device battery such as the time left, the current capacity, and whether it is charging or not.
Setup
So first, we will create a new Flutter application. Since we are targeting macOS only, we will uncheck the other platforms
Flutter Part
Our project is now created. Next, we will write the Dart part of your code. We will create a DeviceBatteryChannel that will handle our calls to the native APIs:
Full file: device_battery_channel.dart
What's happening here
We created a class to handle our calls to native APIs. In this class we defined a Method Channel named com.stevenosse.battery/device_battery.
This class defines three methods that will effectively handle our calls. Let's dive into our first method, getBatteryLevel:
In this method, we are invoking the getBatteryLevel
method from our Method Channel without any parameter.
Native Part
Our native code will be written in Swift.
The code we need to accomplish our task was copied and pasted from this answer on StackOverflow.
See mom, I'm a real programmer 🥰
Initialization
We will now set up our method call handler. To do this, we will override the applicationDidFinishLaunching
method from our AppDelegate
Implementation
So here we implemented our method and initiated our method channel. We will need to implement a method call handler (that will handle invocation from our Dart code). Here is what it looks like for our getBatteryLevel
method:
What's happening here ?
When a call on getBatteryLevel
is received, we use our InternalFinder
class. We will first check whether the device has an internal battery or not, and then get the current charge of the battery and return the result.
The full source code of this app is available here: https://github.com/stevenosse/battery
Go beyond
I wanted to make things the simplest in this article to make it easy to understand. However, we could go further by using Pigeon. Pigeon is a generator tool to make communication between Flutter and the host platform type-safe, easier and faster, it's developed by the Flutter team.
Thank you for reading.