Template for starting Flutter app based on Very Good CLI with some additional stuff. The goal of that is to use Very Good CLI and then apply belowe instructions in order to add some additional stuff. In the future the idea is to create an automatic script/package for that.
- Version manager
- Build the app using Xcode
- Secrets
- Golden - Screenshot Tests
- Linter
- Githooks
- Helpful scripts
- Github Workflows
- Firebase
- Signing the app
- Getting Started π
- Running Tests π§ͺ
- Working with Translations π
- TODO LIST
This project uses mise for managing version of Flutter and other required tools. asdf should also work fine.
.tool-versions file in root project directory defines Flutter version used for that project. Install mise and type the following command to install that Flutter version.
mise installYour Java version must be compatible with Gradle. Gradle version is defined inside android/gradle/wrapper/gradle-wrapper.properties under distributionUrl param.
Here you can check compatibility matrix for Gradle and Java https://docs.gradle.org/current/userguide/compatibility.html#java
In order to be sure that Flutter uses correct Java version it's good to set it manually. Firstly run:
mise where javacopy the output and use that Java path to assign in to our Flutter version:
flutter config --jdk-dir /Users/marcinhradowicz/.local/share/mise/installs/java/openjdk-21.0.2Run
flutter doctor --verboseand verify if Flutter and Java version are correct.

In order to build the app using Xcode you need to set entry point of the app, because the default one is lib/main.dart, but we have main_production.dart, main_staging.dart and main_development.dart.
# Secrets
secrets/keys
tools/secrets/encryption_password.txt
/lib/core/envs/env.g.dart
Secrets are kept in keys/*.env files. Respectively for the environment, these can be named like staging.env or production.env. The example file should look like this:
API_KEY=secret
This project uses envied to store private host URLs, private keys, etc. All the secrets have to be defined in lib/core/env.dart file.
Add packages:
flutter pub add envied
flutter pub add dev:envied_generatorTo generate the file run build_runner:
dart run build_runner build --delete-conflicting-outputs- All secret files and directoriee are stored in
secrets/encrypted_secrets.tar.gz.enc. - All secret files and directoriee are defined in
secrets/secret_files_list.txt. e.g.
android/app/keystore
android/app/key.properties
secrets/keys
firebase.json
tools/secrets/encrypt_secrets.shscript takes all the files defined intools/secrets/secret_files_list.txt, encrypts them and zips intosecrets/encrypted_secrets.tar.gz.enc.tools/secrets/decrypt_secrets.shscript unzips all the files defined intools/secrets/secret_files_list.txtand places them in appropriate places.- In order to use both above scripts you need to provide password in
tools/secrets/encryption_password.txt. In order to get password contact: marcin.hradowicz@gmail.com You can generate a new password using that commandopenssl rand -base64 16. tools/secrets/purge_secrets.shdeletes all secrets defined intools/secrets/secret_files_list.txt.
In order to make your environment variables available in ios/Runner/Info.plist file (e.g. when adding integration with Facebook by adding facebook_client_token secret) you need to reproduce below steps from screenshots for EVERY environment staging, production, development.
Copy .env to native code (so we can use it inside Info.plist) - part1
echo "secrets/keys/staging.env" > ${SRCROOT}/.envfileCopy .env to native code (so we can use it inside Info.plist) - part2
${SRCROOT}/.symlinks/plugins/flutter_config_plus/ios/Classes/BuildXCConfig.rb ${SRCROOT}/ ${SRCROOT}/Flutter/tmp.xcconfigThen add flutter_config_plus package. It is used in above script.
flutter pub add flutter_config_plusNext add this lines to ios/Flutter/Debug.xcconfig AND ios/Flutter/Release.xcconfig
// Config for storing .env inside native iOS code, so it can be used inside Info.plist.
#include "tmp.xcconfig"
Remember to add to ios/.gitignore
# These two files are used for storing .env inside native iOS code, so it can be used inside Info.plist.
/Flutter/tmp.xcconfig
/.envfile
Now you should be able to use your variables in ios/Runner.Info.plist like that:
<key>FacebookAppID</key>
<string>$(facebook_app_id)</string>Sometimes you need to access some secrets inside AppDelegate.swift file e.g. to set up google_maps_flutter package. Firstly the secret has to be defined in Info.plist file as shown in the previous section. Then you can extract it in this way:
let myCustomKey: String = Bundle.main.object(forInfoDictionaryKey:"FacebookAppID") as? String ?? ""In order to make your environment variables available in android/app/build.gradle file (e.g. when adding integration with Facebook by adding facebook_client_token secret) paste that code inside android/app/build.gradle
//////////// ! Environment variables
def Properties flutterEnvProduction = new Properties()
def envFileProduction = file("../../secrets/keys/production.env")
if (envFileProduction.exists()) {
flutterEnvProduction.load(new FileInputStream(envFileProduction))
}
def Properties flutterEnvDevelopment = new Properties()
def envFileDevelopment = file("../../secrets/keys/development.env")
if (envFileDevelopment.exists()) {
flutterEnvDevelopment.load(new FileInputStream(envFileDevelopment))
}
def Properties flutterEnvStaging = new Properties()
def envFileStaging = file("../../secrets/keys/staging.env")
if (envFileStaging.exists()) {
flutterEnvStaging.load(new FileInputStream(envFileStaging))
}
// Example of usage
// flutterEnvStaging.getProperty('facebook_client_token')
//////////// !then you can use it in productFlavors section.
Everything inside manifestPlaceholders is available in AndroidManifest.xml file.
android {
productFlavors {
prod {
dimension "default"
applicationIdSuffix ""
resValue "string", "facebook_client_token", flutterEnvProduction.getProperty('facebook_client_token')
manifestPlaceholders = [
appName: "Starting Flutter Project",
facebookContentProvider: "com.facebook.app.FacebookContentProvider${flutterEnvProduction.getProperty('facebook_app_id')}",
]
}
staging {
dimension "default"
applicationIdSuffix ".stg"
resValue "string", "facebook_client_token", flutterEnvStaging.getProperty('facebook_client_token')
manifestPlaceholders = [
appName: "[STG] Starting Flutter Project",
facebookContentProvider: "com.facebook.app.FacebookContentProvider${flutterEnvStaging.getProperty('facebook_app_id')}",
]
}
development {
dimension "default"
applicationIdSuffix ".dev"
resValue "string", "facebook_client_token", flutterEnvStaging.getProperty('facebook_client_token')
manifestPlaceholders = [
appName: "[DEV] Starting Flutter Project",
facebookContentProvider: "com.facebook.app.FacebookContentProvider${flutterEnvStaging.getProperty('facebook_app_id')}",
]
}
}
}In order to get access to secret inside android/app/src/main/kotlin/com/lilatee/startingproject/starting/flutter/project/MainActivity.kt file you need to add resValue property inside productFlavors in android/app/build.gradle file.
productFlavors {
staging {
dimension "default"
resValue "string", "example_key", flutterEnvStaging.getProperty('EXAMPLE_KEY')
applicationIdSuffix ".stg"
manifestPlaceholders = [appName: "[STG] Starting Flutter Project"]
}
}Then you can use that value inside MainActivity.kt like that:
package com.lilatee.starting_flutter_project
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val exampleKey: String = getString(R.string.example_key)
}
}Project uses golden_test package for golden test and golden_toolkig for fixing fonts and icons on screenshots.
- Add package to
dev_dependencies. For now there is an opened PR that adds support for Routers.
dev_dependencies:
golden_test:
git:
url: https://github.com/dmkasperski/golden_test
ref: add_support_for_router-
Copy
test/helpers/golden_test_runner.dartfile. It's a function that is used to run every golden test. It handles default state of every golden test. -
Copy
test/flutter_test_config.dartfile. It has some basic stuff that is used in every test. Here we use golden_toolkit package for fixing fonts and icons on screenshots. -
Add configuration in
.vscode/launch.jsonto run test from test files directly.
{
"configurations": [
{
"name": "Goldens",
"request": "launch",
"type": "dart",
"codeLens": {
"for": [
"run-test",
"run-test-file"
]
},
"args": [
"--update-goldens"
]
},
]
}- Add new rules to gitignore in the main directory of the project
# Golden tests failures
**/failures/**.png
In VS Code you should see Goldens button above runGoldenTest function.

Project uses mostly leancode_lint with some minor changes.
Add a required packages
dart pub add leancode_lint custom_lint --devand create analysis_options.yaml file in the root of the project
In order to enable githooks in the following configuration remember to run
git config core.hooksPath .githooks Uses dart format command to format all files to a proper format with a line length equals to 120. It uses rules defined in analysis_options.yaml file.
File is present in .githooks/pre-commit.
Runs:
./tools/dart_analysis.shscript,flutter test testcommand to run tests insidetestdirectory;
dart_analysis.sh script uses dart_code_linter for finding for unused parts of the codes and custom_lint for running custom lints so please remmeber to add both of these packages:
dart pub add --dev dart_code_linter custom_lintFiles are present in .githooks/pre-push and tools/dart_analysis.sh.
tools/fix_flutter_environment.sh- Script for removing everything (I hope so) related to Flutter. Run it if something does not work and maybe it will fix it.
.github/workflows/common.env stores common versions of tools for every workflow in order to be consistent with the version of tools across different workflows.
In order to run tests on CI we have to provide a placeholder values for our environments variables. For that case please remember to fill defaultValue parameter when adding a new field in /lib/core/envs/env.dart file.
e.g
@EnviedField(defaultValue: 'example_key_development', varName: 'EXAMPLE_KEY', obfuscate: true)
static final String key = _Env.key;File: .github/workflows/update-cache.yml
Access restrictions provide cache isolation and security by creating a logical boundary between different branches. For example, a cache created for the branch feature-a (with the base master) would not be accessible to a pull request for the branch feature-b (with the base master). But cache created for a default branch of a repository is accesible for every branch. So this workflow updates Cache that then can be accessible by other branches.
GitHub Documentation here: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache
File: .github/workflows/test-and-analyze.yml
Responsible for running ./tools/dart_analysis.sh script and flutter test command.
It uses SECRETS_PASSWORD environment variable in GitHub for decrypting secrets.
In GitHub go to Settings->Secrets and Variables->Actions
then create a New repository secret named SECRETS_PASSWORD that holds password for decrypting secrets.
It's the same password that tools/secrets/encryption_password.txt stores.
For now golden_test package does not support CI tests by handling problems with differnt rendering depending on system. So here you have to use the same system that you use locally. If it will be a big issue you should consider using alchemist which solves that problem.
If any test fails then you can find goldens artifact on Github which stores information what exactly tests failed and what are the differences.
.github/workflows/test-and-analyze.yml workflow also checks for missing translations using tools/check-missing-translations/check-missing-translations.dart script and tools/check-missing-translations/pubspec.yaml file.
File:.github/workflows/release-firebase.yml
It uses
on:
workflow_dispatch:so we can run that manually in GitHub by following these steps: https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow
File: .github/workflows/release-stores.yml
Firstly please remember to build APK and manually upload it to Google Play Console, otherwise fastlane supply init command will throw an error.
Next install fastlane which process is described here.
Then follow instruction provided by fastlane: https://docs.fastlane.tools/getting-started/android/setup/#setting-up-supply
And then this one: https://docs.fastlane.tools/getting-started/android/release-deployment/
In short you should have:
android/fastlane/Appfilewithjson_key_fileandpackage_name;- copy
android/fastlane/Fastfileor onlyupload_to_playstore_internallane from there; - create
android/fastlane/.env.prodand add there
FLAVOR=production
ANDROID_BUNDLE_PATH=../build/app/outputs/bundle/productionRelease/app-production-release.aab
MAIN_FILE_PATH="../../lib/main_production.dart"
Now you can run command fastlane android upload_to_playstore_internal --env prod inside android directory which produces AAB file, uploads it to Google Play and creates a Draft release on Internal testing track. This command automatically fetches the newest version code from Google Console and builds the app with bumped version code.
This command is used in .github/workflows/release-stores.yml workflow that fires on every tag push. So if tag is named "1.0.4" then on Google Play Console you will see Internal Draft Release named "1.0.4".
git tag -a 1.0.4 -m "1.0.4"
git push origin 1.0.4You can also manage your store listing resources through Fastlane by providing these files:

Check out official documentation https://firebase.google.com/docs/flutter/setup
Install firebase tool.
curl -sL https://firebase.tools | bashLogin to Firebase.
firebase loginInstall the FlutterFire CLI
dart pub global activate flutterfire_cliOpen Firebase Console and create separate projects for every environment you need. e.g.
- starting-flutter-project-prod
- starting-flutter-project-stg
- starting-flutter-project-dev
Firebase's documentation strongly encourages MULTIPLE times to create separate Firebase projects for different environments e.g. production and staging. The main reason is that thanks to that you can separate resources (like database or authentication) between production and staging apps.
From my experience what I recall that really can be useful. I remember working on a project that used one Firebase project for both production and staging environments. We were using Firebase authentication on social media. When I logged in using a Google account on the staging app and then on the production app I was not able to create an account because my email was already used in the app, because both the production and staging app were using the same resources.
Then open ./tools/flutterfire_configure_with_flavors.sh file and modify projectName, iosPackageName, and androidPackageName accordingly to your needs.
Next, inside your Flutter project run
./tools/flutterfire_configure_with_flavors.sh prod
./tools/flutterfire_configure_with_flavors.sh stg
./tools/flutterfire_configure_with_flavors.sh dev When prompted, select Build configuration:
You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. βΊ
β― Build configuration
Target Then, choose the Debug-[flavor] build configuration.
It creates GoogleServive-Info.plist files for iOS,

google-services.json files for Android,

and also firebase_options_*.dart files.

Add firebase_core package
flutter pub add firebase_coreModify /lib/bootstrap.dart file to handle different flavors:
Future<void> bootstrap(
FutureOr<Widget> Function() builder, {
required FirebaseOptions firebaseOptions,
}) async {
FlutterError.onError = (details) {
log(details.exceptionAsString(), stackTrace: details.stack);
};
Bloc.observer = const AppBlocObserver();
// Add cross-flavor configuration here
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: firebaseOptions);
runApp(await builder());
}
and use new parameter inside
/lib/main_production.dart, /lib/main_staging.dart and /lib/main_development.dart files.
import 'package:starting_flutter_project/app/app.dart';
import 'package:starting_flutter_project/bootstrap.dart';
import 'package:starting_flutter_project/firebase_options_prod.dart';
void main() {
bootstrap(
() => const App(),
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
);
}At the end add new rules to .gitignore
# Firebase secrets
# These are not really crucial secrets, but always better to hide.
firebase.json
android/app/src/production/google-services.json
android/app/src/staging/google-services.json
android/app/src/development/google-services.json
ios/flavors/production/GoogleService-Info.plist
ios/flavors/staging/GoogleService-Info.plist
ios/flavors/development/GoogleService-Info.plist
ios/Runner/GoogleService-Info.plist
lib/firebase_options_prod.dart
lib/firebase_options_stg.dart
lib/firebase_options_dev.dartthe same files should be added to /tools/secrets/secret_files_list.txt file.
Add new tool in .tool-versions file
# Java version compatible with Gradle 8.3
java openjdk-17Firebase official documentation
- Open Firebase -> App Distribution and press
Get Started. - Go to
Testers and Groupsand create a group namedTesters. It's then used inrelease-firebase.yml, more accuretly insideandroid/Fastfile.
firebase_app_distribution(
app: ENV["FIREBASE_APP_ID"],
service_credentials_file: "app/firebase_service_credentials-prod.json",
android_artifact_type: "APK",
android_artifact_path: ENV["FIREBASE_SERVICE_CREDENTIALS_FILE_PATH"],
groups: "testers", # <====== HERE
)Add new tools in .tool-versions file
# For Fastlane
ruby 3.4.1Then call
mise install
ruby --versionin order to install ruby.
Then after making sure you are using the right version of ruby install fastlane
gem install fastlane -v 2.226.0 Go to android directory and run
fastlane initAdd plugin for uploading build to Firebase App Distribution
fastlane add_plugin firebase_app_distributionand install dotenv
gem install dotenvCopy android/fastlane/Fastfile. That file uses environment variables defined in the same directory in files:
env.prodenv.stgenv.dev
Example env.prod file. Remember to adjust it for every flavor.
FLAVOR=production
FIREBASE_APP_ID=fake_firebase_app_id
FIREBASE_SERVICE_CREDENTIALS_FILE_PATH=app/firebase_service_credentials-prod.json
ANDROID_ARTIFACT_PATH=../build/app/outputs/flutter-apk/app-production-release.apk
MAIN_FILE_PATH="../../lib/main_production.dart"
Instruction on how to generate SERVICE_CREDENTIALS_FILE
https://firebase.google.com/docs/app-distribution/android/distribute-fastlane#service-acc-fastlane
Now we need to handle new secrets. Add these paths to .gitgnore:
android/fastlane/.env.prod
android/fastlane/.env.stg
android/fastlane/.env.dev
android/app/firebase_service_credentials-prod.json
and remove that path **/android/**/gradle-wrapper.jar. It is needed to make gradle available on CI. Thanks to that we use exactly the same gradle on CI and locally. gradle-wrapper.jar from android/.gitignore must be deleted as well.
Add these paths to tools/secrets/secret_files_list.txt
android/fastlane/.env.prod
android/fastlane/.env.stg
android/fastlane/.env.dev
android/app/firebase_service_credentials-prod.json
and run ./tools/secrets/encrypt_secrets.sh in order to update secrets.
In android/fastlane/Fasfile file we set up versionName and versionNumber of built app. This change requires changes inside android/app/build.gradle file:
defaultConfig {
// ...
// Fetch versionCode ane versionName from properties given while calling `gradlew` e.g.
// "gradlew assembleproductionRelease -p . -PversionCode=4 -PversionName=3.0.0"
// If they do not exist then use values from `/android/local.properties` file.
versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : flutterVersionCode.toInteger()
versionName project.hasProperty('versionName') ? project.property('versionName') as String : flutterVersionName
}flutterVersionCode and flutterVersionName should be already defined, but in case here there are:
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}Copy .github/workflows/release-firebase.yml file with workflow. This file uses new tools on CI, so we need to provide also a new records inside .github/workflows/common.env:
JAVA_VERSION=17.0.2
FLUTTER_VERSION=3.24.4-stable
RUBY_VERSION=3.4.1
FASTLANE_VERSION=2.226.0
Finally we can run fastlane command and you can choose which environment should be used.
fastlane android upload_to_firebase --env prodThis project uses get_it for dependency injection.
flutter pub add firebase_crashlytics
flutter pub add get_itCopy lib/core/crashlytics_error_reporter.dart which handles error reporting in the app. It also contains testFirebaseCrashlytics function that can be called whenever you want in order to send different errors to Firebase and check if it works fine. Remember to test Android and iOS, because iOS needs special dSYM files to decode errors. This topic is covered in the next point.
Create lib/core/dependencies.dart for managing dependencies with use of get_it.
import 'package:get_it/get_it.dart';
import 'package:starting_flutter_project/core/crashlytics_error_reporter.dart';
final GetIt sl = GetIt.instance;
Future<void> setupDependencies() async {
final CrashlyticsErrorReporter crashlyticsErrorReporter = CrashlyticsErrorReporter();
await crashlyticsErrorReporter.initReporter();
sl.registerLazySingleton(() => crashlyticsErrorReporter);
}then modify lib/bootstrap.dart file to create dependencies before app run.
Future<void> bootstrap(
FutureOr<Widget> Function() builder, {
required FirebaseOptions firebaseOptions,
}) async {
Bloc.observer = const AppBlocObserver();
// Add cross-flavor configuration here
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: firebaseOptions);
await setupDependencies();
runApp(await builder());
}iOS needs special dSYM files to decode errors sent to Firebase.
We need to add Build Phase script that will send dSYM files to Firebase.

"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols" -gsp "${PROJECT_DIR}/flavors/${FLAVOR}/GoogleService-Info.plist" -p ios "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"This script uses FLAVOR user-defined setting that should be added here:

Follow official documentation and generate Upload Key - https://docs.flutter.dev/deployment/android#signing-the-app
Then create android/key.properties file and fill the required data.
storePassword=<password-from-previous-step>
keyPassword=<password-from-previous-step>
keyAlias=upload
storeFile=<keystore-file-location>and use its values inside android/app/build.gradle. Firstly extract values from android/key.properties
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}then use them here:
android {
signingConfigs {
if (System.getenv("ANDROID_KEYSTORE_PATH")) {
release {
storeFile file(System.getenv("ANDROID_KEYSTORE_PATH"))
keyAlias System.getenv("ANDROID_KEYSTORE_ALIAS")
keyPassword System.getenv("ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD")
storePassword System.getenv("ANDROID_KEYSTORE_PASSWORD")
}
} else {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
}
}Now you can create build release version of the app:
flutter build appbundle --release --flavor production -t lib/main_production.dart
flutter build apk --release --flavor production -t lib/main_production.dartThis project contains 3 flavors:
- development
- staging
- production
To run the desired flavor either use the launch configuration in VSCode/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*Starting Flutter Project works on iOS, Android, Web, and Windows.
To run all unit and widget tests use the following command:
$ flutter test --coverage --test-randomize-ordering-seed randomTo view the generated coverage report you can use lcov.
# Generate Coverage Report
$ genhtml coverage/lcov.info -o coverage/
# Open Coverage Report
$ open coverage/index.htmlThis project relies on flutter_localizations and follows the official internationalization guide for Flutter.
- To add a new localizable string, open the
app_en.arbfile 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:starting_flutter_project/l10n/l10n.dart';
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return Text(l10n.helloWorld);
}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>
...- 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
.arbfile:
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"
}
}
To use the latest translations changes, you will need to generate them:
- Generate localizations for the current project:
flutter gen-l10n --arb-dir="lib/l10n/arb"Alternatively, run flutter run and code generation will take place automatically.
- β localization
- β secrets
- β golden tests - change local path to remote after merging to master
- upload dsym files
- β releasing on Firebase
- Firebase crashylitcs
- Firebase analytics - logScreenViews
- releasing on TestFlight
- β releasing on Google Internal Test
- β flavors production, development, staging
- β workflow tests, analyzer
- β git hooks
- β linter rules
- β launch.json
- β mise configuration
- β dependabot
- β CI info about missing translations
- β caching in workflows
- β secrets fix empty line in list of secrets
- β upload app metadata to playstore https://docs.fastlane.tools/getting-started/android/release-deployment/
- β remove GoogleService-Info.plist from ios/Runner and use the one in ios/flavor/[flavor]





