Flutter Flavorizr adalah package praktis yang dirancang untuk membantu developer mengatur berbagai konfigurasi aplikasi (flavors) dalam Flutter. Dengan package ini, Kita dapat dengan mudah membuat dan mengelola beberapa varian aplikasi tanpa ribet.

Apa itu Flavors?

Flavors, atau yang sering disebut product flavors adalah cara untuk membuat versi aplikasi yang berbeda dengan beberapa konfigurasi khusus, seperti:

  • Development, staging, dan production environments
  • Ikon, warna, dan nama aplikasi yang berbeda
  • Base URL API yang berbeda
  • Fitur-fitur yang disesuaikan

Instalasi

Tambahkan flutter_flavorizr ke file pubspec.yaml proyek Flutter Anda

dev_dependencies:
  flutter_flavorizr: ^2.2.3

Atau dengan menggunakan command berikut pada terminal Anda:

flutter pub add -d flutter_flavorizr

Konfigurasi Sederhana

Buat file flavorizr.yaml dan tambahkan konfigurasi seperti contoh di bawah ini:

ide: "vscode"
flavors:
  apple:
    app:
      name: "Apple App"
      icon: "assets/icons/apple.png"
    android:
      applicationId: "com.example.flavoring.apple"  
    ios:
      bundleId: "com.example.flavoring.apple" 
    config:
  banana:
    app:
      name: "Banana App"
      icon: "assets/icons/banana.png"
    android:
      applicationId: "com.example.flavoring.banana"
    ios:
      bundleId: "com.example.flavoring.banana"
  cherry:
    app:
      name: "Cherry App"
      icon: "assets/icons/cherry.png"
    android:
      applicationId: "com.example.flavoring.cherry"
    ios:
      bundleId: "com.example.flavoring.cherry"

Penjelasan File flavorizr.yaml

File flavorizr.yaml adalah file konfigurasi untuk mengatur berbagai flavor dalam aplikasi Flutter menggunakan Flutter Flavorizr. Setiap bagian file ini memiliki fungsinya masing-masing.

1. ide

ide: "vscode"

  • Deskripsi: Menentukan editor yang digunakan untuk mengembangkan proyek.
  • Value: Diatur menjadi "vscode" untuk menunjuk bahwa proyek ini dikembangkan di Visual Studio Code.
  • Pilihan Lain: Anda juga bisa menggunakan "idea" jika menggunakan IntelliJ IDEA atau Android Studio.
  • Value ini akan memberikan configuration launch yang akan digunakan untuk menjalankan program
  • Berikut adalah contoh yang dihasilkan ketika kita mengisi value dari ide menjadi vscode:
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "apple Debug",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--flavor",
                "apple"
            ],
            "program": "lib/main_apple.dart"
        },
        {
            "name": "apple Profile",
            "request": "launch",
            "type": "dart",
            "flutterMode": "profile",
            "args": [
                "--flavor",
                "apple"
            ],
            "program": "lib/main_apple.dart"
        },
        {
            "name": "apple Release",
            "request": "launch",
            "type": "dart",
            "flutterMode": "release",
            "args": [
                "--flavor",
                "apple"
            ],
            "program": "lib/main_apple.dart"
        },
        {
            "name": "banana Debug",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--flavor",
                "banana"
            ],
            "program": "lib/main_banana.dart"
        },
        {
            "name": "banana Profile",
            "request": "launch",
            "type": "dart",
            "flutterMode": "profile",
            "args": [
                "--flavor",
                "banana"
            ],
            "program": "lib/main_banana.dart"
        },
        {
            "name": "banana Release",
            "request": "launch",
            "type": "dart",
            "flutterMode": "release",
            "args": [
                "--flavor",
                "banana"
            ],
            "program": "lib/main_banana.dart"
        },
        {
            "name": "cherry Debug",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--flavor",
                "cherry"
            ],
            "program": "lib/main_cherry.dart"
        },
        {
            "name": "cherry Profile",
            "request": "launch",
            "type": "dart",
            "flutterMode": "profile",
            "args": [
                "--flavor",
                "cherry"
            ],
            "program": "lib/main_cherry.dart"
        },
        {
            "name": "cherry Release",
            "request": "launch",
            "type": "dart",
            "flutterMode": "release",
            "args": [
                "--flavor",
                "cherry"
            ],
            "program": "lib/main_cherry.dart"
        }
    ]
}

2. flavors

flavors:
  apple:
    ...
  banana:
    ...
  cherry:
    ...

  • Deskripsi: Bagian ini mendefinisikan berbagai flavor dalam aplikasi Anda (seperti apple, banana, dan cherry).
  • Struktur: Setiap flavor memiliki konfigurasi spesifik untuk:
  • Nama aplikasi
  • Ikon aplikasi
  • applicationId (Android) atau bundleId (iOS)

a. Flavor apple

apple:
  app:
    name: "Apple App"
    icon: "assets/icons/apple.png"
  android:
    applicationId: "com.example.flavoring.apple"
  ios:
    bundleId: "com.example.flavoring.apple"

  • **name**: Nama aplikasi untuk flavor apple, yaitu "Apple App".
  • **icon**: Jalur ke file ikon untuk aplikasi ini (assets/icons/apple.png).
  • **android.applicationId**: ID aplikasi unik untuk platform Android (com.example.flavoring.apple).
  • **ios.bundleId**: ID aplikasi unik untuk platform iOS (com.example.flavoring.apple).

Note: Untuk bagian icon, jangan lupa untuk menambahkan pada folder assets/icons/ dan mendeklarasikannya pada file pubspec.yaml.

Menjalankan Flavorizr

Jalankan command berikut untuk menghasilkan konfigurasi flavor:

flutter pub run flutter_flavorizr

Perintah ini akan membuat struktur file dan folder untuk setiap flavor yang sudah didefinisikan dalam file flavorizr.yaml.

Akan ter- generate beberapa file seperti di bawah ini

Beberapa file native juga akan ter- generate dan berubah secara otomatis

a. Android

b. iOS

Konfigurasi Single main.dart

Untuk menjadikan hanya menjadi satu file main.dart kita perlu melakukan beberapa langkah berikut:

1. Modifikasi file flavors.dart

Tambahkan code untuk mendapatkan Flavor dari namanya

enum Flavor {
  apple,
  banana,
  cherry,
}

Flavor getFlavor(String name) { switch (name) { case 'apple': return Flavor.apple; case 'banana': return Flavor.banana; case 'cherry': return Flavor.cherry; default: return Flavor.apple; } }

Kemudian, tambahkan method constructor yang digunakan untuk mendapatkan flavor yang sedang aktif menggunakan library dari flutter services.

import 'package:flutter/services.dart' show appFlavor;

class F { static Flavor selectedFlavor = Flavor.apple; static String get name => selectedFlavor.name; F() { String? flavor = appFlavor; if (flavor != null) { selectedFlavor = getFlavor(flavor); } } static String get title { switch (selectedFlavor) { case Flavor.apple: return 'Apple App'; case Flavor.banana: return 'Banana App'; case Flavor.cherry: return 'Cherry App'; default: return 'title'; } } }

Sehingga file flavors.dart berubah menjadi seperti di bawah ini.

import 'package:flutter/services.dart' show appFlavor;

enum Flavor { apple, banana, cherry, }Flavor getFlavor(String name) { switch (name) { case 'apple': return Flavor.apple; case 'banana': return Flavor.banana; case 'cherry': return Flavor.cherry; default: return Flavor.apple; } }class F { static Flavor selectedFlavor = Flavor.apple; static String get name => selectedFlavor.name; F() { String? flavor = appFlavor; if (flavor != null) { selectedFlavor = getFlavor(flavor); } } static String get title { switch (selectedFlavor) { case Flavor.apple: return 'Apple App'; case Flavor.banana: return 'Banana App'; case Flavor.cherry: return 'Cherry App'; default: return 'title'; } } }

2. Hapus main file yang tidak diperlukan

Setelah selesai memodifikasi file flavors.dart, langkah selanjutnya adalah menghapus file main_apple.dart, main_banana.dart, main_cherry.dart. Sehingga hanya akan ada satu file main.dart seperti gambar di bawah.

3. Modifikasi file main.dart

Kemudian, modifikasi file main.dart menjadi seperti di bawah ini

import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'app.dart';
import 'flavors.dart';

FutureOr<void> main() async {
  runZonedGuarded(
    () async {
      F();
      runApp(const App());
    },
    (error, stackTrace) {
      log(
        'runZonedGuarded: Caught error in my root zone.',
        error: error,
        stackTrace: stackTrace,
      );
    },
  );
}

Bagian yang penting dan perlu diperhatikan adalah pada pemanggilan dari method constructor dari class F untuk menjalankan method yang sudah kita buat sebelumnya.

4. Modifikasi file launch.json

Kemudian, langkah selanjutnya adalah mengkonfigurasi file `.vscode/launch.json` yang telah ter- generate sebelumnya. Ubah seluruh config yang menunjukkan ke main file yang telah kita hapus sebelumnya sehingga menjadi seperti di bawah ini.

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "apple Debug",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--flavor",
                "apple"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "apple Profile",
            "request": "launch",
            "type": "dart",
            "flutterMode": "profile",
            "args": [
                "--flavor",
                "apple"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "apple Release",
            "request": "launch",
            "type": "dart",
            "flutterMode": "release",
            "args": [
                "--flavor",
                "apple"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "banana Debug",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--flavor",
                "banana"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "banana Profile",
            "request": "launch",
            "type": "dart",
            "flutterMode": "profile",
            "args": [
                "--flavor",
                "banana"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "banana Release",
            "request": "launch",
            "type": "dart",
            "flutterMode": "release",
            "args": [
                "--flavor",
                "banana"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "cherry Debug",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--flavor",
                "cherry"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "cherry Profile",
            "request": "launch",
            "type": "dart",
            "flutterMode": "profile",
            "args": [
                "--flavor",
                "cherry"
            ],
            "program": "lib/main.dart"
        },
        {
            "name": "cherry Release",
            "request": "launch",
            "type": "dart",
            "flutterMode": "release",
            "args": [
                "--flavor",
                "cherry"
            ],
            "program": "lib/main.dart"
        }
    ]
}

5. Modifikasi konfigurasi untuk iOS

Kemudian, ubah beberapa konfigurasi untuk iOS yang masih mengarah ke main file sebelumnya. Untuk cara mudahnya, kita tinggal menggunakan fitur search dari vscode.

Ubah keseluruhan, mulai dari main_apple, main_banana, dan main_cherry menjadi main.dart. Sehingga akan menjadi seperti contoh di bawah.

#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

FLUTTER_TARGET=lib/main.dart
ASSET_PREFIX=apple
BUNDLE_NAME=Apple App
BUNDLE_DISPLAY_NAME=Apple App

Flavor sudah siap dijalankan.

Menjalankan Aplikasi berdasarkan Flavor.

File .vscode/launch.json sebelumnya akan memberikan kita konfigurasi yang akan digunakan untuk menjalankan aplikasi sesuai dengan command yang tertera pada file. Jika kita mengarah pada section Run and Debug pada VS Code. Kita akan mendapati sebuah konfigurasi seperti berikut:

Jika kita ingin menjalankan flavor cherry, maka kita tinggal memilih konfigurasi cherry, baik itu Debug, Profile, maupun Release version.

Berikut adalah hasil-hasil dari semua flavor yang telah dijalankan.

Menambahkan Base Url Sesuai dengan Flavor

Untuk menambahkan base url berdasarkan flavor, kita perlu mengubah file flavors.dart

static String get baseUrl {
    switch (selectedFlavor) {
      case Flavor.apple:
        return 'https://apple.com';
      case Flavor.banana:
        return 'https://banana.com';
      case Flavor.cherry:
        return 'https://cherry.com';
      default:
        return 'https://default.com';
    }
  }

Code di atas digunakan untuk menentukan base url sesuai dengan flavor yang sedang digunakan. Sehingga file flavors.dart akan menjadi seperti di bawah ini:

import 'package:flutter/services.dart' show appFlavor;

enum Flavor {
  apple,
  banana,
  cherry,
}
Flavor getFlavor(String name) {
  switch (name) {
    case 'apple':
      return Flavor.apple;
    case 'banana':
      return Flavor.banana;
    case 'cherry':
      return Flavor.cherry;
    default:
      return Flavor.apple;
  }
}
class F {
  static Flavor selectedFlavor = Flavor.apple;
  static String get name => selectedFlavor.name;
  F() {
    String? flavor = appFlavor;
    if (flavor != null) {
      selectedFlavor = getFlavor(flavor);
    }
  }
  static String get title {
    switch (selectedFlavor) {
      case Flavor.apple:
        return 'Apple App';
      case Flavor.banana:
        return 'Banana App';
      case Flavor.cherry:
        return 'Cherry App';
      default:
        return 'title';
    }
  }
  static String get baseUrl {
    switch (selectedFlavor) {
      case Flavor.apple:
        return 'https://apple.com';
      case Flavor.banana:
        return 'https://banana.com';
      case Flavor.cherry:
        return 'https://cherry.com';
      default:
        return 'https://default.com';
    }
  }
}

Untuk mengetahui hasilnya, maka akan kita ubah file my_home_page.dart menjadi seperti ini

import 'package:flutter/material.dart';
import '../flavors.dart';

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(F.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Hello ${F.title}',
            ),
            Text(
              'Base Url ${F.baseUrl}',
            ),
          ],
        ),
      ),
    );
  }
}

Jalankan, sehingga akan tampil seperti ini

Example Source Code:

https://github.com/AkhmadRamadani/learn-flutter-flavor