> For the complete documentation index, see [llms.txt](https://wong-coupon.gitbook.io/flutter/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wong-coupon.gitbook.io/flutter/my-flutter/foundations/clean-code.md).

# Clean code

Best Practices for Writing Clean and Understandable Code in Flutter Team Development

In the world of software development, maintaining **clean and clear code** not only improves work efficiency but also ensures the quality of the final product. Especially when working in a **team**, consistency in coding style and project architecture is crucial for all members to understand and continue developing the project smoothly. Below are some **best practices** to help your team keep the codebase **clean and easy to understand** while developing Flutter applications.

## **Establish a Common Coding Standard for All Members Using Mason**

Each developer typically has their own **coding style**, from naming variables and functions to structuring code blocks. This can make it difficult for others to read and understand the code, leading to **wasted time and reduced efficiency**.

#### **Define a Common Coding Standard:**

* Create **a guideline document** that establishes the project's coding conventions.
* This should include **naming conventions for variables, functions, classes**, folder structures, and **commenting styles**.

#### **Use Code Generation Tools for Standardization:**

* Manually following a **coding standard** can be complex and mentally exhausting.
* I have implemented a **solution using Mason** to **generate code templates** for all project structures, including **classes, enums, and UI components**.
* This approach allows team members to **contribute ideas, build upon them, and easily understand each other's code** since everything follows the same **template**. You can check out my article on **Mason** here:

{% content-ref url="/pages/6Yh2LXIXAdzRX2g1isw5" %}
[Mason](/flutter/my-flutter/foundations/mason.md)
{% endcontent-ref %}

## **Use Linter and Follow "Effective Dart"**

### **Follow "Effective Dart":**

* This is the **official guideline** from the **Dart team**, helping developers write **efficient and standardized code**.
* It includes **styling guidelines, documentation practices, and optimal language usage**.
* Constantly remembering and adhering to these policies **can be frustrating**.
* That’s why I use a library called **lint** to **monitor our code** and provide warnings. You can read more about it here: <https://pub.dev/packages/lint>.
* **Linter** helps detect **rule violations or coding errors** in real-time.
* Ensures code follows **best practices**, making it **readable and maintainable**.

### **How I Apply `lint` in My Project**

#### **Add the Lint Package to Your Project:**

```yaml
dev_dependencies:
  lint: ^2.3.0
```

#### **Configure `analysis_options.yaml`:**

```yaml
# This file configures the analyzer to use the lint rule set from `package:lint`

include: package:lint/strict.yaml # For production apps
# include: package:lint/casual.yaml # For code samples, hackathons and other non-production code
# include: package:lint/package.yaml # Use this for packages with public API


# You might want to exclude auto-generated files from dart analysis
analyzer:
errors:
  invalid_annotation_target: ignore
  lines_longer_than_80_chars: ignore
plugins:
- custom_lint
exclude:
  - packages/mason_core/**
  - '**.freezed.dart'
  - '**.g.dart'

# You can customize the lint rules set to your own liking. A list of all rules
# can be found at https://dart-lang.github.io/linter/lints/options/options.html
linter:
  rules:
  - unawaited_futures
rules:
  # Util classes are awesome!
  # avoid_classes_with_only_static_members: false

  # Make constructors the first thing in every class
  # sort_constructors_first: true

  # Choose wisely, but you don't have to
  prefer_double_quotes: true
  prefer_single_quotes: true
  avoid_dynamic_calls: true
  lines_longer_than_80_chars: false
  avoid_classes_with_only_static_members: true
  use_named_constants: true
  annotate_redeclares: true
```

#### **Then Run the Following Command:**

```bash
dart analyze
```

## **Implementing Clean Architecture**

### **Use Clean Architecture:**

* **Separate the app into layers**:
  * **Presentation**, **Domain**, **Data**.
* **Ensure one-way dependency flow**:
  * **Outer layers depend on inner layers** but not vice versa.
* I have written an article on **Clean Architecture** at here:

{% content-ref url="/pages/DOQf7tbR65m4o5Jlz62x" %}
[Flutter Clean Architecture](/flutter/my-flutter/architecture-state/flutter-clean-architecture.md)
{% endcontent-ref %}

### **Use Dependency Injection (DI):**

* **Manage dependencies** between classes using **DI (Dependency Injection)**.
* **Easier to replace and mock dependencies** for unit testing.

## **Separate Third-Party Plugins Using Dedicated Classes**

Relying too much on third-party plugins directly in the main codebase can cause issues if the plugin is no longer maintained or introduces breaking changes.

#### **Solution:**

* **Create Adapters or Service Layers:**
  * Encapsulate plugin usage within separate classes.
  * Use **interfaces** or **abstract classes** to define behaviors.

#### **Example:**

```dart
abstract class AuthenticationService {
  Future<User> login(String username, String password);
}

class FirebaseAuthService implements AuthenticationService {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;

  @override
  Future<User> login(String username, String password) async {
    // Sử dụng plugin Firebase Auth
    final userCredential = await _firebaseAuth.signInWithEmailAndPassword(
      email: username,
      password: password,
    );
    return userCredential.user;
  }
}
```

## **Separate Native Code into Independent Plugins**

Embedding native code directly into a Flutter project can be problematic when modifications are needed, code reuse in other projects is required, or Flutter updates cause Android/iOS code to become outdated.

#### **Solution:**

* **Create Separate Flutter Plugins for Native Code:**
  * Store Android and iOS code in standalone plugins.
  * Manage them as independent packages.
  * You can check out my article on plugins at here.

{% content-ref url="/pages/0WC9Kg79sKDw0COGDLUj" %}
[Plugin](/flutter/my-flutter/architecture-state/plugin.md)
{% endcontent-ref %}

## **Use Dart Commands to Keep Code Clean**

#### **Problem:**

Over time, code can become messy with unnecessary imports, unused code, or inconsistent formatting. However, automatic formatting in Flutter may sometimes make code harder to read, especially when lines exceed 80 characters.

#### **Solution:**

```bash
dart fix --apply
dart format --line-length=1000 .
dart analyze
```

***

## **Conclusion**

Maintaining a clean and structured codebase is essential for a successful project, especially in a team environment. By:

* Standardizing coding conventions
* Using linting tools and following best practices
* Applying clean architecture principles
* Isolating dependencies on third-party plugins
* Separating native code into independent plugins
* Leveraging Dart commands for code cleanup

You not only improve the quality of your project but also enhance team efficiency, minimize risks, and create a scalable and maintainable codebase for the future.

[Buy Me a Coffee](https://buymeacoffee.com/ducmng12g) | [Support Me on Ko-fi](https://ko-fi.com/I2I81AEJG8)
