Refactoring code in Flutter

In previous sections we have learned a few key design-components in Flutter. Not only we have built our first Flutter App, but also we have learned how the Widget tree in Flutter works. However, we can improve this design without breaking the functionality.

In Software Engineering it is known as “Refactoring Code”.

As we progress, we will find that Flutter App can grow into a huge Application.

As a result, it is not a good idea that the entry point of our Flutter App are staffed by all Widgets.

So far, we have done exactly the same thing. The top-level function main() file are occupied with too many Widgets.

Let us see the Business Card App first.

Flutter Business Card
Flutter Business Card in Chrome Browser

In the body section, we have used one Image, two Container and two Card Widgets.

As a consequence, the length of the code increases unnecessarily. You can take a look at the full code snippet at this GitHub repository.

For example, we have used two Container Widgets like this:

Container(
              padding: const EdgeInsets.all(5.0),
              child: Text(
                'Lion King',
                style: GoogleFonts.laila(
                  textStyle: Theme.of(context).textTheme.headline6,
                  fontSize: 50,
                  fontWeight: FontWeight.w700,
                  color: Colors.orange.shade900,
                ),
              ),
            ),
            Container(
              padding: const EdgeInsets.all(5.0),
              child: Text(
                'Flutter Developer',
                style: GoogleFonts.lato(
                  fontSize: 30,
                  fontWeight: FontWeight.w900,
                  color: Colors.orange.shade500,
                ),
              ),
            ),

Both the Container Widgets have some common value which is constant. As we see, the padding is same.

However, the String data changes. Also the style property takes two different values.

Therefore, we can create a custom Container Which might pass two values that are changing.

Right?

Th same way we can refactor the Card Widgets.

Why?

Because we have also used two Card Widgets like the following.

Card(
              margin: const EdgeInsets.fromLTRB(40.0, 5.0, 40.0, 5.0),
              color: Colors.orange.shade100,
              child: const ListTile(
                title: Text('100 123 456'),
                trailing: Icon(Icons.phone_android),
              ),
            ),
            Card(
              margin: const EdgeInsets.fromLTRB(40.0, 5.0, 40.0, 5.0),
              color: Colors.orange.shade100,
              child: const ListTile(
                title: Text('to@iamlion.com'),
                trailing: Icon(Icons.email_outlined),
              ),
            ),

I hope you get the idea.

By refactoring our code we can break our long code into small modules. Now each module acts as a different objects that can talk to another object.

Why do we refactor code?

Flutter encourages us to refactor code and break the tedious long code into small modules.

Therefore, our aim is simple. We do not want to change the functionality of the code.

Instead, we want to rewrite the Widget by passing common variables.

As a result, we have created two folders in our “lib” folders. We have named it “view” and “controllers”.

Now, in the “lib” folder, we have only one file – main.dart.

import 'package:flutter/material.dart';
import 'view/app.dart';

main() {
  runApp(const App());
}

As a result, our entry point looks cleaner than before. It is no longer more than 100 lines of code.

However, we have imported another file from the “view” folder.

The “app.dart” file in the “view” folder is a Widget which returns the MaterialApp Widget whose home property expects the Home page.


  
import 'package:flutter/material.dart';

import 'app_home.dart';

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  static const title = 'The Lion King Business Card';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: title,
      home: AppHomePage(
        title: title,
      ),
    );
  }
}

Consequently, the home page is another widget in the “view” folder.

The file “app_home.dart” returns the Scaffold Widget.

Moreover, now we have two Custom Container and Card Widgets in place. As we have kept these two custom Widgets in our “controller” folders, we should import them.

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

import '../controller/custom_card.dart';
import '../controller/custom_container.dart';

class AppHomePage extends StatelessWidget {
  const AppHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Colors.amber[900],
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const CircleAvatar(
              backgroundImage: AssetImage(
                'images/lion-king.jpg',
              ),
              radius: 75.0,
            ),
            CustomContainer(
              data: 'Lion King',
              textStyle: GoogleFonts.laila(
                textStyle: Theme.of(context).textTheme.headline6,
                fontSize: 50,
                fontWeight: FontWeight.w700,
                color: Colors.orange.shade900,
              ),
            ),
            CustomContainer(
              data: 'Flutter Developer',
              textStyle: GoogleFonts.lato(
                fontSize: 30,
                fontWeight: FontWeight.w900,
                color: Colors.orange.shade500,
              ),
            ),
            SizedBox(
              height: 16.0,
              width: 60.0,
              child: Divider(
                color: Colors.orange.shade300,
                thickness: 2.0,
              ),
            ),
            const CustomCard(
              title: Text('100 200 300'),
              icon: Icon(Icons.phone_android_rounded),
            ),
            const CustomCard(
              title: Text('to@iamlion.com'),
              icon: Icon(Icons.email_outlined),
            )
          ],
        ),
      ),
    );
  }
}

As we can see in the above code, the custom Container Widgets passes two properties that change the value.

The same is true for the two custom Card Widgets.

What is refactoring method?

Let us take a look at the refactoring method.

We have created a custom Container Widget that passes two named parameters.

import 'package:flutter/material.dart';

class CustomContainer extends StatelessWidget {
  final String data;
  final TextStyle textStyle;
  const CustomContainer({
    Key? key,
    required this.data,
    required this.textStyle,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(5.0),
      child: Text(
        data,
        style: textStyle,
      ),
    );
  }
}

Now let us have a look at the “view” folder, where we have passed two different values.

We call the CustomContainer Widget and pass two parameters as the following.

CustomContainer(
              data: 'Lion King',
              textStyle: GoogleFonts.laila(
                textStyle: Theme.of(context).textTheme.headline6,
                fontSize: 50,
                fontWeight: FontWeight.w700,
                color: Colors.orange.shade900,
              ),
            ),

The same way we have created two custom Card Widgets.

We have set two named parameters. One is title and the other is icon.

import 'package:flutter/material.dart';

class CustomCard extends StatelessWidget {
  final Text title;
  final Icon icon;
  const CustomCard({
    Key? key,
    required this.title,
    required this.icon,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.fromLTRB(40.0, 5.0, 40.0, 5.0),
      color: Colors.orange.shade100,
      child: ListTile(
        title: title,
        trailing: icon,
      ),
    );
  }
}

Now, in the “view” folder, the home page calls the custom Card widgets with required values.

The same thing we have done for the custom Container Widgets.

const CustomCard(
              title: Text('100 200 300'),
              icon: Icon(Icons.phone_android_rounded),
            ),

Now, run the refactored Flutter App.

The phone number no longer remains the same.

Refactoring code in Flutter changes the phone number
Refactoring code in Flutter changes the phone number

The idea is clear.

We refactor the code, because we can call the custom Widgets anywhere in our Flutter App.

Whenever we call them, we need to pass two values.

To implement the idea you can download the code snippet from the GitHub Repository.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Technical blog

Twitter


Posted

in

, ,

by

Comments

One response to “Refactoring code in Flutter”

  1. […] our previous sections we have learned a lot of new features in Flutter. That includes how to refactor code, anonymous functions, and moreover, the role of Stateful widget. We have also built a simple Photo […]

Leave a Reply