- Published on
How to configure Very Good CLI for Flutter projects
- Authors
- Name
- Nguyen Xuan Loc
Flutter Starter App (Core) π
This template is a Flutter starter application with VGV-opinionated best practices. It is the default template for the very_good create flutter_app
command.
Why Very Good Core?
We liked the [starter app][counter_app_link] provided by the flutter create
command, but found ourselves making adjustments every time we started a project. To help streamline the process, we decided to create our own starter template with the core standards and best practices we use at [Very Good Ventures][vgv]. Similar to the Flutter Starter app, Very Good Core contains a basic counter app with some additional features for a more robust app foundation.
App Features β¨
Multi-Platform Support - Support for iOS, Android, Web, and Windows (macOS and Linux coming soon!)
Build Flavors - Multiple flavor support for development, staging, and production
Internationalization Support - Internationalization support using synthetic code generation to streamline the development process
Sound Null Safety - No more null-dereference exceptions at runtime. Develop with a sound, static type system.
[Bloc][bloc_pub] - Layered architecture with
bloc
for scalable, testable code which offers a clear separation between business logic and presentationTesting - Unit and widget tests with 100% line coverage (integration tests coming soon!)
Logging - Extensible logging to capture uncaught Dart and Flutter exceptions
[Very Good Analysis][vga] - Lint rules for Dart and Flutter used internally at Very Good Ventures
Continuous Integration - Lint, format, test, and enforce code coverage using GitHub Actions
Getting Started π
:::info In order to start using Very Good Core you must have the [Flutter SDK][flutter_install_link] installed on your machine. :::
Installation π»
For first time users, start by installing the [Very Good CLI from pub.dev][cli_pub].
dart pub global activate very_good_cli
Create a new Flutter Project π
Then, you can use the very_good create flutter_app
command just like you would flutter create
. If desired, can specify a custom org name at time of generation with the --org
flag.
# Create a new Flutter app named my_app
very_good create flutter_app my_app --desc "My new Flutter app"
# Create a new Flutter app named my_app with a custom org
very_good create flutter_app my_app --desc "My new Flutter app" --org "com.custom.org"
Before running the project β‘
You'll have some errors when you run the app right now. It says that these import files don't exist. You only need to remove the library with the name: flutter_gen
Running the Project β‘
Once you have finished running very_good create
with the project directory of your choice, you can change directories into the new project directory and install the dependencies
cd my_app
flutter packages get
This project contains 3 flavors:
- Development
- Staging
- Production
Each flavor has dedicated entry point (main_development.dart
, main_staging.dart
, main_production.dart
) which can be used to setup, instantiate, and inject flavor-specific dependencies into the application.
For example:
- In
development
we might want to output logs to the console but instaging
andproduction
we might want to upload logs to [sentry.io][sentry_link] or [firebase analytics][firebase_analytics_link]. - We might want to configure an
ApiClient
orDatabaseClient
to point to a different endpoint for each flavor.
To run the desired flavor either use the launch configuration in VSCode or Android Studio, or use the following commands:
# Development
flutter run --flavor development --target lib/main_development.dart
# Staging
flutter run --flavor staging --target lib/main_staging.dart
# Production
flutter run --flavor production --target lib/main_production.dart
:::caution Flavors are only supported on iOS, Android, Web, and Windows. :::
Now your app is running π
Project Structure and Architecture ποΈ
Although Very Good Core is fairly basic in terms of functionality, the architecture and project structure is intended to scale from a simple hobby project to a large production ready application. A folder-by-feature project structure is used to maintain a modular project structure which helps the project scale as the number of features and/or developers increase.
In Very Good Core, there is only a single feature (counter
) to start but that will quickly change as you build out your project. Each feature usually consists of a view
and a cubit
(or bloc
). The view is responsible for holding the UI (Widgets
) which the user sees and interacts with and the cubit
/bloc
is responsible for containing the business logic needed to manage the state of the feature.
For more details, [read our best practices for building scalable apps][blog_scalable].
Testing π§ͺ
Very Good Core ships with 100% code coverage. :::note To learn more about why we believe 100% code coverage is important and other testing best practices, [read our guide to Flutter testing][blog_testing]. :::
Running Tests π§βπ¬
To run all unit and widget tests use the following command:
flutter test --coverage --test-randomize-ordering-seed random
To view the generated coverage report you can use lcov.
# Generate Coverage Report
genhtml coverage/lcov.info -o coverage/
# Open Coverage Report
open coverage/index.html
Working with Translations π
This project relies on [flutter_localizations][flutter_localizations] and follows the [official internationalization guide for Flutter][flutter_internationalization].
Adding Strings
- To add a new localizable string, open the
app_en.arb
file atlib/l10n/arb/app_en.arb
.
{
"@@locale": "en",
"counterAppBarTitle": "Counter",
"@counterAppBarTitle": {
"description": "Text shown in the AppBar of the Counter Page"
}
}
- Then add a new key/value and description
{
"@@locale": "en",
"counterAppBarTitle": "Counter",
"@counterAppBarTitle": {
"description": "Text shown in the AppBar of the Counter Page"
},
"helloWorld": "Hello World",
"@helloWorld": {
"description": "Hello World Text"
}
}
- Use the new string
import 'package:very_good_core/l10n/l10n.dart';
Widget build(BuildContext context) {
final l10n = context.l10n;
return Text(l10n.helloWorld);
}
Adding Supported Locales
Update the CFBundleLocalizations
array in the Info.plist
at ios/Runner/Info.plist
to include the new locale.
...
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>es</string>
</array>
...
Adding Translations
- For each supported locale, add a new ARB file in
lib/l10n/arb
.
βββ l10n
β βββ arb
β β βββ app_en.arb
β β βββ app_es.arb
- Add the translated strings to each
.arb
file:
app_en.arb
{
"@@locale": "en",
"counterAppBarTitle": "Counter",
"@counterAppBarTitle": {
"description": "Text shown in the AppBar of the Counter Page"
}
}
app_es.arb
{
"@@locale": "es",
"counterAppBarTitle": "Contador",
"@counterAppBarTitle": {
"description": "Texto mostrado en la AppBar de la pΓ‘gina del contador"
}
}
Continuous Integration π€
Very Good Core comes with a built-in [GitHub Actions workflow][github_actions] but you can also add your preferred CI/CD solution.
Out of the box, on each pull request and push, the CI formats
, lints
, and tests
the code. This ensures the code remains consistent and behaves correctly as you add functionality or make changes. The project uses [Very Good Analysis][vga] for a strict set of analysis options used by our team. Code coverage is enforced using the [Very Good Coverage GitHub Action][very_good_coverage].
Updating App Icons π±
When you create a new project, it has a default launcher icon. To customize this icon, you can do it by using the following steps for each platform.
Android
Review the [Material Design product icons][material_design_product_icons] guidelines for icon design.
In the
[project]/android/app/src/main/res/
directory, place your icon files in folders named using [configuration qualifiers][android_configuration_qualifiers]. The defaultmipmap-
folders demonstrate the correct naming convention.In
AndroidManifest.xml
, update the [application
][android_application_element] tagβsandroid:icon
attribute to reference icons from the previous step (for example,<application android:icon="@mipmap/ic_launcher" ...
).To verify that the icon has been replaced, run your app and inspect the app icon in the Launcher.
iOS
Review the [iOS app icons guidelines][ios_app_icon_guidelines].
In the Xcode project navigator, select
Assets.xcassets
in theRunner
folder. Update the placeholder icons with your own app icons.Verify the icon has been replaced by running your app using
flutter run
.