Package

Package - Store the Best of Your Code for Your Projects

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

How I Set Up Packages for Use Across Multiple Projects

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.

fire_core.

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

cd pathTo/packages

flutter create --template=package hello_core

Step 2: Configure Lint in pubspec.yaml

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

Step 3: Replace analysis_options.yaml

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

# 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:

include: ../../analysis_options.yaml

Learn more

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

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.

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:

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

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

Step 2 (Optional): Push to Remote

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:

How to Use Packages in a Real Project

Step 1: Define the Function in string.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

export 'dart/src/string.dart';

Step 3: Declare the Package in pubspec.yaml

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

Step 4: Use It in Your Project

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 | Support Me on Ko-fi

Last updated