Flutter has been getting more and more popular lately. You can use it to build complex applications that work smoothly on MacOS, Windows, and Linux.
But building these applications is not always a simple process. You often have to refactor your code to maintain the app’s performance.
One such refactoring technique is extracting duplicated code and components and reusing them in multiple places.
In this tutorial, you'll learn how to replace a duplicated component by building a custom widget in Flutter.
What is a Custom Widget?
In Flutter, a custom widget refers to a user-defined widget that encapsulates a specific set of functionalities or visual representations.
Custom widgets are the building blocks of a Flutter application. They allow developers to create reusable UI components that can be used throughout the application.
If you're switching from React Native, you can think about custom widgets as custom React components. And what we call
props in React are called
parameters in Flutter.
Why Use Custom Widgets?
Custom widgets help you encapsulate complex UI elements. They also promote code re-usability and enhance the maintainability of your Flutter applications.
There are a number of reasons to build build custom widgets in Flutter. Let's look at some of them.
Custom widgets allow developers to encapsulate complex functionality and appearance into reusable components.
Once created, custom widgets can be used multiple times throughout the application, reducing code duplication and promoting a modular development approach.
Custom widgets contribute to the maintainability of the codebase. By encapsulating specific functionality or visual representation, custom widgets create a separation of concerns. This separation makes it easier to locate, modify, and debug code related to a particular UI component.
They also enable developers to define a consistent and unified UI design across their application.
And finally, custom widgets provide a level of abstraction that hides the implementation details and complexity of a particular UI element.
You can create high-level widgets that expose a simplified interface and handle the internal logic. This allows other developers to use the widget without worrying about its internal workings. This abstraction promotes modularity, making it easier to understand, test, and maintain the code.
How to Build a Custom Widget in Flutter
Let’s start building our custom widget.
Clone the Repo
Instead of starting from the scratch, I’ve created a Flutter app in GitHub and added duplicated code/components in that repo. Let’s begin from there.
Pull the code from GitHub by running the below command:
By default, it’ll be in the
master branch. I’m switching to a
refactor branch (you don’t need to) because I want you all to have a look at my initial and final code. The initial code will be in the
master branch and the final code will be in the
Run the following command to install all the dependencies:
Run the App
Open the repo in Visual Studio Code and spin up your emulator (you may connect your mobile device, too). Once your emulator is up and running, press
F5 to run the app in the emulator.
Here’s the view of your app on the first run.
If you’ve come this far, that’s great.
Analyze the Code
Let’s look at the code. Open the
We have a
MyApp class called at the beginning. This in-turn calls the
This is our code for the entire UI which is defined in
And so you can reference the line numbers, here's a visual:
If you’re someone who loves writing clean code, you would definitely say that this is ugly code.
Here’s the reason for it. Look at the code carefully – lines 44 to 56 and lines 58 to 70 are completely duplicated except for a very few handpicked words. For example, the word “Twitter” has been replaced with the word “Instagram”.
The clean coder will definitely refactor this code before working on adding new features/functionalities. Let's follow those clean coding practices now, too.
Refactor the Code and Build a Custom Widget
We have to extract the text and button into a separate component. This component should accept the
onPressed as its parameters. We can template out the common text from them.
So, our code to build the custom widget looks like this:
As we discussed above, the template text and accept
onPressed parameters. We replaced
platform wherever we need and call the
onPressed method as the extension of showing a snack bar.
Add the above code at the very end of the
Integrate the Custom Widget
Let’s integrate our custom widget into our code.
Pick the first block of code from the line 44 to 56 as shown below
Replace it with the following code:
Similarly, pick the next block of code from the line 58 to 70 as shown below
Replace it with the following code:
Here's the final code of
_MyHomePageState class after we complete our refactoring process.
And again, here's the screenshot for line number reference:
Run your app now.
Unfortunately, you’ll not notice any change in the UI. But your underlying code has changed. That’s exactly what refactoring is.
Quoting from Martin Fowler,
Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. – https://refactoring.com/
You may be wondering something after looking at the above code. Lines 43 and 50 also contain the same code (
const SizedBox(height: 16),). So why don’t we include that into the component?
That’s great if you had this question.
There is no need for the custom widget component to include the
SizedBox component. This is because the
SizedBox component is added in the Home page to give some space between each component. But it's not necessary that whenever we use this button, we give a space at the top/bottom of the widget.
Still, if such cases arise, you can add the
SizedBox widget inside your custom widget.
Why Build a Custom Widget?
You might not see a direct benefit right away. But you may experience it in the future. Here’s a quick example for you.
Let’s assume you’ve built this app for a client. It has become a complex app and you’ve used this custom widget around 20 places in your app. The app is released and people enjoy using it.
About 6 months later, your client come back to you with the next version of changes. One of the items in the huge list is, “We’re coming up with a slight change in theme. Replace all the social media referral buttons so that they're an outlined shape and change the color to green”.
It is one simple configuration change in the custom widget. But imagine if you hadn't built the custom widget and had to copy/pasted the same code in all the 20 places. Then you'd have to carefully look at each place and replace each instance with care without touching other pieces of code.
These are the only 2 lines we have to change in our custom widget in this example:
But if you hadn't refactored your code, you'd have to make this change in 20 places.
I’ve pushed my code to the same GitHub repo. Refer to the
master branch for the non-refactored code and the
refactor branch for the refactored code.
Use Cases for Custom Widgets
Always use custom widgets for their specific use cases. For example, in our case, it is for Social media redirects. This widget should not be used in places which are unrelated to its context.
If you do, remember the above case where the client requirement was to change the design of only the social media referral buttons...but our change would be applied to all the other places where this widget was used. This would lead to unexpected bugs.
You should always write unit test cases for Custom Widgets which will help you mitigate any bugs earlier.
One more tip is to name your component in a more readable way. This helps other developers know what the widget does just by reading its name.
In our case, I've named it
CustomButton which makes no sense. Instead, some good alternatives would be
SocialButton, and so on which fit into our use case.
In this tutorial, you learned about building a custom widget by removing duplicated code/components.
Building custom widgets in Flutter promotes code reusability, maintainability, consistency, abstraction, flexibility, and community collaboration.
Custom widgets are a powerful tool in the Flutter developer’s toolkit, enabling you to create beautiful and functional user interfaces while maximizing efficiency and maintainability.