# Package

**Creating and managing packages significantly reduces coding time, allows for code reuse, and makes project maintenance easier. Below are some key benefits of using packages:**

* **Code Reusability**

  When you create packages for common features or components, you can reuse them across multiple projects without rewriting code. This saves both time and effort.
* **Easy Management and Maintenance**

  Packages are well-organized, making them easy to update and maintain. When a feature needs to be changed or upgraded, you can simply update the package without affecting the entire project.
* **Enhanced Consistency**

  Using packages ensures that your code remains consistent and follows the same rules and standards across different projects.
* **Faster Development**

  With pre-built packages, you can focus on developing project-specific features without spending time on fundamental components.
* **Collaboration and Sharing**

  Packages can be shared with the community or within a development team, enhancing collaboration and leveraging collective knowledge.
* **Reduced Errors**

  Reusing well-tested and stable packages minimizes the risk of encountering new bugs during application development.

Below is a detailed guide on how to create and manage packages:

## How I Set Up Packages for Use Across Multiple Projects

<figure><img src="https://2074953091-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F63bX4jdEctx3wiLiur0x%2Fuploads%2Fgit-blob-6f724c52888f8d0d9a91663208535d318851e9e1%2Fimage1%20(7).png?alt=media" alt=""><figcaption><p>How I Set Up Packages for Use Across Multiple Projects</p></figcaption></figure>

I have created a directory named **packages** to store all the packages I develop.

I categorize them into two types:

* The first type belongs to **ancestor\_core**, which consists of foundational packages where I have encapsulated functions following an object-oriented programming style. When using these packages, you simply call them without worrying about the complex internal processing and setup.
* The second type consists of **UI packages**, which contain all the UI-related widgets and components. These help minimize a significant amount of code and can be reused in future projects.

Now, let's dive into one of them: **fire\_core**.

<figure><img src="https://2074953091-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F63bX4jdEctx3wiLiur0x%2Fuploads%2Fgit-blob-20b3886056f225539522f736c470daffce497ab6%2Fimage2%20(3).png?alt=media" alt=""><figcaption><p><strong>fire_core</strong>.</p></figcaption></figure>

This package stores all the Firebase-related code I have written. In the future, if a new project requires Firebase, I can simply import this package and reuse the previously written code.

## Setting Up a Package

#### Step 1: Run

```bash
cd pathTo/packages

flutter create --template=package hello_core
```

#### Step 2: Configure Lint in `pubspec.yaml`

```bash
cd pathTo/hello_core
flutter pub add lint
flutter pub add test
flutter pub add mockito
```

The **lint** package helps detect errors in Dart code, ensuring a clean and well-formatted codebase.\
[Learn more](https://pub.dev/packages/lint)

#### Step 3: Replace `analysis_options.yaml`

Replace the **project's** `analysis_options.yaml` file to include error analysis configurations from the `lint` package.

```yaml
# This file configures Dart analysis using rules from package:lint

include: package:lint/strict.yaml # For production applications
# include: package:lint/casual.yaml # For samples, hackathons, and non-production code
# include: package:lint/package.yaml # For packages with public APIs

# Exclude generated files from Dart analysis
analyzer:
  errors:
    invalid_annotation_target: ignore
  plugins:
    - custom_lint
  exclude:
    - packages/mason_core/**
    - '**.freezed.dart'
    - '**.g.dart'

# Customize linting rules as desired. A complete list of rules can be found at:
# https://dart-lang.github.io/linter/lints/options/options.html
linter:
  rules:
    - unawaited_futures
rules:
  # Utility classes are great!
  # avoid_classes_with_only_static_members: false

  # Keep constructors at the top of each class
  # sort_constructors_first: true

  # Good practices, but optional
  prefer_double_quotes: true
  prefer_single_quotes: true
  avoid_dynamic_calls: true
  lines_longer_than_80_chars: true
  avoid_classes_with_only_static_members: true
  use_named_constants: true
```

For **package-level** `analysis_options.yaml`, replace it with:

```yaml
include: ../../analysis_options.yaml
```

[Learn more](https://docs.flutter.dev/packages-and-plugins/developing-packages)

To quickly generate the folder and file structure as shown in the images, you can use **Mason**. If you're not familiar with Mason, check out my **brick** here: [dr\_folder\_package](https://brickhub.dev/search?q=dr_folder_package)

## Synchronizing Package Folders Across Different Projects

My idea is to **sync all packages using Git**, with each package in a project stored in a separate branch. Later, you can merge them all into one if needed.

### Setting Up in a Project

The idea is to create a **packages project** on Git and link it to our local `packages` directory.

```bash
git clone https://gitlab.com/yourdomain/packages.git
cd pathTo/packages
git checkout master
git branch feature-snap
git checkout feature-snap
```

If the directory already exists:

```bash
cd pathTo/packages
git init
git remote add origin https://gitlab.com/yourdomain/packages.git
git fetch
```

You might find this process a bit tedious, so why not create a **separate package project** that is independent of the main project?

The benefit of my approach is that when coding, you can **easily add, edit, and search within packages** while keeping all package code within the main project. This ensures safety in case someone **locks your packages on Git**.

### Usage

**Step 1: Commit Changes**

```bash
cd pathTo/packages
git add .
git commit -am "changed"
git push origin feature-snap
```

**Step 2 (Optional): Push to Remote**

```bash
cd pathTo/packages
git fetch
git pull origin master --rebase
```

## How to Use Packages in a Real Project

Let's say I have a function to **print JSON to the log in a properly formatted way**. I will put it inside a class named `string.dart`, which is located in a package called **dart\_core**, as shown below:

<figure><img src="https://2074953091-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F63bX4jdEctx3wiLiur0x%2Fuploads%2Fgit-blob-7407ebbf0ff158229d60001d2186ad927adf2a96%2Fimage3%20(1).png?alt=media" alt=""><figcaption><p>How to Use Packages in a Real Project</p></figcaption></figure>

#### Step 1: Define the Function in `string.dart`

```dart
import 'dart:convert';

extension MyStringHelper on String {
  String get myPrintStringJson {
    if (isJSON(this)) {
      const JsonDecoder decoder = JsonDecoder();
      const JsonEncoder encoder = JsonEncoder.withIndent('  ');

      final object = decoder.convert(this);
      final prettyString = encoder.convert(object);
      var string = "";
      prettyString.split('\n').forEach((element) => string = '$string\n$element');
      return string;
    } else {
      var format = this;
      format = format.replaceAll('{', '\n{\n  ');
      format = format.replaceAll(',"', ',\n  "');
      format = format.replaceAll('}', '\n}');
      format = format.replaceAll('":', '" : ');
      format = format.replaceAll('[', '[\n  ');
      format = format.replaceAll(']', '\n]');
      return format;
    }
  }
}
```

#### Step 2: Export It in `dart_core.dart`

```dart
export 'dart/src/string.dart';
```

#### Step 3: Declare the Package in `pubspec.yaml`

```yaml
dependencies:
  flutter:
    sdk: flutter
  
  dart_core:
    path: packages/ancestor_cores/dart_core
```

#### Step 4: Use It in Your Project

```dart
import 'package:dart_core/dart_core.dart';

myLog.warning((await myLocation.locationAccuracy).toString().myPrintStringJson);
```

And that completes the process of **creating and using a function inside a package**.

Now, you can **store and reuse** your best utility functions whenever needed. This method is especially useful for **Firebase projects**, as you can reuse the setup across multiple projects **without having to configure it again**.

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