Flutter and Dart Clean Code

Written by, Codderlly on January 5, 2022

flutterdart

Introduction to Clean Code in Dart and Flutter

Clean code is an essential philosophy and practice in software development that focuses on writing code that is easy to understand, maintain, and modify.

In the context of Dart and Flutter, applying clean code not only improves code quality in the short term, but also makes the project easier to maintain and scale in the long term.

This approach is especially crucial in mobile app development, where complexity can quickly increase as new functionality is added and the user experience is refined.

Fundamental Principles of Clean Code

Simple example

    // Unclean code
    void f(List<int> x) {
        var t = 0;
        for (var i = 0; i < x.length; i++) {
            t += x[i];
        }
        print(t);
    }

    // Clean code
    void calculateAndPrintSum(List<int> numbers) {
        final sum = numbers.reduce((value, element) => value + element);
        print('The sum is: $sum');
    }

In this example, readability is significantly improved by renaming f to calculateAndPrintSum and changing x to numbers, which makes the purpose of the code immediately apparent.

    int add(int a, int b) => a + b;

This function is pure, as its result depends only on the parameters a and b, without altering the global state or depending on external variables.

Advanced example

    // Unclean code
    class UserMgr {
        void crtUsr(String n, String e, int a) {
            // Logic to create user
        }

        void updUsr(int id, String n, String e, int a) {
            // Logic to update user
        }

        void delUsr(int id) {
            // Logic to delete user
        }
    }

    // Clean code
    class UserManager {
        Future<User> createUser(UserCreationDto userDto) async {
            final user = User.fromDto(userDto);
            await _validateUser(user);
            return await _userRepository.save(user);
        }

        Future<User> updateUser(int id, UserUpdateDto updateDto) async {
            final existingUser = await _userRepository.findById(id);
            final updatedUser = existingUser.applyUpdate(updateDto);
            await _validateUser(updatedUser);
            return await _userRepository.update(updatedUser);
        }

        Future<void> deleteUser(int id) async {
            await _userRepository.delete(id);
        }

        Future<void> _validateUser(User user) async {
            // Validation logic
        }
    }

In this example, we can see how applying principles such as single responsibility and separating logical layers (validation, persistence, etc.) make the UserManager class much more maintainable and extensible.

Complementary Best Practices

    // This method makes sure that the user has a valid age before saving it to the database.
    Future<void> _validateUser(User user) async {
        // Validation logic
    }

In this case, a short comment explains why the validation is being performed, which can be helpful for those unfamiliar with the context of the code.

    extension DateTimeExtensions on DateTime {
        bool isWeekend() => this.weekday == DateTime.saturday || this.weekday == DateTime.sunday;
    }

With this extension, any DateTime instance can now use the isWeekend() method naturally, improving code expressiveness and reusability.

Summary

Clean Code in Dart and Flutter is not just a matter of following rules or conventions, but of adopting a mindset that prioritizes code quality in terms of readability, simplicity, and maintainability.

By applying these principles, you not only improve the developer experience, but you also create code that is easier to scale, test, and debug, resulting in more robust and reliable software.