Flutter: enhance development and maintenance of mobile applications

David Gonzalez
Flutter Community
Published in
7 min readApr 15, 2021

I recently discovered that you can make shortcuts in Flutter. First, I thought this feature will be used for desktop and web Flutter applications.

Is It really the case ? Let’s see four usages that will enhance your productivity and the reliability of your (mobile) applications :

  • Enable / Disable debug banner for taking screenshots
  • Enable / Disable the performance overlay and the material grid, to check widgets implementations
  • Switch between Dark and Light mode to ensure that UI design is well implemented
  • Send a big amount of debug data (however you want), feature mainly for the testing team
[Shortcuts] F10 : toggle performance layout; F12 : toggle theme mode; F8 : toggle material grid; Shift+F12 : send heavy debug report

All of these feature without compromising the UI Design and your application architecture!

Shortcut features are limited by your own imagination !

Introduction

Shortcuts are accessible anytime, but you may want to make them enabled only for debug builds. Also, you will have to connect a keyboard to your physical device or to your simulator/emulator.

Reminder : on iOS simulators, check that your laptop keyboard is connected

On Android emulators and simulators, it’s a little bit more complex. Android Emulators coming from SDK don’t recognize special keys such as ‘shift’, ‘alt’, ‘ctrl’, ‘F12’,… at least on MAC OS. I only get results on common keys such as letters and digits.

Edit : you can also use adb commands to send key events to your emulator or your device. In my case, I tested on my device, sending F12 keycode with this command (in a terminal): adb shell input keyboard keyevent KEYCODE_F12.

A complete list of keyevent can be found here: https://developer.android.com/reference/android/view/KeyEvent

I got satisfying results using other android simulators, such as Genymotion (in my case). Precautions have to be made as some of their simulators doesn’t work with Flutter. Therefore I recommend you to test shortcuts using Google Pixel 2 API 28, after having defined, in Genymotion preferences, the Android SDK path used by Android studio.

Android Studio and Genymotion must point to the same SDK tools.

On both Android and iOS devices, you can connect a USB and Bluetooth keyboard. Maybe with a dongle for iOS, if you don’t have the Magic Keyboard for iPad…

You can directly add shortcuts into your MaterialApp widget instance, through a combination of Shortcuts, Actions and Focus widgets, and by using FocusableActionDetector widget (that groups previous listed widgets together).

In this usecases, we will focus only on MaterialApp usage.

Implementing an action by pressing a keyboard key is decomposed in two steps:

  • 1/ Define a shortcut : which key combination to press to throw a shortcut intent
  • 2/ The action is associated to the shortcut intent

All these notions are defined in the MaterialApp, as shown below:

MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primaryColor: Colors.white,
),
darkTheme: ThemeData.dark(),
themeMode: themeMode,
showPerformanceOverlay: showPerformanceOverlay,
debugShowCheckedModeBanner: showDebugBanner,
debugShowMaterialGrid: debugShowMaterialGrid,
actions: {
DevelopmentIntent: DevelopmentAction(),
ToggleThemeIntent: ToggleThemeAction(),
SendDebugDataIntent: SendDebugDataAction(),
},
shortcuts: _shortcuts,
home: MyHomePage(),
);

Define shortcuts

In all cases, shortcuts as defined as a map of LocicalKeySet (keys) and Intent (values).

A LogicalKeySet define the keys to press simultaneously in order to throw an Intent.

You have to create your own Intents, as Intent class is abstract. Here is a simple implementation.

class DevelopmentIntent extends Intent {
final VoidCallback action;

DevelopmentIntent({this.action});
}
class SendDebugDataIntent extends Intent {
const SendDebugDataIntent();
}
class ToggleThemeIntent extends Intent {
final void Function(Brightness brightness) toggle;

ToggleThemeIntent({this.toggle});
}

You can put any information you want. In my example, I was very lazy so I implemented some generic Intent, named DevelopmentIntent, that stores a function that will be called later.

Why not call the action directly into the Intent ? Because you can manage actions policy, prevent them to execute in specific conditions for example. Also, you can have a proper access to a BuildContext in Actions (have a look to the SDK ContextAction class).

Define Actions

Actions are defined as a map of Intent types (as keys) and Action (as values). As for Intent, you have to create your own actions by creating classes inheriting from Action.

class DevelopmentAction extends Action<DevelopmentIntent> {
@override
Object invoke(covariant DevelopmentIntent intent) {
if (kDebugMode) {
intent.action?.call();
}
}
}
class SendDebugDataAction extends ContextAction {
@override
Object invoke(covariant Intent intent, [BuildContext context]) {
if (!kDebugMode) {
return null;
}
showDialog(...);
}
}
class ToggleThemeAction extends ContextAction {
@override
Object invoke(covariant Intent intent, [BuildContext context]) {
if (kDebugMode && intent is ToggleThemeIntent) {
intent.toggle?.call(Theme.of(context).brightness);
return true;
}
return false;
}
}

As you can see, you have the choice to implement a background process or something more visual, by inheriting ContextAction class.

Let’s see advantages to do that in your mobile application.

Usecase 1 : Debug banner toggle

Look at the upper right corner ;)

In that scenario, the purpose is to make demonstration and screenshots of a debug version of your application, without creating a custom build for that. It’s even more maintainable if you have to do that frequently.

The only development is located around the MaterialApp which command the display of various overlays, banner, ThemeMode, et cetera… To perform that, you have to wrap it into a StatefullWidget handling these features.

Or you can also use SharedPreferences for that, if your features need to be saved. Or create your own ValueNotifier (through Provider for example)… There is tons of ways to implement it. I choose maybe the laziest one :D .

Me thinking about implementing my shortcuts using a Provider…

Usecase 2 : Display overlays to debug your widgets

left: performance overlay, right: material grid overlay

When I implemented some custom widgets, such Wall layout (https://pub.dev/packages/flutter_wall_layout) and the cupertino listview (https://pub.dev/packages/cupertino_listview), I used a lot these two overlays to get some metrics about my libraries. It could have been more comfortable to use them directly on the device screen, instead of using Android studio panels.

Usecase 3 : Toggle light and dark mode

It’s a very simple feature, but it will save a lot of time! When I started developing my own Flutter applications, I may have spoil hours changing screen mode in device settings, to check that light and dark mode on both Android and iOS were fine. It can be a real pain to modify ThemeData properties in order to set the right color, and doing it without going out your application keep your focus!

It’s always good to create dev tools to ensure quality and maintainability of your app. And I think this one is a must have!

Usecase 4 : Export a large set of debug data

Some times ago I worked on an application handling a big set of data, up to 500Mo, packaged and distributed through a Gateway. When we had to fix bugs, most of the time we had a partial view of the problem, because we hadn’t access to local device data.

You know, that kind of bug you can’t reproduce, and only occurring on your client device. As you may understand, this is not a feature for you but for testers !

With Shortcuts, testers will be able to export these internal data, and bring it to you. In this example, we display a dialog to let the user send data by mail, but we could have exported data to a device public directory (for big data)!

You : he could have used crashlytics to send data attached to the bug!

I’m not sure that you can export a large amount of information using Crashlytics, as “Crashlytics supports a maximum of 64 key/value pairs. After you reach this threshold, additional values are not saved. Each key/value pair can be up to 1 kB in size.” (source: https://firebase.google.com/docs/crashlytics/customize-crash-reports-fabric-sdk)

Conclusion

My first idea was to discover Shortcuts for Flutter desktop and web applications, but I was astonished that it actually works for simulators.

Coupled to the fact that we can connect hardware keyboards on our devices, I could be possible to use this hidden features on real devices too.

Yes, please remind that it shall remain “hidden features”, because it’s not conventional on mobile applications development. And it could introduce a backdoor to your application if not well designed. That’s why I implemented it to be working in debug mode only.

The features : great but must remain hidden.

Last but not least, I showed you some usecases that make sense to me, have you other ones to share ?

As always, you can get the source code used in that article here (Flutter beta-channel, to make Flutter web application available):

--

--

David Gonzalez
Flutter Community

Hi, I’m David, a french mobile applications developer. As a freelance, I work on Android and iOS applications since 2010. I also work on Flutter now !