Option 1: Use Material

You can use Material for both iOS and Android. This is absolutely acceptable. It has been pretty well established that Material transcends Android and today is heavily used on the web and on iOS. You’re not breaking any rules. Sleep soundly.

It is true that ink splashes can look a bit out of place on iOS. I’ve had a few clients that have specifically requested “no material” which ultimately meant “no ink splashes on iOS please”. If that isn’t a problem, or you like the style, then you’re all set! Material everything.

Option 2: Platform “Wrappers”

I’m calling any Widget that chooses Material on Android and Cupertino on iOS a Platform Wrapper. This approach seems to be emerging as a fairly common pattern but I’m going to say that, in my opinion, its plain wrong in most cases. There, I said it.

Depending on how true to the platform you are, you will eventually bump up, hard, against platform/widget specific differences. Oh? Let’s consider buttons. On iOS, UIButton doesn’t allow for text and images. Should you faithfully replicate that? If not, why do you care if its a Cupertino button?

If you’re not faithfully replicating the iOS situation, then what’s the point of the Cupertino widgets? I would argue that they don’t have a lot of usefulness unless you’re specifically targeting iOS (as a single OS app).

Also, what if the underlying Widgets change? Will you keep updating your wrapper to fit? What’s the lead time on that? There is a giant can of worms with your name on it.

There are definitely times when it makes sense to do this, for instance for a Switch. You’re not going to remake a switch (I hope).

Option 3: Build something in-between

Ugh build it? Why would you make me do that? The answer is because it’s quick, easy and it’ll be a lot nicer in the long run. Besides, you only have to do it once and then it’s done. You can reuse it 500x in your app or in every app you develop after this one.

OK, but let’s be super clear. I’m not advocating that you go off and re-build the entire set of widgets provided in Flutter. That would be crazy. I am saying that you should pick high value interactions and visual elements that make sense for your application. What does this mean? Here are a few examples. The full list is a lot longer, but it’ll help to provide context:

Navigation elements: You don’t have to use platform specific app/nav bars unless you want to. You should pay attention to iconography and placement of things like buttons and text though. Transitions: Flutter has a navigation paradigm that is closer to Android’s but is still unique. I’ve built enough UINavigationControllers to know that they have good bits and bad bits. I also know that you don’t have to replicate them if you don’t want. You can build your transitions different for iOS and Android if you want, or you can build them in a common way that works for your app. Button hit states: As discussed above, the “ink” effect is super common place on android and it’s generally expected. You don’t have to use it though. You just need to make sure that you have nice, complete hit states on all of your buttons. The animation below is from a button I use in all my projects:

For Android I use ink because it makes sense and I get that effect for free in Flutter. For iOS, I use a simple fade effect that is not standard on iOS because the iOS standard effects (the ones designed by Apple) are lame. All I need is to indicate to my users that they hit the button. How I do it is up to me.

In the end, the only difference is that there is a highlight state that is Android only. Everything else is identical.

In case you’re wondering, the button above took about 20–30 mins to make, is flexible, looks awesome and is available here.