How to build a macOS viewer app powered by Flutter

Mykola, Flutter dev at Krootl
November 17, 2023

After spending a few years with Flutter, I’ve always wanted to bring input to the Flutter open-source community. The perfect chance came up when Google shared the news about Flutter 3.

It made things stable for macOS, completing Flutter’s cross-platform plans. That’s when I knew it was the right time to jump in with Vector Xplorer.

In this article, I’ll walk you through the process of creating and launching an app that simplifies [.c]VEC[.c] management for developers using the flutter_svg plugin.

This project was crafted at the Krootl agency. The app is free and available in the MacOS App Store. We’re wrapping things up and getting it ready to share the whole project with everyone shortly.

Project Extension for Apple Photos on Mac - MILK Books
Download the resulting app on the Mac App Store

‎Vector Xplorer

Contents

Scalable vector graphics (SVG) have become an essential format for developing beautiful apps. They offer the flexibility of resolution-independent images that can be scaled without losing quality.

While developing projects, my team and I often use the flutter_svg plugin to handle [.c]SVG[.c] images in applications. However, after upgrading the package to Flutter 3.10, the functionality of caching files has been removed from the lib. So, the loading assets became longer than it was, but the developer provided instructions on how to reduce the time on precompiling and optimizing [.c]SVG[.c]s.

The vector_graphics backend supports [.c]SVG[.c] compilation which produces a binary format that is faster to parse and can optimize [.c]SVG[.c]s to reduce the amount of clipping, masking, and overdrawing. The [.c]SVG[.c] compilation is provided by package: vector_graphics_compiler.

Grand Theft Auto Michael GIF by Rockstar Games

Following this parsing process, it becomes impossible to simply view a preview of these binary files, and conversion can only be achieved using a script. Hence, I started developing a Vector Xplorer ([.c]SVG[.c] converter and [.c]VEC[.c] viewer).

As I delved into the developing process, it became evident that information was scarce within the open-source community regarding the creation of macOS applications using Flutter. With this in mind, let’s embark on a comprehensive exploration of the development process!

Setup the project and choose the design

First of all, you need to set up a new project in your IDEA, for this one I use Android Studio. No worries, you will have to use xCode further 🥲.

During the app development process, I opted for a design that closely resembled a native macOS application, and I achieved this by utilizing the macos_ui library.

For detailed guidance on how to use this tool, you can refer to the instructions available at this link: macos_ui_first_app.

For state management, I chose a provider package

The result of using macos_ui lib

The result of using macos_ui lib

Status bar and Hotkeys

Hotkeys are like magical spells for your computer, saving you from the exhausting quest of cursor-wandering to the menu bar. They’re the Gandalf of productivity, saying, “You shall not click!” 🧙‍♂️

Let’s establish personalized keyboard shortcuts for the status bar element labelled as a separate class MacShortcuts:

To gain mastery over the system status bar and incorporate hotkeys for your application’s support, it’s essential to utilize the PlatformMenuBar widget.

The code example:

  1. [.c]PlatformMenuBar[.c] — represents the macOS application’s menu bar.
  2. [.c]Menus[.c] — the list of dropdown menus, each represented by a [.c]PlatformMenu[.c] widget. These menus are organized under categories such as Application Info, File, View and Help.
  3. [.c]PlatformMenu[.c]: Each menu, like Application Info or File, has a label (the displayed menu name) and a list of items (menu options).
  4. [.c]PlatformMenuItem[.c]: These are individual items within a menu, each with a label (text) and an optional action or function (specified with the onSelected callback). When you select a menu item, it triggers the associated action.
  5. [.c]PlatformMenuItemGroup[.c]: This widget groups multiple menu items within a menu. For example, in the File menu, there are separate groups for different operations.
  6. [.c]Child Widget[.c]: The child property of [.c]PlatformMenuBar[.c] allows you to embed other widgets within the menu bar.
The result of using PlatformMenuBar

With the design phase now finished and users able to interact with the UI, the status bar effectively overridden, and hotkeys configured, what comes next on the agenda?

Preview, handle and convert SVG-to-VEC

For now, let’s explore the process of converting [.c]SVG[.c] to [.c]VEC[.c], as well as including Vector Xplorer in the list of supporting apps for opening these files.

As I noted at the beginning, for compiling [.c]SVG[.c] to [.c]VEC[.c] files, the app uses vector_graphics_compile lib. So, let’s write the class tool to manipulate the input file.

Example:

So, the compiler is done, how does it preview the files?
Let’s create a widget that will show [.c]SVG[.c]/[.c]VEC[.c] files, dynamically.

A seal is shown by VectorViewer widget

So, we can preview [.c]VEC[.c] and [.c]SVG[.c] files, but we need to insert them into the app.

Here are two methods for managing files in the app:

The first step is straightforward: we must enable the ability to drag files into the Flutter app. I achieved this by the desktop_drop package. This library provides a widget that delivers callbacks with outcomes when a user drags files into the active area.

Example:

Handle the files by dragging them to the active area

While the fundamental aspects of managing these particular files have been put in place, the real excitement lies in configuring the application to have files automatically open with Vector Xplorer through Finder!

The last part of the process involves configuring the [.c]Info.plist[.c] and the provided AppDelegate class to include Vector Xplorer in the list of supporting applications, allowing it to open [.c]VEC[.c]/[.c]SVG[.c] files both by double-clicking on the file and through the context menu’s “Open With” option. This is where we’ll dive deeper into xCode, exploring its intricacies and capabilities.

Document Types

Defining file and data types for your app by Apple

In xCode's [.c]Info.plist[.c] file, you can define various document types to specify which file types your app can open and interact with. Document types are essential for declaring the file formats your app supports and how it handles them. Let’s set up them.

Info.plist document with document types array:

  1. [.c]CFBundleTypeExtensions[.c] specifies the file extensions associated with this document type. In this case, it is set to [.c]SVG[.c] and [.c]VEC[.c], indicating that this document type is for [.c]SVG[.c] images and [.c]VEC[.c] files.
  2. CFBundleTypeIconFile the icon file that should be used to represent this document type. It is set to Document-Svg-Icon or Document-Vec-Icon, which likely references the icon file for [.c]SVG[.c] and [.c]VEC[.c] documents.
  3. [.c]CFBundleTypeIconSystemGenerated[.c] the icon for this document type which was generated by a system. In this case, it is set to 0, which suggests that the icon is not system-generated.
  4. [.c]CFBundleTypeName[.c] specifies the human-readable name for the document type. It is set to [.c]SVG[.c] Image and [.c]VEC[.c] Image, which is the name used to identify this type of document.
  5. [.c]CFBundleTypeRole[.c] — the role of the document type. In this case, it is set to “Editor” for SVG files and “Viewer” for [.c]VEC[.c] files, indicating that this document type is associated with editing or viewing [.c]SVG[.c]/[.c]VEC[.c] files.
  6. [.c]Document OS Types[.c] — the OS types associated with this document type. It includes [.c]SVG[.c] and [.c]VEC[.c], which are the types of documents that this entry applies to.
  7. [.c]LSHandlerRank[.c] — defines the rank or level of preference for your app when it comes to handling specific document types. It determines how the system should prioritize your app when multiple apps are capable of handling the same type of document.

Imported Type Identifiers

This identifier is used when an app primarily opens and imports documents of this type. In this scenario, the app acts as a consumer or viewer of the documents. Importing typically means that the documents are read and displayed within the app, but any changes made to them may not be directly saved back to the original document. The app might create a copy or store the document’s data internally.

That identifier is used to define and categorize various types of data and files, allowing applications to work with specific data formats or file types consistently.

Let’s configure it:

  1. [.c]UTTypeConformsTo[.c] — represents an array of universal type identifiers to which the imported type conforms. In this case, the imported type conforms to public.content and public.data, meaning it shares characteristics with these types.
  2. [.c]UTTypeDescription[.c] — contains a string that describes the imported type. For the first dictionary, the description is Vector Xplorer Vec and for the second dictionary, it’s Vector Xplorer Svg.
  3. [.c]UTTypeIcons[.c] — associated with a dictionary that defines the icons related to the imported type.
  4. [.c]UTTypeIconBackgroundName[.c] — specifies the name of the icon background. In the first dictionary, the background name is Document-Vec-Icon, and in the second dictionary, it’s Document-Svg-Icon.
  5. [.c]UTTypeIdentifier[.c] — holds a unique string identifier for the imported type. The first dictionary has an identifier com.krootl.vectorxplorer.vec, and the second has com.krootl.vectorxplorer.svg.
  6. [.c]UTTypeTagSpecification[.c] — point to a dictionary that specifies tag specifications for the imported type.
  7. [.c]com.apple.ostype[.c] — contains an array that defines an OSType tag for the imported type. The first dictionary has [.c]VEC[.c] as the OSType tag, and the second dictionary has [.c]SVG[.c].
  8. [.c]public.filename-extension[.c] — holds an array of filename extensions associated with the imported type. For the first dictionary, the extension is [.c]VEC[.c], and for the second dictionary, it’s [.c]SVG[.c].
The Info.plist file in a readable format
Custom icons for VEC files which are supported by Vector Xplorer

So, we configured [.c]Info.plist[.c] file for notifying the OS that Vector Xplorer supports [.c]VEC[.c] and [.c]SVG[.c] documents. When the app is installed on the machine, the system will override the generated system icons for unsupported files (in our case [.c]VEC[.c]) and the document will have the next view:

Vector Xplorer is installed. The OS haldles VEC and SVG files.
The OS suggests opening VEC files by Vector Xplorer by default.
The OS suggests opening SVG files by Vector Xplorer additionally.

Success! The operating system now recognizes Vector Xplorer as a tool for handling [.c]SVG[.c] and [.c]VEC[.c] files. However, if we attempt to open them, nothing will occur since we haven’t yet implemented the channel method required to transmit the files to the DART side.

But no worries, let's navigate to the [.c]AppDelegate.swift[.c] and setup the Flutter method channel.

With the invoke method set up on the native side, it’s time to exit xCode and back to the Dart side! 🚀

Configure the deep-link manager to handle the file path when a file is opened from Finder, allowing the system to send it to the supported app for further processing.

deep_link_manager.dart:

The manager is configured, create the DeepLinkManager instance in the main.dart function and set a listener for handling the files.

In the [.c]_HomePageState[.c], there must be an overridden [.c]initState[.c] method and check for file path in the [.c]LocalStorage[.c] and make further manipulation for showing [.c]VEC[.c]/[.c]SVG[.c] documents in the app.

In this article, we built the [.c]SVG[.c] handler app, which provides the ability to convert [.c]SVG[.c] to a Binary [.c]VEC[.c] file and easily view it.

I hope you found this article useful, informative and enjoyable. if you have any questions or suggestions, please write them in the comments below.

Farewell 👋

more from the blog

Get in touch

Got a project in mind?
We are eager to learn more about it!

Or just say hi

hello@krootl.com

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Or just say hi

hello@krootl.com