Flutter: Writing platform specific code on macOS

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.

App preview

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.

Did you find this article valuable?

Support Steve Nosse by becoming a sponsor. Any amount is appreciated!