Implementation

Since you are reading this article, probably you have already used Flutter and/or want to get familiar with it (thank you, Captain Obvious). The main advantage of using Flutter is delivering applications for different platforms by using the same code base. However…

“With great power comes great responsibility.”

Even though you are using the same code base with Flutter, usually there is a demand that UI components should look different on different platforms. The simplest imaginable use case in Flutter context — showing the Material or Cupertino style widgets based on whether you are using accordingly an Android or iOS device.

To show an appropriate widget in the UI, you should check the current platform and then provide either Material or Cupertino widget (a simple if statement should work in this case). In general, this kind of implementation is inflexible:

The current platform should be checked in every place where different widgets for different platforms are needed;

It is hard to maintain the code when the new platform is introduced (for instance, Web) — additional checks should be introduced, the if statements would expand.

The Factory Method design pattern offers a relatively simple solution to this problem: every platform-specific widget has its concrete creator subclass extending the common abstract creator class. By introducing this, the UI code should only care about the common interface (base class) of all the specific components and based on the current platform an appropriate factory method would be called to create the widget — the code does not have to reference specific implementations of these components anymore.

As an example of this approach, we will create a page where you can select the current platform (it is simulated using the radio buttons) and based on that an appropriate alert dialog (Material or Cupertino widget) would be shown.

Class diagram

The class diagram below shows the implementation of the Factory Method design pattern:

Class Diagram — Implementation of the Factory Method design pattern

CustomDialog is an abstract class which is used as a base class for all the specific alert dialogs:

getTitle() — an abstract method which returns the title of the alert dialog. Used in the UI;

create() — an abstract method which returns the specific implementation (UI component/widget) of the alert dialog;

show() — calls the create() method to build (create) the alert dialog and show it in the UI.

AndroidAlertDialog and IosAlertDialog are concrete classes which extend the CustomDialog class and implement its abstract methods. AndroidAlertDialog creates a Material style alert dialog of type AlertDialog while the IosAlertDialog creates a Cupertino style alert dialog of type CupertinoAlertDialog.

Widget, CupertinoAlertDialog and AlertDialog are the already implemented classes (widgets) of the Flutter library.

FactoryMethodExample contains the CustomDialog class to show the specific alert dialog of that type using the show() method.

CustomDialog

An abstract class for showing custom dialogs. CustomDialog class implements the main logic to show the dialog (show() method). For the dialog creation itself, only the header of create() method is provided and every specific class extending the CustomDialog should implement it by returning a custom Widget object of that particular alert dialog.

Alert dialogs

AndroidAlertDialog — a concrete alert dialog class which extends the CustomDialog and implements the create() method by using the Material AlertDialog widget.

IosAlertDialog — a concrete alert dialog class which extends the CustomDialog and implements the create() method by using the Cupertino (iOS) CupertinoAlertDialog widget.

Example

First of all, a markdown file is prepared and provided as a pattern’s description:

FactoryMethodExample contains a list of CustomDialog objects. After selecting the specific dialog from the list and triggering the showCustomDialog() method, a selected dialog is shown by calling the show() method on it.

As you can see in the showCustomDialog() method, it does not care about the specific implementation of the alert dialog as long as it extends the CustomDialog class and provides the show() method. Also, the implementation of the dialog widget is encapsulated and defined in a separate factory method (inside the specific implementation of CustomDialog class — create() method). Hence, the UI logic is not tightly coupled to any specific alert dialog class which implementation details could be changed independently without affecting the implementation of the UI itself.

The final result of the Factory Method design pattern’s implementation looks like this:

As you can see in the example, by selecting the specific alert dialog style and triggering the ‘Show Dialog’ button, an appropriate dialog is created by the factory method and provided to the user.

All of the code changes for the Factory Method design pattern and its example implementation could be found here.