The concept of Headless CMS has been a rage for quite some time. At Axelerant, we have been using Drupal as a Headless CMS in many projects. Headless drupal provides a JSON API for accessing the published content of Drupal, including menus via the Drupal Decoupled Menus module.
In this tutorial, we will use the Flutter framework to render a material design-styled menu across Android, iOS, Web, Windows, and macOS.
You might be inclined to ask why we choose Flutter instead of using React Native. The simple answer is we feel that Flutter is more polished as a framework. For more in-depth comparisons between the two frameworks, you can check this.
Head over to flutter.dev and follow instructions to install flutter on your machine & also install VS Code if you haven’t got that already. Let us create a flutter project by running:
flutter create drupal_flutter_menu
Open the drupal_flutter_menu folder in vs code. The moment you open it inside vs code, you will be prompted for installing flutter and dart plugins, well go ahead and install them.
On the Drupal side, we need a Drupal instance running with Decoupled Menus module installed and enabled Before we move further let us first look at the JSON returned by Drupal menu API if you navigate to the Drupal menu endpoint (https://<yourdrupalsite>/system/menu/main/linkset) and look for any menu, in this case, “main” menu, then the response JSON will look something like following:
The output will vary depending on what links are present in your specific Drupal menu.
If you look closely at this peculiar-looking JSON representation, it is a special media type called application/linkest+json which has been recently introduced by IETF. This media type is a special way to represent a set of links and their relations in JSON format. In order to know more about this representation head here. Our next step would be to parse this JSON in our flutter code and then create a simple Abstract Data type that will represent the parsed drupal menu, but wait wouldn’t it be better that we have something prebuilt that makes over lives easy, well we have already gone ahead and created a simple flutter package drupal_linkset_menu which takes a drupal menu API URL or a JSON string and returns a Menu object and then render it in Flutter.
Let’s add this package by running from the command line.
flutter pub add drupal_linkset_menu
This command will add the package to our package.yml file. The package.yml file is just like your composer.json file which is used to manage dependencies. Your updated package.yml should look like this,
The source code for a Flutter-based app resides inside the lib folder. We will be only working with the specially named file main.dart inside this folder. Let us delete all the code in the main.dart file and Replace with the following code, which will display “Hello World” in the center of the screen.
In order to run, click on the run and debug button inside the Run and debug section side menu section in vs code, choose dart and flutter on the next step, and then choose chrome on the next step.
This will run the app inside chrome browser, if you want to run on android you need to have android SDK and for iOS, you need to have Xcode installed, if both of the things are installed then you can use:
flutter run android && flutter run ios
to run on corresponding platforms, for more information on this head over to flutter.dev
Everything in flutter is a Widget! There are stateless widgets & stateful widgets, we will be working with stateless widgets today.
The code that we have put in the main.dart files does the following:
- It creates a Material app. Material is a visual design language that is standard on mobile and the web. Flutter offers a rich set of Material widgets.
- The main method uses arrow (=>) notation. Use arrow notation for one-line functions or methods.
- The app extends StatelessWidget, which makes the app itself a widget.
- The Scaffold widget, from the Material library, provides a default app bar, and a body property that holds the widget tree for the home screen. The widget subtree can be quite complex.
- A widget’s main job is to provide a build() method that describes how to display the widget in terms of other, lower-level widgets.
- The body for this example consists of a Center widget containing a Text child widget. The Center widget aligns its widget subtree to the center of the screen
Now update the code in main.dart with the following code:
Also, add a package called url_launcher by typing:
flutter pub add url_launcher
This package will allow us to open a URL, when any link in the menu is clicked.
Let us break down step by step what the code adds:
- In the MyApp widget’s build method instead of showing a “Hello world” text at the center, we have introduced a new widget called HomePage that will show two menus “main” & “footer” menu of our drupal site.
- The HomePage widget is another widget that houses the necessary build method that describes how to show the two menus and a couple of helper functions.
- The getMenu function is responsible for interacting with the drupal_linkset_menu packages helper method called getDrupalMenuFromURL which takes API URL and the menu name/id and returns a Menu object which is used to construct the UI.
- The two functions buildMenu & buildMenuItem are used to recursively build the ui for the menu,A special flutter material inbuilt widget called ExpansionTile is used to create the menu items.
- The build method of HomePage contains a Column widget that lays out children in a vertical fashion, it is analogous to how flexbox works on the web. The column has two FutureBuilder widgets that call the getMenu function, till the getMenu function returns a Menu object, a CircularProgessIndicator widget is shown, and when the Menu object becomes available the menu is created.
- In buildMenuItem we are using a GestureDetector to listen to taps and when a mouse click or tap is performed on a menu item the URL is launched.
Run again or hot reload again by pressing “r” on your command line to see the changes.
The aim of this tutorial was to give a sense of how quickly we can build a native cross-platform app with Flutter and the new Decoupled Menu API. Now you might be wondering that we didn’t talk about running the project on Windows and macOS- the support for both these platforms is still in beta but as an exercise you can still run the project on Windows and macOS by changing the Flutter stable branch for which more information can be found here.
All the code for this project can be found on GitHub.