Photo by Joshua J. Cotten on Unsplash
Isolating Flutter Widgets: Getting Started with Monarch š¦
Monarch enables Flutter developers to separate UI development into "stories," allowing quick access to all widgets with a single click.
Why Is Monarch Useful
Are you tired of wrestling with the wild tangle of your growing Flutter app's UI? Is your Flutter UI growing faster than you can say 'hot reload'? Ever wished for a magic wand to simplify the chaos? So let me introduce you to Monarch.
Dealing with the complexity of a growing Flutter app's user interface can be quite a challenge. Fortunately, Monarch comes to the rescue by allowing you to isolate your widgets. With Monarch, you can create individual stories for your widgets, allowing you to see how they appear in different visual states across various devices and themes without emulators.
This simplifies tasks like handling edge cases, identifying bugs, and designing responsive UIs, without constantly running your app or backend. Monarch runs smoothly on your desktop platform (Windows, Linux, and MacOS), so no need to rush into another platform to give it a try.
Set Up Monarch
Monarch is an incredibly easy-to-set-up tool for any kind of developer. The installation guide is clear enough to follow by any level developer.
After following those easy instructions you can go to your terminal and type monarch
then you'll get the following result.
The Monarch CLI it's ready to use just in the blink of an eye šļø
Hello World Monarch
As you may already have noticed, after installing the Monarch CLI in your machine there are some options available to start using the tool. The only requirement before starting is to have an already existent Flutter project, it doesn't matter if it's a new one.
After that, you're ready to initialize your project including all the dependencies needed to make Monarch work on your project. Fortunately, Monarch deals with this using the CLI, just type on the root of your Flutter project the following command.
monarch init
This command would add all the dependencies needed to work automatically with Monarch on your project. But don't worry, is not a lot, only these 2 little packages are needed to work with Monarch.
# New Monarch dependencies
monarch: ^3.0.0
build_runner: ^2.1.11
Also, this will create a new stories
folder on the root of your project, and inside of it you'll find 2 new files: sample_button.dart
and sample_button_stories.dart
that would work as an example for you. So don't be mad and let's take a look into this example.
import 'package:flutter/material.dart';
import 'sample_button.dart';
Widget primary() => const Button('Button', ButtonStyles.primary);
Widget secondary() => const Button('Button', ButtonStyles.secondary);
Widget disabled() => const Button('Button', ButtonStyles.disabled);
But hey, don't let them steal all the fun. Let's create something by ourselves. Go and create a new file inside the stories
folder called hello_world_stories.dart
. Now just copy and paste the following code snippet.
Widget helloWorld() => const Center(child: Text('Hello World Monarch'));
It's pretty easy to understand, is just a centered text widget with the message 'Hello World Monarch'
, is not a big deal.
Now that is all set, you can go directly to the root of your project on the terminal and type the following command. That would turn into a new window popping up with all the widgets that you just added to your project.
monarch run
Monarch will sort your widgets by file and in the order you set on the file. Then you can easily play around with all the options available with the tool such as different types of devices, themes, locales, scale factors, etc.
_stories.dart
files are located inside your project, Monarch will find them and include them. Just remember to follow best practices, allocate them in the same place, and follow your project structure, just like a test.Take a look at the implementation of this chapter on the following link.
Working With Screens
The previous example seemed straightforward, right? Now, letās dive deeper and explore more complex use cases with Monarch. Imagine youāre developing a feature where a user enters their name, taps a button, and is then redirected to a second screen displaying a warm welcome message along with their age, if provided. While building this feature might not pose a significant challenge, if itās buried deep within your feature tree, manual testing could become a time-consuming task for both you and your QA team.
The next widget prompts the user for a name before redirecting them to the welcome screenāitās a simple process that doesnāt require much explanation.
import 'package:flutter/material.dart';
import 'package:flutter_monarch/welcome_screen.dart';
class NameRequestScreen extends StatelessWidget {
NameRequestScreen({super.key});
static const route = '/';
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(24.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Enter your name, please!'),
const SizedBox(height: 8),
TextField(controller: controller),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(
context,
WelcomeScreen.route,
arguments: WelcomeScreenRouteArgs(
name: controller.text,
),
);
},
child: const Text('Start'),
)
],
),
),
),
);
}
}
The next screen is the welcome screen, where weāll display the information received from the NameRequestScreen
. Like the previous example, this is a straightforward implementation: a widget that displays the information, with a simple conditional statement to check if the age parameter is not null
.
import 'package:flutter/material.dart';
class WelcomeScreenRouteArgs {
final String name;
WelcomeScreenRouteArgs({required this.name});
}
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({super.key, required this.name, this.age});
final String name;
final int? age;
static const route = 'welcome';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Welcome $name'),
const SizedBox(height: 8),
if (age != null && age! > 0) Text('Age: $age'),
],
),
),
);
}
}
Going back to the testing phase, doing some tests around this feature would be very easy in this example because it's the only feature. But if this feature would be part of a huge app probably nested between a lot of functionality, then would turn into a huge effort. Monarch finds a way to make your developer's life easier.
Let's get started creating some files on the stories
folder named name_request_screen_stories.dart
and welcome_screen_stories.dart
this would serve as entry points for the screens that are going to be displayed on the monarch tool.
As simple as we previously saw the implementation of a story just needs to define a function that returns the widget wanted to be displayed. Just simple as that.
import 'package:flutter/material.dart';
import 'package:flutter_monarch/name_request_screen.dart';
Widget nameRequestScreen() => NameRequestScreen();
Next, for the welcome screen, we have 2 scenarios. The first one only passes the name for the welcome screen and the second one passes both name and age. So we can figure the output for both scenarios with just simple clicks.
import 'package:flutter/material.dart';
import 'package:flutter_monarch/welcome_screen.dart';
Widget welcomeScreen() => const WelcomeScreen(name: 'Messi');
Widget welcomeScreenWithAge() => const WelcomeScreen(name: 'Messi', age: 33);
Let's see the result of our work, go to your console and type. And you will get the following result.
monarch run
Youāll now notice two new story groups: welcome_screen_stories
and name_request_screen_stories
, each containing their respective stories. Letās take a look at the results for each.
The nameRequestScreen
displays a straightforward interface with a text field for entering your name and a Start button beneath it. This simple layout is easily accesible in the Monarch preview, making it clear and easy access to test and verify the UI.
The welcomeScreen
presents a clean and minimal interface, displaying a simple welcome message: āWelcome Messiā. This straightforward layout is clearly shown in the Monarch preview.
The welcomeScreenWithAge
displays a personalized welcome message along with the userās age. In this example, the screen shows āWelcome Messiā followed by āAge: 33ā. This layout, visible in the Monarch preview, helps ensure that the conditional rendering of the age information works correctly in your app.
With this capability, you can effortlessly toggle between various states of your screens, enabling you to thoroughly test the UI elements and behaviors of your widgets under different conditions. This not only streamlines the process of identifying potential issues but also enhances your ability to ensure that your app performs consistently across multiple scenarios.
Conclusion
Monarch is a game-changer for Flutter developers, making UI development and testing a breeze. By isolating widgets and easily toggling between different states, it simplifies the process of ensuring your appās UI works perfectly across various scenarios, devices, and themes. With Monarch, you save time and effort by cutting down on repetitive manual testing, all while enhancing your appās reliability and responsiveness. Itās easy to integrate, familiar to use, and ideal for projects big and small.
In short, Monarch helps you tame the chaos of UI development, so you can focus on building great apps without the headache. Check out the GitHub repository for this article.