Flutter is all about widgets. All those widgets nested within each other will result in a widget tree. For a simple UI, you might still be able to re-read and understand the code even when you come back to it after days or weeks. But how about complex UIs? Complex UIs usually consist of a lot of widgets and what does that mean? A higher chance that the code will become less readable, harder to maintain, and become less efficient.
As the number of widgets we use gradually increase, the more complex the widget tree becomes and the harder it is to manage and read the code. So, this is where refactoring becomes handy. The main idea of refactoring is to keep the widget tree shallow which will help the overall performance of the app.
Below is a design on a project I am currently working on. It is not simple neither complex, but it’ll do for an example.
The UI looks pretty simple right? Just a bunch of texts, buttons, a dropdown and cards right? Now lets look at the code.
I’m sure that’s probably how you’re reacting right now. But actually, the code above is mostly refactored. Imagine how the code would look like if it had not been refactored. Unreadable, unmaintainable, you just don’t want to look at it, right?
Anyways, let’s get straight to what methods of refactoring we did here.
There are three mostly used methods to refactor widget that I use while using Flutter, which are:
Refactoring with Extract Method
This is the mostly used refactoring method I’ve used in my project since it really helps make the widget readable, especially extracting nested widgets from the
build method of the Widget. Extracting widgets is very simple since all you need to do is create a method that returns the widget you wish to extract. Let’s see an example taken from the code above.
You can see that in the
build method, we extract the children of the Column widget into smaller methods. It makes it more readable and easier to maintain. You should also realize that each method name describes what widget it returns. Now, imagine how it would look like if none of the children of the Column widget in the
build method were not extracted. I am 100% sure that you’d have a hard time reading the code too.
Even though performance-wise, it doesn’t make much of a difference since widgets that are extracted into methods depend on the parents BuildContext and will be rebuilt every time it’s parent get rebuilt. Which is not what we want from refactoring.
In Visual Studio Code, it is very easy to refactor. Here are the steps to do it:
- Open any file you have on your project.
- Click on the first widget you would like to extract, then right click on it. In the case above, it would be the Container method of every child of the Column widget of the
- Select Refactor > Extract method.
- In the Extract method dialog, you can name your method to whatever you want. But in this case, the first one would be
- The Container widget in the
buildmethod is then replaced by the
_buildCreateReport()method call. And if you scroll down, you should see the
_buildCreateReport()method which returns the Container widget.
Refactoring with Extract Class
The extract class method is useful in flutter because you will be able to create reusable widgets. These widgets can be placed in a different dart file and you can import them whenever you want to use it in a different class. Let’s see a snippet of the code above that uses this method.
This class is for each card that is in the design I posted above. It will render something like the picture below.
Now, imagine it not extracted into a class. You would have to hardcode each and every one card which is a hassle right. We could see that the class itself is around 100 lines. Now, how much lines would 3 cards be if the cards weren’t extracted into a class? A lot for just 3 cards right? Since the cards have the same layout, the extract class works like a charm.
The class extracted widget has it’s own BuildContext so it will not redraw, recreate, or reassemble it’s widgets every time the parent redraws. Therefore, the overall performance by using extract class is better compared to extract method.
In VS Code, it is easy to extract a widget into a class. All you have to do is:
- Open any file on your project.
- Place the cursor on the widget you would like to extract then right click on it.
- Select Refactor > Extract Widget
- In the dialog, choose a name for your class. In this case, it is
- You should be able to see the class if you scroll down.
Refactoring with Extract Variable
Extracting widgets into variables is almost the same as extract method but here we initialize the widget as a final widget. The widget depends on the parents BuildContext, therefore will redraw its widgets everytime its parent redraws. So performance-wise, it is the same as extract method which does not increase the overall performance. But, by extracting widgets to variables, we get a more readable, maintainable, and shallower tree.
The code above does not implement the extract variable refactoring method, so here is a little example from another part of the project. And you can see that the code below has a
myTabBarView local variable.
In VS Code, it is easy to extract a widget into a local variable. Here’s how to do it:
- Open any file in your project.
- Place the cursor in widget you would like to extract then right click on it.
- Select Refactor > Extract Local Variable
- In the dialog, pick a describable name for the variable. In the case above, it will be
- You should be then able to see the local variable if you scroll up.
Not just Flutter, but for every other projects, refactoring is a must to implement since we developers will really benefit from it in the long run.