Using native share API in Flutter based web apps

One of the fairly new APIs added to the web stack is the Navigator.share API. The API allows websites and apps to invoke the native share sheet on OS running the web browser to let the user share text, links and images to the services they actually use.

This week, one of the tasks on our new web app (written in Flutter) was to transition from our custom “share this link” UI that was preconfigured to a select set of services (Twitter, Facebook, Reddit, email) to the native share api allowing users to share the link on their configured services.

Some thoughts on the Share sheet

Before I continue, lets talk about the OS share system in general. If there was ever a “God Object” equivalent of user interfaces, the system share sheet on mobile devices has to be it. The widget has become sort of like an escape hatch for the underlying application with not only share capabilities, but a host of other actions in the same menu (Find on page? Print? Add to home screen?) which keeps getting updated by your installed applications. I don’t think anyone can list the menu options there from memory, and yet if we expect to run any action on any data from the underlying application, we expect that action to be available in the share sheet.

I bring this up because the native share from a webpage is now not as simple as sending a title, text, link and image to the share sheet and assuming it would work. Different behaviors from different apps get enabled based on various combination of the 4 fields. For example, one service we wanted to make sure users could use was Instagram (assuming they were Instagram users). IG behavior via share is something like this:

InputInstagram behavior
Only title, or title + textCannot share to Instagram
link + any combination of title and textShare with a friend on Instagram as a message
Only imageShare that image in a post, story or message
Image + any of the other fieldsShare image as a reel
Instagram behavior matrix

Other image oriented services like Snapchat and Pinterest also seem to be sensitive to the combination of the 4 fields sent. Its kind of a bummer that we have to now spend QA cycles making sure we are sending the right order of fields to get the desired behavior from the receiving app.

Native share on the desktop?

The other challenge to native sharing is that while the browser API may be available on a lot of platforms, native share on the desktop does not make sense since most people don’t install apps for the services they use on their desktop which means that the share menu is pretty barren. Chrome on the Mac desktop, perhaps sensibly, does not support the web share API but Safari does.

Our solution for the app now is to use our custom share UI on desktop browsers but use the native one on mobile. Feels a little hack-ish but it makes the most sense from a user’s point of view.

Sharing images on Flutter web apps with Share Plus

The last aspect of the implementation that tripped us a bit was trying to include images from a web URL along with the title and link to the share API. We are using the Share Plus library which is pretty good and supports the native share API. Unfortunately we didn’t find any links on how to convert an image link to the image attachment (its still early days for Flutter web I suppose). If anyone else needs the snippet, here is how you do it:

final http.Response responseData = 
  await http.get(Uri.parse(linkToFile));

Uint8List bytes = responseData.bodyBytes;

var file =  XFile.fromData( bytes,  mimeType: 'image/png', name: "MyPic.png");

await Share.shareXFiles([file], text: 'Hello', subject: 'Check out this image' );

The thing that I wasted 2 hours on today was this snippet not working cause the name I was giving my file did not include the file extension 🤦‍♂️. So be warned 🙂

GDG-Philly and Philly Cocoa’s “State of Mobile Union” Event

This week the GDG-Philadelphia group that I manage collaborated with the Philly Cocoa group to run our first in-person event since Covid. The number of folks who showed up was much higher than I thought they would, I think everyone just really wanted to meet each other IRL again.

The developer meetups in the city have unfortunately not re-emerged from Covid as much and I am hoping they do. I am hopeful that more such events start happening again.

Thanks to Kotaro for leading so much of the organization effort for this event, and to Comcast Labs and the Lift Labs group for sponsoring the space and food.

And of course the speakers:

Looking forward to the next one 👋

Some gotchas when using Firebase Dynamic Links

The last couple of weeks I have been trying to add Firebase Dynamic Links into an app and it took me way longer than I had originally planned. In this post, I wanted to share some of the lessons learned.

First: note that there are 2 kinds of links that you can use:

  1. Dynamic links that you generate on the Dynamic Links section of your Firebase project. These will be the same for every user and are great to link to universally same sections of your app. These are quick to set up and are probably a good idea to try before generating the second kind of deep links
  2. Dynamic links generated by a user for another user on a client application. These would be custom links only relevant for unique users and so cannot be generated via the project dashboard.

In my case, I was trying to get #2 working and it proved to be a real bear.

The problem is that when generating a unique URL, you are essentially doing a couple of handoffs: The first link is managed completely by Firebase (usually with a *.page.link URL). This link checks to see if the app is installed on the device the link is launched on and links to the app-install page if not. If that is fine, the link redirects to the second link that you actually are trying to get to. The second link is often a web address on your own domain which needs to be correctly configured for that page, or else the link will just open that webpage which is probably not what you want to do.

Gotcha 1: [Android] Make sure you have the SHA256 signature saved in your Firebase project

For the longest time, I didn’t realize that I had only the SHA1 key saved in my project. Deep links don’t work without SHA256 values for your project. Thanks to this answer from StackOverflow.

It took me a while to get this document correctly deployed (mostly my fault). I really should have read the documentation on verified site associations on Android more carefully. You can verify your assetlinks setup via this URL (just replace the domain and, if applicable, port values):

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://domain1:port&relation=delegate_permission/common.handle_all_urls

Also remember, if you are using Google Play Store to sign and release your app, your assetlinks should refer to their key’s SHA256 signatures. Conveniently, you can copy the assetlinks file from the Play Store itself under the Setup > App Integrity section of the developer console

Gotcha 3: [Android] Make sure “autoverify” attribute in your intent-filter is set to true

Not sure how I missed this but, this took a long time to find

 <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
     <category android:name="android.intent.category.BROWSABLE" />
     <data
          android:scheme="https"
          android:host="my.app.com" />
 </intent-filter>

iOS:

Surprisingly, as frustrating as getting the Android version to work was, the iOS integration was much simpler. Just following this video helped a lot!

Hope some of this info helps you if you are using dynamic links in your app

Flutter: Efficiently loading images between animations

I was recently playing with Flutter’s PaletteGenerator library. The library does what Android’s Palette API does: gives you a palette of colors based on a bitmap given.

The experience I was going for was to use the dominant color from an image to color the detail screen when the user tapped on it.

It’s fairly easy to use the library if you have a handle on the ImageProvider or the Image object.

getDominantColor(ui.Image img, Function(PaletteColor) onColor){
  if(img == null) return;
  PaletteGenerator.fromImage(img).then((PaletteGenerator g){
    onColor(g.dominantColor);
  });
}

Most of the examples I have seen for PaletteGenerator use an in-app image that the system can immediately load but using remote images is more complicated since the library has to wait for enough of the image to load to read the color data. This gets further complicated if you need to run an animation while loading the image.

After trying a number of iterations, the best approach seems to be using Flutter’s precacheImage method before kicking off the animation

await precacheImage(NetworkImage(b.image), context);

Also, consider using the CacheImage library to improve later performance if that screen is loaded multiple times

Suddenly, Flutter

I had no plans to learn Flutter in 2019

When I first heard of Flutter last year, I couldn’t help but draw parallels to Java Swing, the UI technology I started with in grad school (and thankfully dropped a year or so later). If you don’t know much about Java’s UI technologies, suffice to say that for all of Java’s strengths, no version of their UI framework was ever one of them.

It started okay-ish enough with Java’s AWT toolkit that let Java call native code to create system windows, buttons etc, but devs soon realized that building cross-platform applications (which was always Java’s pitch) was really hard when you could only target the least common denominator widgets that were available across all platforms. “No more” said the Java community, and proceeded to build Swing, a cross-platform UI framework that emulated the system controls by drawing them itself on a canvas.

Image result for write once debug everywhere java

Sound familiar? That was what Flutter promises with the core graphics engine that would emulate the native Android and iOS widgets

The problem is that Swing turned out to be crap. The widgets never felt native and performed poorly. You could always tell if you were using a Swing app. And it was always interesting when some app wasn’t coded right and you’d end up with apps emulating the Windows look-and-feel on a Mac (who checked on a Mac back in those days)

So Flutter was on my “no-thanks” list. Besides my lack of faith in faking native system widgets, the language they chose was Dart! Who knew Dart? More importantly, where were the interesting libraries in Dart? Having done a few React Native apps before, I liked my options with JavaScript and the dream of making cross-platform (mobile AND web) apps.

But then a couple of things happened. One: I saw some pretty compelling Flutter based apps. The interesting thing was, the best apps try to create their own design language anyway, so deviating from the system look-and-feel felt okay; and then second: I tried Flutter for a labweek project and was won over by the one click deploy to multiple platforms and the hot reload (it might also have been just fortuitous timing as I was losing my mind with React Native’s minimal support for custom views and animations, something that Flutter promises a lot more control over)

So for the last 4 months or so I have been working with Flutter and I have to say I really enjoy it. Some parts are definitely rough (I still don’t love the indentation hell when describing layouts) but Dart is enjoyable and is very inspired by the best parts of JavaScript and Swift (among others). Recent additions to Dart have been very interesting, because they address issues in real-world UI development. This feels different from the more generic “Lets create a general purpose language and allow it for UI dev” approach of other languages.

But the core reason I am excited about Flutter is the culture. The reason Swing was a dud (IMHO) was that it was built by people who didn’t care to push UI experiences. The native mobile toolkits are better but still make it hard to build complex user interfaces (SwiftUI and Jetpack Compose are trying to change that). Example “Hello World” apps you see using native toolkits are pretty generic form-based apps.

But look at the kinds of apps Flutter shows off:

Image result for flutter timeline app

🤩

While this may not be everyone’s cup of tea, this “think-outside-boxy-layouts” approach has my vote.

4 months in, I am still new-ish to Flutter but I guess I am on board. Stay tuned for more random Flutter stuff on this blog 🙂