Base Project

Creating a Base Project in Flutter

This article is complete when combined with Clean Architecture and Package.

Why Do You Need a Base Project?

Any Flutter developer, after working on multiple projects, wants to create a project template to speed up the initialization of future projects. However, extracting reusable parts and setting up the project structure from scratch can be time-consuming and tedious.

To solve this, I explored various ways to automate the creation of essential files and folders. One approach is using Windows command-line tools to generate files, but a more powerful tool that can save time for you and your team is Mason.

If you're unfamiliar with Mason, check out my guide: Boost Your Flutter Development Efficiency with Mason

Essential Libraries for a Base Project

Below is a list of important libraries that I use to ensure the code is clean, scalable, and maintainable.

Dependency Injection with get_it

get_it on pub.dev

  • Helps with data retrieval and dependency injection.

  • Allows access to repositories anywhere in the project without manually passing data between classes.

Routing Management with go_router

go_router on pub.dev

  • Official Flutter routing library—works well for both mobile and web.

  • Provides a clear and scalable way to manage navigation.

State Management with flutter_bloc

flutter_bloc on pub.dev

  • One of the most popular and efficient state management libraries.

  • If you're new to bloc, check out my guide:

Bloc

Code Generation with json_serializable & freezed

json_serializable on pub.dev freezed on pub.dev

  • Reduces repetitive boilerplate code.

  • Automatically generates helpful functions like copyWith, fromJson, and toJson for easier data handling.

Code Quality Improvement with lint

lint on pub.dev

  • Enforces Effective Dart coding standards.

  • Ensures consistent and readable code across teams.

  • If you haven’t read Effective Dart, check it out here: Effective Dart.

App Icon Setup with flutter_launcher_icons

flutter_launcher_icons on pub.dev

  • Automatically generates app icons for both Android and iOS.

  • Saves time when setting up project branding.

Creating a Base Project with Mason

Below is how I structure the lib/ directory in my base project:

lib/  
│── main.dart           # Runs the app, supports dev, staging, production environments  
│── router/             # Manages routes using `go_router`  
│── helper/             # Contains utility functions (e.g., localization, utilities)  
│── features/           # Stores the app’s main features, clearly separated  
│── config/             # Holds environment settings, API configurations, etc.  

Outside of the lib/ folder, I also set up:

assets/                 # Stores images and localization files  
HELP.md                 # Project setup guide  
CHANGELOG.md            # Records version changes  
analysis_options.yaml   # Linting rules  
pubspec.yaml            # Dependency management  
build.yaml              # Build configuration  

You can check out my base project template, which is built using Mason, here: dr_base_project on BrickHub

Setting Up the Development Environment

A well-configured development environment helps prevent issues before the app reaches users. To easily switch between environments (dev, staging, production) without manual changes, I use an enum to store environment settings.

Defining the Environment Enum

late EnviEnum ourEnvi;

enum EnviEnum {
  develop(
    code: 0,
    title: "develop",
    config: _BaseUrlConfig(baseUrl: "http://192.168.20.82:5173/"),
  ),
  staging(
    code: 1,
    title: "staging",
    config: _BaseUrlConfig(baseUrl: "https://staging-partner.kdmp.net/"),
  ),
  product(
    code: 2,
    title: "product",
    config: _BaseUrlConfig(baseUrl: "https://partners.kdmp.net/"),
  );

  const EnviEnum({required this.code, required this.title, required this.config});
  final int code;
  final String title;
  final _BaseUrlConfig config;

  String get baseUrl => config.baseUrl;
}

class _BaseUrlConfig {
  final String baseUrl;
  const _BaseUrlConfig({required this.baseUrl});
}

You can extend this enum to include additional configurations like API keys, logging settings, and more.

Initializing the App for Each Environment

Development Environment (Local Server)

void main() {
  ourEnvi = EnviEnum.develop;
  myMain(() {
    setup();
    runApp(const App());
  });
}

Staging Environment (Internal Testing Before Release)

void main() {
  ourEnvi = EnviEnum.staging;
  myMain(() {
    setup();
    runApp(const App());
  });
}

Production Environment (Final Release for Users)

void main() {
  ourEnvi = EnviEnum.product;
  myMain(() {
    setup();
    runApp(const App());
  });
}

Conclusion

Setting up a base project in Flutter saves a lot of time when starting a new project, making it easier to maintain and scale.

  • Use Mason to automatically generate necessary files and directories.

  • Leverage popular libraries such as go_router, flutter_bloc, json_serializable, and freezed to enhance development efficiency.

  • Configure dev, staging, and production environments to better manage application environments.

Buy Me a Coffee | Support Me on Ko-fi

Last updated