Platform Specific UI with Flutter

Mehmet Fidanboylu
3 min readSep 3, 2017

--

Flutter is quite good at handling platform specific interactions. For instance, you don’t need to write extra code to get over-scroll indicator for Android and elastic bounce for iOS. However, there might be patterns you want to implement differently on each platform such as building the main app navigation in a platform-dependent way.

In this article, I will show you a developer friendly way of using Drawer for Android and BottomNavigationBar for iOS. Let’s start simple:

In Flutter, you can import dart:io and use Platform property to look up which platform you are currently running on. The API is quite nice:

import 'dart:io';Platform.isIOS // Returns true on iOS devices
Platform.isAndroid // Returns true on Android devices

The most straight-forward option is to do everything twice:

if (Platform.isIOS) {
navigationBar = new BottomNavigationBar(...);
}
if (Platform.isAndroid) {
drawer = new Drawer(...);
}
return new Scaffold(
...,
drawer: drawer,
bottomNavigationBar: navigationBar,
);

This is a decent solution if you also want to customize the available menu options based on OS. However, most often, you will end up writing each navigation option twice and execute complicated book-keeping to track which menu item is currently selected.

Your next intuition could be to define each menu item only once but produce different navigation objects depending on platform. Let’s see how that looks:

Then you can use the NavigationMenu object as follows:

This looks promising:

  • We defined each navigation item only once and it magically works for Android and iOS.
  • The API is easy to use on each page.
  • The page still has full control of the Scaffold.

However, we quickly run into problems:

  • The NavigationMenu object does not have access to context so you can’t, for instance, automatically close the drawer once an item is clicked.
  • We end up with a ugly pattern of having to pass two parameters every time even though only one of them is set. If we forget to pass one, there’s no warning until we test on that platform and it blows up.
  • The currently selected page is not part of the object state.

When you start needing access to BuilderContext and the state needs to be passed into you, it should become apparent that what you need is a StatelessWidget.

In Flutter, the cleanest way to do this is to define a new widget with the API that we would like to have and build Scaffold as the output of that widget.

We can then refer to this widget from every page of the app and it will render the correct navigation on both platforms and correctly highlight the current page.

You can make this pattern more robust by having each page provide its own NavigationItem and having a registry that collects all of them in one place. That makes setting selectedItem easier as well.

This pattern fixes all of the shortcomings of the previous approach but you end up losing a bit of freedom in configuring the scaffold. In this case, that might be OK given that most use cases of scaffold rarely go outside of body, floatingActionButton and appBar.

This is just one way of writing platform specific widget code in Flutter. Hopefully the concepts introduced here will help you come up with your own patterns.

--

--