Flutter Heyteacher Meta

A Flutter meta project implementing utilities and best practices for Flutter package and app project avoiding Copy & Paste pattern.

  • environment setup instructions for app and package projects and Firebase setup app projects
  • git utilities for manage versions and releases
  • release app in Google Play and Firebase App Distribution
  • integration test and Firebase Test Lab utilities
  • backup and restore Firestore utilities
  • localization setup instructions
  • documentation utilities
  • Launcher Icon, Splash, dart builder, ffmpeg, webcrypto utilities

Table of Contents

Installing

  • setup environment following instruction Environment Setup

  • install flutter_heyteacher_meta as dev package into your app or package project:

    flutter pub add dev:flutter_heyteacher_meta
    

    or add to your pubspec.yaml

    dev_dependencies:
      flutter_heyteacher_meta: 
    
  • follow instuctions in Configure FastLane in order to configure a app or package project.

  • test all works fine, running fl will show command avaiable

Credits

  • yaml_edit: A library for YAML manipulation while preserving comments

  • very_good_analysis: This package provides lint rules for Dart and Flutter which are used at Very Good Ventures

  • auto-changelog: Command line tool for generating a changelog from git tags and commit history

  • vscode-markdownlint: Markdown/CommonMark linting and style checking for Visual Studio Code

  • vscode-markdown: All you need for Markdown (keyboard shortcuts, table of contents, auto preview and more).

Requirements

  • a shell terminal on Linux, MacOS or WSL on Windows

  • git 1.7.2 or later

  • a GitHub account

  • gh 2.46.0 or later (GitHub CLI)

  • other software and utilities as described in Environment Setup

Environment Setup

Instructions for setup environment installing all software needed to develop a Flutter project.

Flutter

install flutter manually following instructions docs.flutter.dev/install/quick#install

  • setup your ~/.bashrc with this env variables

    # flutter
    export PATH=/usr/local/flutter/bin:$PATH
    
    # dart 
    export PATH="$HOME/.pub-cache/bin":$PATH
    
    # flutter_heyteacher_meta scripts
    latest_meta_version=`ls $HOME/.pub-cache/hosted/pub.dev | grep flutter_heyteacher_meta |  tail -n 1`
    project_meta_root="$HOME/.pub-cache/hosted/pub.dev/$latest_meta_version"
    export PATH="$project_meta_root/scripts":$PATH
    

Visual Studio Code

install Visual Studio Code 1.77 or later with the Flutter extension for VS Code

You can configure you vscode to execute the command-line utility version in order to automatically update build version every run/debug execution of your code:

  • install flutter_heyteacher_meta package as dev dependency ad described in Installing

  • create/modify .vscode/tasks.json in the root of your project

    {
          "version": "2.0.0",
          "tasks": [
                  {
                          "type": "dart",
                          "command": "dart",
                          "args": [
                                  "run",
                                  "flutter_heyteacher_meta:version",
                                  "build"
                          ],
                          "group": "build",
                          "problemMatcher": [],
                          "label": "dart: run flutter_heyteacher_meta:version build",
                          "detail": "increment version build number",
                          "presentation": {
                                  "close": true,
                                  "echo": false,
                                  "reveal": "silent",
                                  "focus": false,
                                  "panel": "shared",
                                  "showReuseMessage": false,
                                  "clear": false
                          }
                  }
          ]
    }
    
  • add preLaunchTask in your launch configurations '.vscode/launch.json'

    ...
        
      "preLaunchTask": "dart: run flutter_heyteacher_meta:version build"
    

Android Studio

  • install Android Studio

  • setup your ~/.bashrc with this env variables and alias

    #android studio
    export ANDROID_HOME="$HOME/Android/Sdk/"
    export PATH="${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/"
    export PATH=/usr/local/android-studio/jbr/bin/:$PATH
    

auto-changelog

auto-changelog is an utility which generate CHANGELOG.md automatically based on commits, pull requests and tags.

  • install npm as described in nodeJs

  • install auto-changelog

    npm install -g auto-changelog
    
  • test the command

    auto-changelog
    

FastLane

  • install rbenv, ruby and bundler

    brew install rbenv
    rbenv init
    rbenv install -l
    rbenv install 3.4.3
    rbenv local 3.4.3
    gem install bundler
    
  • create Genfile in project root containing:

    source "https://rubygems.org"
    gem "fastlane"
    
  • install fastlane via bundle

    bundle update
    
  • setup your ~/.bash_aliases with this fl alias

    # alias for fastlane inside flutter projects
    alias fl='fl.sh'
    

    fl is an alias of fl.sh command.

#!/bin/bash
#
# Run FastLane Lanes.
#
# Executed without paramenter show lanes available and documentation 
if [[ -z ${@} ]] 
then
    # show lanes avalilable and documentation
    bundle exec fastlane lanes
else
    # run lane 
    bundle exec fastlane $@
fi  

the execution fl in root project directory without paramenter show all lanes configured and how to use them.

nodeJs

  • install nodeJs

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
    nvm install 22
    

Firebase CLI

  • install firebase-tools

    npm install -g firebase-tools
    

Create a flutter project

  • flutter app project:

    flutter create <app project name>
    
  • flutter package projects:

    flutter create -t package <app project name>
    

Configure FastLane

After setup the environment run from root project directory and create the project:

  • for flutter packages:

    configure_flutter_package.sh
    
  • for flutter app

    configure_flutter_app.sh
    

    This scripts create a skeleton of fastlane/metadata mandatory for release application in Google Play. So, before release the application you need to:

    • edit fastlane/metadata/android/en-US/title.txt

    • edit fastlane/metadata/android/en-US/short_description.txt

    • edit fastlane/metadata/android/en-US/full_description.txt

    • edit fastlane/metadata/android/en-US/video.txt

    • edit fastlane/metadata/android/en-US/changelogs/default.txt

    • edit fastlane/metadata/android/en-US/images/icon.png

    • edit fastlane/metadata/android/en-US/images/featureGraphic.png

    • add screenshots to fastlane/metadata/android/en-US/images/phoneScreenshots

    For utilize Firebase Firestore backup and restore utilities, edit ./fastlane/Fastfile uncommenting and setup these environment variable:

    • google_storage_backup_bucket the Google Storage backup bucket name

    For utilize Firebase App Distribution, edit ./fastlane/Fastfile uncommenting and setup these environment variable:

    • firebase_app_distribution_service_credentials_file the Firebase App Distribution service credentials file name

Fastlane lines for app and package projects

Common Fastlane lines are provided to app and package projects. In details:

  • generate dart documentation
  • checkout and release on github
  • run unit test

doc

fl doc

Generate the dart documentation and run webserver on http://localhost:8080

docweb

fl docweb

Runs local webserver on http://localhost:8080 with dart documentation

test

fl test

Run unit tests of the project.

checkout

fl checkout

Checkout the latest remote branch already created remotely (i.e. in github project ).

Runs git fetch and git checkout to the latest branch fetched.

release

fl release semver:major|minor|patch [suffix:<nmenonic_tag_suffix>] [merge:true|false] [github:false|true]

Release to main branch after you commit and push your changes into your branch.

  • semver: increments the version into pubspec.yaml following Semantic Versioning, for example:
    • major: move version from 1.0.0 to 2.0.0
    • minor: move version from 1.0.0 to 1.1.0
    • patch: move version from 1.0.0 to 1.0.1
  • suffix: (optional) add a mnemonic suffix to git tag greated
  • merge: (optional, default true) make the marge to main branch
  • github: (optional, default false) create the github release and update the CHANGELOG.md

These command make several tasks:

  • increments the version into pubspec.yaml
  • create a github release and update CHANGELOG.md (if github param is `true)
  • create a pull request and merge changes into main branch
  • checkout the main branch
  • delete the branch merged (if merge param is `true)
  • create a git tag named {package-name}-{version}

github_release

fl github_release

Create a github release and update CHANGELOG.md

bump

fl bump

Commits pubspec.lock and pubspec.yaml without generate a new version and without create a new release.

Creates a github release and update CHANGELOG.md

Fastlane lines for app projects

Specific Fastlane lines are provided for app projects. In details:

  • build AAB
  • run integration test locally or on Firebase Test Lab
  • backup and restore Firestore dababase
  • release application in Google Play and Firebase App Distribution
  • build and release web application in Firebase Hosting

appbundle

fl appbundle

Clean and build the application.

When completed, the AAB is generate into build/app/outputs/bundle/release/app-release.aab.

integration_test

fl integration_test

Run integration tests of app project.

testlab

fl testlab

Run integration test of app project in Firebase Test Lab

firestore_backup

fl firestore_backup [snapshot:YYYY-MM-DDTHH:mm:ss.00Z] [database:<database>]

Backup Firestore dababase. if snapshot is set, create backup at time specified. If database isn't set use (default). For details Firestore backup, restore and Point-in-time recovery.

firestore_restore

fl firestore_restore backup:<YYYY-MM-DDTHH:mm:ss_mi> [database:<database>]

Restore Firestore dababase to specified backup. If database isn't set use (default). For details Firestore backup, restore and Point-in-time recovery.

firestore_remove_backup

fl firestore_remove_backup backup:<YYYY-MM-DDTHH:mm:ss_mi>

Remove Firestore dababase backup specified. For details Firestore backup, restore and Point-in-time recovery.

app_distribution

fl app_distribution

Build the release and publish app in Google Play via Firebase App Distribution

playstore

fl playstore track:production|beta|alpha|internal [upload_only:true|false]

Upload app in Google Play via supply on track. if upload_only is true upload app without build. (default false)

playstore_promote

fl playstore_promote from_track:beta|alpha|internal to_track:production|beta|alpha|internal

Promote a release in Google Play via supply from from_track to to_track.

buildweb

fl buildweb [version:profile|debug]

Build web and run local webserver on http://localhost:8080

deployweb

fl deployweb [release_type:release|profile|debug]

Deploy web in Firabase Hosting with release type release_type (default release)

git utilities

checkout and release commands with git hooks for git conventional commit and avoid commit on main branch helps you to work properly with versions, git branches, git tags and github releases.

bump command commit pubspec.lock and pubspec.yaml after a bump version on dependencies without create a new version and without create new release.

git conventional commit

The commit message should follow che conventional commit specification:

<type>[optional scope]: <description>

where <type> MUST be one of:

  • build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
  • chore: (updating grunt tasks etc; no production code change)
  • ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
  • docs: Documentation only changes
  • feat: A new feature
  • fix: A bug fix
  • perf: A code change that improves performance
  • refactor: A code change that neither fixes a bug nor adds a feature
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  • test: Adding missing tests or correcting existing tests

avoid commit on main branch

main branch is the default branch and user cannot be commit directly on it, but only merge are allowed.

If you try to commit on main this message is show

You can't commit directly to main branch

example: release a patch

  • if you create an github issue and a branch on github

    fl checkout
    

    otherwise create a branch locally and push it to remote

    git branch hotfix
    git push -u origin hotfix
    
  • make changes to your code, commit and push changes to branch

    git add .
    git commit -m "fix: fix bug ..."
    git push
    
  • release the patch merging chenges to main branch and create a github release

    fl release version:patch github:true
    

Firebase setup for app flutter project

  • login in firebase and install flutterfire

    firebase login
    dart pub global activate flutterfire_cli
    flutterfire configure
    
  • add firebase core and other packages

    flutter pub add firebase_core
    flutter pub add firebase_auth
    flutter pub add cloud_firestore
    flutterfire configure
    
  • initialize firestore, and deploy rules and indexes

    firebase init firestore
    firebase deploy --only firestore
    
  • build and run application created

    flutter run
    

Release app

Sign app

  • generate upload keystore

    mkdir -p ~/.flutter
    keytool -genkey -v -keystore ~/.flutter/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
    
  • create android/key.properties containing:

    storePassword=<password-from-previous-step>
    keyPassword=<password-from-previous-step>
    keyAlias=upload
    storeFile=<keystore-file-location>
    
  • configure Gradle following instruction docs.flutter.dev/deployment/android#configure-signing-in-gradle

  • create the ABB file running flutter build appbundle. The file created is located here:

    build/app/outputs/bundle/release/app-release.aab
    
  • extract SHA-1 SHA-256 from keystore

    keytool -list -v -alias upload -keystore ~/.flutter/upload-keystore.jks
    

    or directly from app-release.aab

    keytool -printcert -jarfile build/app/outputs/bundle/release/app-release.aab 
    
  • load SHA256 in firebase AppCheck section in (appcheck/apps) (this istruction doesn't work for firebase.google.com/docs/app-check/android/play-integrity-provider Currently, the built-in Play Integrity provider only supports Android apps distributed by Google Play and stackoverflow.com/a/78698583/1123065)

  • link Google Cloud project to Google Play console follow istructions developer.android.com/google/play/integrity/setup

Firebase App Distribution

  • follow istructions firebase.google.com/docs/app-distribution/android/distribute-fastlane?apptype=aab

  • Copy the AppDistribution JSON keys created in google cloud IAM in:

    ~/.flutter/<YOUR_PROJECT>-app_distribution.json
    
  • setup your ~/.bashrc

    # setup for `Firebase App Distribution` in `~/.flutter/` 
    export GOOGLE_APPLICATION_CREDENTIALS=<path_of_app_distribution_json>
    
  • run firebase login:ci and copy the token generated

  • create the file ~/.flutter/firebase_cli_token.json and paste the token generate in this json:

    {
      "firebase_cli_token": "<paste here token generated by 'firebase login:ci'>"
    }
    
  • run fastlane

    fl.sh distribute
    

Google Play

Integration Test

Follow the instruction github.com/flutter/flutter/tree/main/packages/integration_test

with this changes:

  • android/gradle/wrapper/gradle-wrapper.properties

    - distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
    + distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
    
  • android/settings.gradle

    - id "com.android.application" version "8.1.0" apply false
    + id "com.android.application" version "8.7.0" apply false
    

Run locally on device connected:

flutter test integration_test/main.dart

or

flutter driver --driver=test_driver/main_test.dart --target=integration_test/main.dart 

or with gradlew

./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../integration_test/main.dart

Firebase Test Lab

  • buid integration test artifacts using scripts android/firebaseTestLabBuild.sh, shortcut of:

    ./gradlew app:assembleAndroidTest
    ./gradlew app:assembleDebug -Ptarget=`pwd`/../integration_test/main.dart
    
  • select device from supported devices:

    gcloud firebase test android models list 
    
  • you can filter only virtual devices (hight availability, lower cost):

    gcloud firebase test android models list --filter=virtual 
    
  • start test in Firebase Test Lab on the selected device launching gcloud command. For example, run test in Medium Phone virtual with android 13 (version 33):

    gcloud firebase test android run --type instrumentation --app=../build/app/outputs/apk/debug/app-debug.apk --test=../build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --device model=MediumPhone.arm,version=33,locale=en,orientation=portrait
    

The lane fl testlab run commands above

Firestore backup, restore and Point-in-time recovery

install gcloud

With Google Cloud CLI you can manage command line Gogle Cloud services.

sudo apt-get install apt-transport-https ca-certificates gnupg curl
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt-get update && sudo apt-get install google-cloud-cli

inizialize configuring your project

gcloud init

create the bucket <PROJECT_NAME>-backups which hosts backups here: console.cloud.google.com/storage/browser

Restore a Point-in-time Recovery (PITR)

You can restore the database snapshot since last 15 days specifying snapshot-time in ISO 8601 format.

fl firestore_backup snapshot:<YYYY-MM-DDTHH:mm:ss.00Z>
# restore the backup <YYYY-MM-DDTHH:mm:ss_mi> already created
fl firestore_restore <YYYY-MM-DDTHH:mm:ss_mi>

Backup and Restore database

  • create a backup of current firestore database

    fl firestore_backup
    
  • restore a firestore backup

    # list all backups
    fl firestore_restore 
    # restore a backup
    fl firestore_restore <YYYY-MM-DDTHH:mm:ss_mi>
    
  • remove a firestore backup

    # list all backups
    fl firestore_restore 
    # remove backup
    fl firestore_remove_backup <YYYY-MM-DDTHH:mm:ss_mi>
    

Firebase Hosting

Deploy and publish a flutter web app into Firebase Hosting.

Default site:

  • Site Id: <Firebase Project Id>
  • Default URL: <Firebase Project Id>.web.app and <Firebase Project Id>.web.firebaseapp.com/

Deploy default site

  • configure firebase.json adding hosting configuration

      "hosting": {
        "public": "build/web",
        "frameworksBackend": {
          "region": "<Firebase Region>"
        }
      }
    
  • build web and test locally

    fl buildweb [--debug:true]
    
  • deploy to default site

    firebase deploy --only hosting
    

Default site cannot be deleted, you can disable entire hosting:

firebase hosting:disable

Alternative sites

  • define the alternative site

    firebase target:apply hosting <Alternative Site Id> <Alternative Site Id>
    
  • Create the alternative site

    firebase hosting:sites:create <Alternative Site Id>
    
  • configure firebase.json adding target

      "hosting": [
        {
          "target": "<Alternative Site Id>",
          "public": "build/web",
          "frameworksBackend": {
            "region": "<Firebase Region>"
          }
        }
      ]
    
  • deploy to the alternative site

    firebase deploy --only hosting
    

To list all sites created:

firebase hosting:sites:list

To delete an alternative site:

firebase hosting:sites:delete <Alternative Site Id>

localization setup

  • install packages

    flutter pub add flutter_localizations --sdk=flutter
    flutter pub add intl:any
    
  • modify pubspec.yaml setting flutter artifact generation

    flutter:
      generate: true
    
  • in root project creat l10n.yaml

    arb-dir: lib/src/l10n
    template-arb-file: flutter_heyteacher_locale_en.arb
    output-localization-file: flutter_heyteacher_locale.dart
    output-class: FlutterHeyteacherLocaleLocalizations
    output-dir: lib/src/l10n
    untranslated-messages-file: untranslated-messages.txt
    
  • create the arb files of your supported languages

    mkdir lib/l10n
    touch lib/l10n/flutter_heyteacher_locale_en.arb
    touch lib/l10n/flutter_heyteacher_locale_it.arb
    
  • insert in flutter_heyteacher_locale_en.arb the translation

  • commit untranslated-messages.txt the file containing localized strings to be translated, this file should be always empty

    git add untranslated-messages.txt
    git commit -m "chore: localized strings to be translated, this file should be always empty"
    
  • insert localized string into flutter_heyteacher_locale_en.arb

    {
      "@@locale": "en",
      "userNotAutenticated": "User not autenticated",
      "@userNotAutenticated": {},
      "notAuthenticated": "Not Authenticated",
      "@notAuthenticated": {},
      "errorOnRetrieveData": "Error on retrieve Data",
      "@errorOnRetrieveData": {},
      "timeoutOnRetrieveData": "Timeout on retieve data",
      "@timeoutOnRetrieveData": {}
    }
    
  • regenerate the artifacts

    flutter pub get
    
  • create a file lib/localizations.dart containing the export

    export 'package:flutter_heyteacher_locale/src/l10n/flutter_heyteacher_locale.dart' show FlutterHeyteacherLocaleLocalizations;
    
  • add delegate to your app

    MaterialApp.router(
      localizationsDelegates: [
        .
        .
        .
        FlutterHeyteacherLocaleLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
    
    )
    
  • import and use in your code

    import 'package:flutter_heyteacher_locale/localizations.dart';
    .
    .
    .
    FlutterHeyteacherLocaleLocalizations.of(context)!.userNotAutenticated
    

documentation utilities

fl doc

Generates dart documentation, run a local web server on http://localhost:8080 and open a browser on it.

fl docweb

Run a local web server on http://localhost:8080 and open a browser on project documentation already generatd

Launcher Icon

  • install flutter_launcher_icons

    flutter pub add flutter_launcher_icons
    
  • create or modify 'assets/icon/icon.png' and 'assets/icon/background.png'

  • add configuration to pubspec.yaml

flutter_launcher_icons:
  android: "launcher_icon"
  ios: false
  remove_alpha_ios: true
  image_path: "assets/icon/icon.png"
  min_sdk_android: 21 # android min sdk min:16, default 21
  adaptive_icon_background: "assets/icon/background.png"
  adaptive_icon_foreground: "assets/icon/icon.png"
  web:
    generate: true
    image_path: "assets/icon/icon.png"
    background_color: "#000000"
    theme_color: "#000000"
  windows:
    generate: true
    image_path: "assets/icon/icon.png"
    icon_size: 48 # min:48, max:256, default: 48
  macos:
    generate: true 
    image_path: "assets/icon/icon.png"
  • run

    dart run flutter_launcher_icons
    

Splash

add configuration to pubspec.yaml

flutter_native_splash:
color: "#000000"
image: assets/splash.png
android_12:
  image: assets/splash.png
  color: "#000000"  
  • run

    flutter_splash.sh
    

    an alias of:

    dart run flutter_native_splash:create
    

Dart Builders

The builders like:

can be gererated using script:

dart_builders.sh

ffmpeg utilities

ffmpeg_cmd is a bash script with utilites for crop, cut, estract and concat.

ffmpeg_cmd.sh

Usage:

  • crop

    ffmpeg_cmd.sh crop <input_video> <output_video> <width_in_px> <height_in_px> <x_in_px> <y_in_px>

  • cut

    ffmpeg_cmd.sh cut <input_video> <output_video> <start_in_sec> <end_in_sec>

  • extract

    ffmpeg_cmd.sh extract <input_video> <output_video> <start_in_sec> <end_in_sec>

  • concat

    ffmpeg_cmd.sh concat <input_video_1> <input_video_2> [input_video_3 ...] <output_video> <fade_duration_in_sec>

command-line utility version

From the root of your project, run:

dart run flutter_heyteacher_meta:version major|minor|patch|build|show|show-build [--dry-run]
  • major,minor, patch increment the version in your pubsec.yaml. --dry-run show how the version will be changed without modify pubsec.yaml

  • build set the build version in your pubsec.yaml to YYMMddHHm based on the current time.

  • dry-run show how the version will be changed without modify pubsec.yaml

  • show print the version in pubsec.yaml

  • show-build print only the build version from pubsec.yaml

webcrypto setup for tests

Flutter tests which use webcrypto need to be compiled locally running this command:

flutter_webcrypto_setup

an alias of:

dart run webcrypto:setup

Libraries

fake_pubspec_version
main
pubspec_version
A command-line utility to manage the version string in pubspec.yaml.