Frontend Development Plan
Meetings
We held weekly meetings to discuss our plans. My friend and I began designing a mind map. Once we completed the mind map, we identified and resolved any issues. After that, we moved on to creating the basic wireframes.
We then reviewed the wireframes with our mentor and began working on the designs. We didn't document everything thoroughly, anticipating that some aspects would change. We are not experts in hunting, so we were unsure about the workflow for the hunter's diary and other related features.
I understand that not having everything well-documented can lead to problems during development. However it is what it is, and it will take a bit more time to develop.
Project setup
I had Flutter already installed so I just needed to install Melos this is a CLI tool that helps you manage Dart projects that contain multiple packages. Once I had everything installed, I created a directory on my computer.
lovec
├── apps
├── packages
├── melos.yaml
├── pubspec.yaml
└── README.md
I updated pubspec.yaml file with the following code:
name: lovec
environment:
sdk: '>=3.0.0 <4.0.0'
dev_dependencies:
melos: ^4.1.0
And then I updated melos.yaml
name: lovec
packages:
- apps/**
- packages/**
scripts:
analyze:
exec: dart analyze .
Creating Flutter apps
I navigated to apps folder and created lovec_mobile and lovec_web with
flutter create lovec_web --platforms web
and
flutter create lovec_mobile --platforms=android,ios
Then I pushed the directory to Github.
Development plan
We decided to start with our mobile app, structuring the packages in a way that allows for easy use in both mobile and web apps. This approach will simplify and clarify the development of the web app. Each package and app will contain a CHANGELOG.md to document all changes and releases, ensuring that both of us are aware of the updates.
We have the repo on GitHub with four different branch types.
- feature: for adding a new feature.
- bugfix: for fixing a bug.
- release: where we merge feature and bugfix branches.
- bugfix: for fixing a bug.
- release: where we merge feature and bugfix branches.
We will merge the release branches into the dev branch, then build the app and provide it to our mentor for testing. By maintaining a detailed CHANGELOG, it will be easy for our mentor to understand and test the specific changes we made.
If everything is approved, we will merge the dev branch into the master branch.
Example
Let's say our latest release is release/beta-0.0.1. This means the latest branch that was merged into development and then into master was release/beta-0.0.1.
Next, we create a new branch from the dev or master branch:
release/beta-0.0.2
From now on, we create new branches from the newly created branch release/beta-0.0.2.
For instance, if we add a new feature, we create a branch like feature/onboarding. Once the feature is complete, we document the changes in CHANGELOG.md. Then, we create a merge request to merge this branch into release/beta-0.0.2.
After several merge requests, we decide to release this version. In the release/beta-0.0.2 branch, we update CHANGELOG.md, moving the changes from the “Unreleased” section to the release name and adding the date. This is the final step before merging into the dev branch.
The CHANGELOG.md will look something like this (using emojis from gitmoji to make it more visually appealing):
# CHANGELOG
## release/beta-0.0.2 19.6.2024
- 💄 Enhancements:
- Introduced new files to define custom colors, routes, and constants for margins and padding to improve code organization.
- Wrote a simple test for util functions.
- ✨ New Features:
- Added onboarding functionality.
- Implemented an `appStartup` widget to handle app startup
## release/beta-0.0.1 1.6.2024
- Project setup
Coding
The logic must be separated from the UI, and the code should be documented as illustrated in the example below. Self-explanatory code does not require additional documentation.
/// A class containing static methods for validating form input fields.
class Validators {
/// Validates a password field.
///
/// [value] is the password input to be validated.
///
/// Returns an error message if validation fails, otherwise returns null.
static String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Field cannot be empty';
}
if (value.length < 6) {
return 'Password cannot be shorter than 6 characters';
}
return null;
}
/// Validates an email field.
///
/// [value] is the email input to be validated.
///
/// Returns an error message if validation fails, otherwise returns null.
static String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Field cannot be empty';
}
final emailRegExp = RegExp(r'^[^@]+@[^@]+.[^@]+');
if (!emailRegExp.hasMatch(value)) {
return 'Invalid email';
}
return null;
}
}
App structure
The app structure for our projects will follow a feature-first approach. You can learn more about this approach in this article by Andrea Bizzotto. I've learned a lot from his articles and I recommend you check them out.
I started with creating the app structure in the lib directory.
lib
├── src
│ ├── common_widgets
│ ├── exceptions
│ ├── features
│ ├── l10n
│ ├── routing
│ ├── shared
│ ├── utils
│ ├── app_startup.dart
│ ├── app.dart
└── main.dart
I've set up the translations (l10n).
With our project setup it was time to develop. I've created the first feature/onboarding.
Since the onboarding feature is only for the mobile app and not needed for the web app, I didn't move it to a package.
Authentication package
Now, I will show you how we set up the authentication package, as it will be used in both apps.
First, I created a package inside the packages directory using the following command:
flutter create --template=package authentication
Then, I ran the command:
melos bootstrap
Next, I added the authentication package to the pubspec.yaml file of each app.
Code
In the authentication package I created, I added a CHANGELOG.md file to document all the changes. This way, we will both be aware of all updates and modifications.
This is how the authentication package structure looks:
authentication
├── lib
│ ├── src
│ │ ├── utils
│ │ │ └── validators.dart
│ │ └── widgets
│ │ │ └── custom_text_form_field.dart
│ │ └── authentication_repository.dart
│ └── authentication.dart
├── test
│ ├── authentication_test.dart
│ └── validators_test.dart
├── CHANGELOG.md
└── README.md
The package includes validators, custom text form fields, and an authentication repository, all of which will be used by both apps.
The last thing we did was export the necessary files in authentication.dart. Here's how it looks:
authentication
├── data
│ └── authentication_repository.dart
├── domain
│ └── user.dart
├── presentation
│ └── login_screen.dart
Inside authentication_repository.dart, we imported the authentication package and used the repository with Riverpod:
import 'package:authentication/authentication.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'authentication_repository.g.dart';
@Riverpod(keepAlive:true)
AuthenticationRepository authenticationRepository(AuthenticationRepositoryRef ref) {
return AuthenticationRepository();
}
In the presentation layer, I used CustomTextFormField inside a Form and added validators. This approach is also quick to replicate for the web.
Testing
Each app and package will include tests. We will write unit tests, widget tests, and, if time permits, integration tests to ensure the quality of our code.
We will run our tests with Melos and write a script that runs all the tests. And test before merging the release branches into dev branch.
Conclusion
Throughout this article, I've walked you through the entire process of setting up and developing our apps and packages. I believe that by following these practices, we've made our development process more efficient and our code easier to understand and maintain. Remember, thorough documentation and testing are crucial to avoid potential issues and ensure smooth development.
I hope you enjoyed the article and found some inspiration or learned something new. Even though I don't have a lot of experience, I'm always happy to help.
I'd love to hear your feedback on how you would do things or any changes you'd suggest!