Using platform variants when testing flutter widgets

Martin Fink
Geek Culture
Published in
4 min readJul 20, 2021

--

As Flutter evolves from being primarily focused on mobile device development to include desktop and the web, it’s increasingly important to test your widgets on all possible platforms.

Flutter makes it easy to check which platform you code is running on using theme.of(context).platform and then checking that against the TargetPlatform enum. When you write your tests, you should make sure your widgets perform or display as expected on any platform.

Historically, the way to do this would be to write your tests with the debugDefaultTargetPlatformOverride setting in your tests and making sure your widgets work as expected. If we use the default flutter app and the default test that is created with it, we can modify the test as shown to ensure that our widget works on iOS (the default is that all test run on Android). Notice also the need to reset debugDefaultTargetPlatformOverride to null otherwise the tests will fail.

Widget test for default flutter app

This worked well enough when we were making sure that our widgets worked on Android and iOS. But, it starts getting onerous when you want to check if everything runs on all Desktop variants (Linux, Windows, Mac), in addition to all mobile variants. As we move forward, I think it’s best to make sure all your widgets are tested for all platforms from the ground up.

In comes variants

The testWidgets function has a parameter called variant: which can be used to run multiple variants of a test. You do this by subclassing the TestVariant class to create your variants. Check out the class documentation here. Flutter provides a specific implementation of this class called TargetPlatformVariant. This implementation has built-in constructors for all, desktop, mobile and only. Let’s look at a smaller version of the default test from above with this implementation:

As I said, I just shortened the testWidgets function to save space. Focus on the added variant: parameters. It’s that easy. If I run this test (in VSCode), this is the output in the debug window:

You can see that one run of the test automatically ran the test for all desktop variants that flutter supports. I tend to just use TargetPlatformVariant.all(). It’s so easy to do, and it just makes sure that no unexpected issues popup for a specific platform.

Checking the TargetPlatform during the test

Even if you choose to run tests for all platforms, you will likely encounter situations where you expect different results on different platforms. This is where the previously mentioned debugDefaultTargetPlatformOverride comes in. You can use that to check for a platform, and expect different results. Here’s an example (it doesn’t make much sense in the default app context, but will show you what I mean):

Not the best example, but you should get the point. Even though the variant will run this test on all platforms, there will be different expectations for the iOS version.

Implications of debugDefaultTargetPlatformOverride

The TargetPlatformVariant and debugDefaultTargetPlatformOverride depend on using Theme.of(context).platform to make platform decisions in your code. There are a few different pub packages out there that attempt to remove the need for context when making a platform decision. I fell into the trap of untestable code by using universal_platform and GetX (which happens to be my chosen state management solution and I think is the best one by far). Each of these solutions (there may be others) allow you to do running platform checks without context. But, when you attempt to run your tests with variant: or defaultDebugTargetPlatformOverride, your code won’t recognize the platform change and won’t run as expected.

I’ve learned and accepted that doing a platform check requires a context and it’s just a fact of life if you want to add tests to your widgets.

To help myself, I created a utility class called RunningPlatform with a collection of static functions (that all need the context) that let me do almost any platform check I need in my code. I just import '<path_to>/utils/running_platform.dart' and then use RunningPlatform.isDesktop(context) (or any of the other utility functions) to do my platform checks. I’ve included the code for this below, feel free to use it if it’s useful.

As flutter evolves beyond it’s mobile roots into web and desktop, I encourage everyone to test all their widgets on all platforms a matter of course. Also, if anyone has a better/easier way of doing this, please let me know.

--

--