
Insight

Insight

Insight
Modular app development with Flutter: Outsourcing major features into separate packages
Modular app development with Flutter: Outsourcing major features into separate packages
Jun 17, 2024




Photo by Vlado Paunovic on Unsplash
As an experienced Flutter app developer, I have learned the advantages of the multipackage approach in numerous projects. In this insight, I will show you how to outsource large features into their own packages in Flutter to make your project more efficient and organized. We will discuss both the advantages and the potential disadvantages of this approach.
A particular focus is on the feature-first approach, which has proven to be highly effective. By modularizing features into standalone packages, you increase the reusability of your code and promote better teamwork. I will explain step by step how to create a new package using the very_good_cli.
Using a practical example—the "Journal" package for an app—I will demonstrate the implementation and integration into a main app. This will give you a comprehensive insight into the structuring and modularization of Flutter projects.
Why a Multipackage Project?
In my experience as a Flutter developer, I have learned that the structure of a project has a significant impact on its scalability and maintainability. A multipackage project approach, where large features are outsourced into their own packages, offers numerous advantages that I can attest to firsthand.
Clarity and Maintainability
One of the biggest advantages of a multipackage project is the clarity and maintainability of the code. When you outsource large features into separate packages, your main project becomes much clearer. Each package can have its own responsibilities and dependencies, making it easier to understand and maintain the code. This separation of responsibilities leads to a cleaner and better-organized codebase, which is invaluable, especially in large projects.
Reusability of Code
Another advantage that I often utilize is the reusability of code. By outsourcing features into their own packages, you can reuse these packages across different projects. Imagine you are developing functionality for authentication or a UI widget. By creating these as standalone packages, you can easily reuse them in future projects without having to rewrite the code. This not only saves time but also ensures consistency and quality in your projects.
Better Teamwork and Parallel Development
In my projects, I often collaborate with other developers. A multipackage approach allows teams to work in parallel on different features without stepping on each other's toes. Each team member can work on their own package, minimizing conflicts in the code and maximizing productivity. Additionally, this approach facilitates onboarding new team members, as they can initially focus on a specific package rather than having to understand the entire project.
Challenges and How to Overcome Them
Of course, there are also challenges in implementing a multipackage project. One of them is managing the dependencies between the packages. It is important to have a clear structure and good documentation to maintain oversight. In my projects, using tools like very_good_cli
has proven to be extremely helpful. These tools assist you in creating and managing your packages and ensure a consistent structure.
Another potential issue is the increased complexity of integrating the various packages into the main app. Here, it is important to define clear interfaces and document the communication between the packages well. Through careful planning and regular code reviews, you can ensure that the integration goes smoothly.
In summary, the multipackage approach in Flutter offers numerous advantages that I have come to appreciate in my projects. From improved clarity and maintainability to the reusability of code and better teamwork—well-structured multipackage projects can make the difference between a successful and a failed project. By applying these techniques, you can significantly enhance the quality and efficiency of your app development.
The Feature-First Approach in the Context of Multipackage Projects
Through the feature-first approach, projects can be structured and efficiently designed. In the context of multipackage projects, this approach has its full effect and offers numerous advantages.
Explanation of the Feature-First Approach
The feature-first approach means that you organize your project structure by functionalities and features rather than by technical aspects such as database, UI, or network. Each feature is treated and developed as a standalone unit, making it easier to keep the entire code modular and well-maintained. In a multipackage project, this approach is reinforced by outsourcing each large feature into its own package.
Advantages of This Approach for Multipackage Projects
Clear Distinction of Responsibilities:
By separating features into their own packages, a clear distinction of responsibilities emerges. Each package contains only the code necessary for its specific feature. This not only simplifies understanding and maintenance of the code but also aids in troubleshooting.
Improved Reusability:
Another major advantage of the feature-first approach in multipackage projects is the improved reusability of code. A package created for a feature can easily be reused in other projects. For instance, you can use an authentication or logging feature across various apps without having to develop it anew each time.
Efficient Teamwork:
In larger teams, the feature-first approach promotes an efficient way of working. Each developer or development team can work on their own package without affecting the code of others. This parallel development accelerates the development process and reduces conflicts in the code.
Scalability and Flexibility:
The feature-first approach offers high scalability and flexibility. New features can be easily added as new packages without needing to change existing structures. This facilitates the extension and adaptation of your app to new requirements and market conditions.
Practical Examples
In one of my projects, I successfully applied the feature-first approach by outsourcing an extensive journal feature into its own package. This journal feature included functionalities such as creating, editing, and deleting entries, as well as synchronization with a backend.
By outsourcing into its own package, I was able to develop and test the feature independently of the rest of the app’s code. Additionally, this approach allowed me to reuse the journal feature later in other projects, which saved significant time and resources.
Another example is the implementation of a payment feature as a separate package. This package included all payment logic and could therefore be integrated easily into various apps without needing to adjust the code each time.
In summary, the feature-first approach in multipackage projects offers numerous advantages that make development more structured, efficient, and scalable. By clearly separating features into their own packages, you can significantly improve the maintainability and reusability of your code while optimizing teamwork.
Creating a New Package
In this section, I will show you how to create a new package in Flutter using the very_good_cli. Using the example of a journal package, I will explain the planning, preparation, and implementation of such a package.
Step-by-Step Guide to Creating a New Package
Installing the very_good_cli:
First, you need to install the very_good_cli, a helpful tool for managing Flutter projects. Open your terminal and execute the following command:
dart pub global activate very_good_cli
Creating a New Package:
With the very_good_cli, you can create a new package. Change to the directory where you want to create the package, and run the following command:
very_good create --template package your_package_name
This generates a basic package structure for you.
Example: Creating a "Journal" Package
Now I will show you how to implement this process using the example of a journal package.
Planning and Preparation
Before you begin implementation, thorough planning is necessary. Define the functionalities that your journal package should contain. In our case, the core features include creating, editing, and deleting journal entries, as well as synchronization with a backend.
Create a clear structure for your package to ensure easy extension and maintenance. A typical package could have the following directory structure:
lib/
├── src/
│ ├── models/
│ │ └── journal_entry.dart
│ ├── services/
│ │ └── journal_service.dart
│ └── journal.dart
└── journal.dart
Implementation of the Package
Defining Models:
Start by creating the data models. In lib/src/models/journal_entry.dart
, define the model for a journal entry:
class JournalEntry {
final String id;
final String title;
final String content;
final DateTime date;
JournalEntry({
required this.id,
required this.title,
required this.content,
required this.date,
});
}
Implementing Services:
Create the service class that contains the business logic. In lib/src/services/journal_service.dart
, implement the methods for creating, editing, and deleting entries:
class JournalService {
final List<JournalEntry> _entries = [];
List<JournalEntry> get entries => _entries;
void addEntry(JournalEntry entry) {
_entries.add(entry);
}
void updateEntry(JournalEntry entry) {
// Logic for updating an entry
}
void deleteEntry(String id) {
_entries.removeWhere((entry) => entry.id == id);
}
}
Providing Package API:
In lib/journal.dart
, provide the public API of your package:
library journal;
export 'src/models/journal_entry.dart';
export 'src/services/journal_service.dart';
Integration into the Main App:
To integrate the journal package into your main app, add it as a dependency in the pubspec.yaml
of your main app:
dependencies:
journal:
path: ../journal
After that, you can use the functionalities of the journal package in your app:
import 'package:journal/journal.dart';
void main() {
final journalService = JournalService();
// Using the JournalService methods
}
With this step-by-step guide and the example of the journal package, you now have the knowledge to outsource large features in Flutter as packages independently. This significantly improves the structure, maintainability, and reusability of your projects.
Integrating the Journal Package into a Main App
After we have created the journal package, the next step is to integrate this package into our main app. This integration allows you to seamlessly utilize the functionalities of the journal package in your app. Based on my experience, I will show you how to integrate your own package into a Flutter main app.
Steps to Integrate Your Own Package into the Main App
Adding the Package to pubspec.yaml:
First, you need to add the journal package as a dependency in your main app. Open the pubspec.yaml
file of your main app and add the package. Assuming the journal package is located in the directory ../journal
:
dependencies:
flutter:
sdk: flutter
journal:
path: ../journal
Save the changes and run flutter pub get
in the terminal to install the dependencies.
Importing the Package:
Now you can import the journal package in your Dart code. Open the file where you want to use the journal package and add the import:
import 'package:journal/journal.dart';
Using the Journal Functions:
Now you can utilize the functions of the journal package in your main app. In the following example, I will show you how to use the JournalService
to create, edit, and delete journal entries.
void main() {
final journalService = JournalService();
// A new journal entry
final newEntry = JournalEntry(
id: '1',
title: 'Erster Eintrag',
content: 'Dies ist der Inhalt des ersten Eintrags.',
date: DateTime.now(),
);
// Add entry
journalService.addEntry(newEntry);
// Show entry
print('Journal Entries: ${journalService.entries}');
// Edit entry
final updatedEntry = JournalEntry(
id: '1',
title: 'Erster Eintrag (aktualisiert)',
content: 'Dies ist der aktualisierte Inhalt des ersten Eintrags.',
date: DateTime.now(),
);
journalService.updateEntry(updatedEntry);
// Delete entry
journalService.deleteEntry('1');
}
In this example, the JournalService
is instantiated, and a new journal entry is created, added, edited, and finally deleted. These basic operations demonstrate how you can utilize the functionalities of the journal package in your main app.
Integration into the User Interface:
To integrate the functionalities of the journal package into the user interface of your app, you can, for example, display a list of journal entries and provide forms to create and edit entries.
import 'package:flutter/material.dart';
import 'package:journal/journal.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: JournalScreen(),
);
}
}
class JournalScreen extends StatelessWidget {
final JournalService journalService = JournalService();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Journal'),
),
body: ListView.builder(
itemCount: journalService.entries.length,
itemBuilder: (context, index) {
final entry = journalService.entries[index];
return ListTile(
title: Text(entry.title),
subtitle: Text(entry.content),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
journalService.deleteEntry(entry.id);
// Aktualisiere die Ansicht
},
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Logic for adding a new entry
},
child: Icon(Icons.add),
),
);
}
}
In this example, a simple user interface is created that displays a list of journal entries and provides the ability to delete entries and add new ones. The integration of the journal package into your main app is thus completed, and you can utilize the full functionality of the package.
By using your own package in your main app, you benefit from a clear separation of responsibilities, improved maintainability, and reusability of your code. This approach helps you make your projects more efficient and improve teamwork.
Conclusion
Through my extensive experience as a Flutter developer, I have clearly recognized the advantages of the multipackage project and the feature-first approach. These approaches not only offer clarity and maintainability, but also improved code reusability and promote teamwork.
Creating and integrating a new package, such as the journal package, may initially seem complex, but it is manageable with tools like the very_good_cli. The separation of large features into their own packages allows you to work in a structured way and increases the flexibility of your projects.
Despite some challenges, such as managing dependencies, the benefits clearly outweigh the drawbacks. By applying these strategies, you can significantly enhance the quality and efficiency of your app development. Use these methods to take your next Flutter project to the next level and benefit from the numerous advantages.
If you need support in implementing a multi-package project for your Flutter app, feel free to schedule an appointment with us.
All insights
All insights
“Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.”
“Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.”
Copyright ©2025. Julian Giesen. All rights reserved.
“Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.”