How I built and published the Shortcut Keeper app on both Apple’s and Microsoft Store, using Flutter for desktop.
Shortcut Keeper is a simple, lightweight desktop app that you can use to save your favorite keyboard shortcuts. You can find more about its features in the previous blog post.
In this post, I touch upon the app’s development process, how I decided which tools and frameworks to use, and challenges I encountered on the way.
Choosing Flutter and Dart
At first, I had no idea which framework and language to use to build a desktop app. My main background is web development, so I set out to research and select a framework to use for desktop app development.
Swift and SwiftUI
Initially, I only wanted to build Shortcut Keeper as a macOS app. I use daily a Mac, so it only made sense to work and build on my main OS and workstation.
Naturally, it made sense to have a look at Swift and SwiftUI, the modern framework for building apps with it. It’s probably the most used language for macOS app development, so it should have a mature ecosystem and plenty of support.
However, I found that while there are thousands of iOS/SwiftUI courses and tutorials, the macOS side is a bit lacking. It must be because SwiftUI is fairly new, and there is a lot more interest in building iOS apps. Nevertheless, it seems disappointing to be unable to find at least a to-do app tutorial for macOS and SwiftUI.
In the end, I decided to look for something else. At least I came out of it having learned the basics of Swift, which I found great to work with.
- I don’t really like the whole NPM ecosystem. In fact, I find it a mess of unmaintained packages, huge node_modules directory trees, and confusing build processes (webpack I am looking at you).
- Electron apps have the reputation of lagging in performance. Of course, Shortcut Keeper, being a small app, would most likely not have such issues.
- I couldn’t find a way to make Electron apps look native and match the macOS design language. It would take a fair amount of effort to build such a package myself for all platform-specific UI elements (buttons, icons, sidebars, text fields, etc.).
My eventual pick would solve all the above issues:
I have been using Flutter for the MapChart mobile apps, and loving it ever since. Earlier this year, beta desktop support was included in the stable build, and Flutter is now considered a truly cross-platform framework.
Indeed, it ticked all of my previous concerns:
- I was already comfortable with Dart and Flutter to build apps on mobile devices, so I would not need to get used to the language and framework.
- The package ecosystem is well maintained, and dependency handling is smooth and straightforward.
- There is no need for additional tooling and configurations, like transpilers, linters, testing frameworks, and so on. Most of these are already baked into the framework and the IDE-specific plugins.
- Dev Tools support is stellar; Hot Reload works flawlessly, and tools such as the performance overlay or the app’s outline are indispensable.
- There is a fair amount of tutorials and support for Flutter, including official, up-to-date guides on building a desktop app. For example, this code lab from Google, which was immensely helpful.
- There are already packages that handle the UI side of the app, making it easy to have a native-looking app for each platform (more on that later).
At first, I had my concerns on how mature desktop support is, but it is fair to say that it exceeded my expectations and enabled me to build a great-looking desktop app in a really short time!
I made the first commit to the codebase on August 27, 2021:
The macOS app was ready to publish on the Mac App Store after almost 3 weeks:
Porting the app to Windows also took around two weeks:
Building the main app features
The Shortcut Keeper app is a pretty simple app in terms of features.
It’s basically a glorified to-do app, in that it lets you apply CRUD operations on the recorded saved shortcuts, instead of on to-do items. The added complexity comes mostly from handling the recording of keyboard combinations and the nuanced differences between the platforms (macOS and Windows).
In this section, I will be describing its main components and briefly point to how I implemented them with Flutter.
I wanted the app to look native on both platforms and comply with each one’s design norms and guidelines. Two stellar packages made this far easier:
Both of them try to implement the current design language for their respective platform. They provide properly styled widgets that adhere to Apple’s Human Interface Guidelines and Microsoft’s Fluent Design language out of the box.
It is fair to say that these packages were the main reason that I managed to build the app in such a short time! Most of the provided components were ready to use and gave me a clean and proper layout to build on.
In the process, I got acquainted enough with them, that I became a contributor to their codebase and will continue doing so! It just makes sense to improve the tools that your app depends on.
The app uses a typical SQLite database to store the saved shortcuts locally, on the user’s machine.
I implemented this with the great sqflite package. It offers the basic database operations with a simple API for both platforms, though it needed some extra setup for using on Windows.
There is a plan to eventually move the data storage to the cloud, with something like Firebase, so that the user can access his shortcuts from any device.
Recording keyboard shortcuts
To add a new shortcut, the user can record its keyboard combination. This important feature of the app was implemented using a
Focus widget and listening to the user’s key presses.
A fair amount of work went into properly stopping the recorder from recording the user’s key presses. Also, I needed to adapt the keyboard shortcuts labeling for each platform. For example, macOS keyboard shortcuts have a different modifier key order from Windows:
Of course, a user cannot record shortcuts that are already in use by the OS, such as Command-Space for macOS (brings up Spotlight) or Alt-F4 for Windows (closes the current program). For these situations, there is also the ability to select the shortcut to add with a classic interface:
The global hotkey and platform channels
Another useful feature of Shortcut Keeper is that you can use a hotkey, like
Command-Option-K, at any time while using another app, and two things happen:
- Shortcut Keeper is brought to the front.
- The shortcuts of the app you were previously on are filtered and shown.
This is made possible using Flutter’s platform channels, i.e. custom platform-specific code (Swift for macOS and C++ for Windows).
Thankfully, this code lab from Google had clear steps on how to achieve the first step, and I continued from there to get the second feature to work. Having not used C++ since a long time, it gave me a big headache, but all went well in the end:
I have to say that it is understated how useful this feature of Flutter can be, as you are able to access all capabilities of the underlying system, just as you would with a classic, native desktop application.
I used the Provider package and found it suited my needs and the app’s complexity perfectly. There are three providers in use throughout the app, one for handling the shortcuts, one for the theme, and one for the settings.
Other useful packages
I should also mention that I used:
- shared_preferences for handling simple settings.
- file_picker for importing/exporting JSON and CSV files.
- bitsdojo_window for adjusting the app’s window design and size.
In the end, I found that most of the typical stuff that an app needs is already available and ready to use with Flutter desktop.
The official Flutter desktop documentation states that it is not advisable to publish an app with the implementation currently in beta status.
However, I had no issues building the release versions; both apps were accepted by the respective app stores and are now used daily without issues.
While the deployment for desktop is not as extensively documented as for the mobile platforms, it is getting better each day. I also had my previous experience with building for iOS and Android to help, as well as support from the Flutter community.
To conclude, I would certainly suggest using Flutter for desktop development, even for more complex apps.
Flutter enabled me to quickly build a beautiful, performant app, adapted to both major operating systems, with a single codebase. It is fair to predict that it can only get better as time passes, adding more features and capabilities and letting anyone build truly cross-platform apps.
Shortcut Keeper feels at home on both macOS and Windows. In the next post, I will be focusing on how I built the app to match each platform’s norms and design guidelines. Stay tuned!