Mobilutvikling med Flutter · leksjon

Build-varianter og miljøkonfigurasjon

Definer dev-, staging- og prod-varianter med miljøspesifikke ressurser og Dart-define-konfigurasjoner.

Leksjon 1 av 413 trinn

Build-varianter og miljøkonfigurasjon er en gratis leksjon i Mobilutvikling med Flutter på CoddyKit. Dette er leksjon 1 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Mobilutvikling med Flutter, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Mobilutvikling med Flutter inneholder totalt 4 leksjoner.

Hvorfor byggingsvarianter er viktige

En Flutter-app i produksjon kommuniserer sjelden med bare én backend. De trenger et dev-bygg som peker på et lokalt API eller et staging-API, et staging-bygg for QA og et herdet prod-bygg for appbutikken.

  • Flavors er navngitte byggingsvarianter som kan ha ulik app-ID, ikon, navn og signering.
  • Miljøkonfigurasjon er dataene hver flavor injiserer: basis-URL-er, funksjonsflagg og API-nøkler.

Målet er å installere dev, staging og prod side om side på én enhet, der hver variant er fullstendig isolert og umulig å forveksle.

De to lagene: native flavors og Dart-define

Et ryddig oppsett skiller mellom to ansvarsområder:

  • Native flavors (Android productFlavors, iOS schemes/xcconfig) styrer appens identitet: bundle-ID, visningsnavn og ikon.
  • Dart-define-verdier styrer kjøringskonfigurasjonen som Dart-koden leser: API-URL, miljønavn og loggnivå.

Native flavors gjør at dev, staging og prod kan eksistere samtidig på enheten med ulike ID-er, som com.acme.app.dev. Dart-define holder hemmeligheter og URL-er ute av kildekontrollen og låser dem til kompileringstidspunktet.

Definere en typesikker miljøenum

Start i Dart med én autoritativ kilde for hvilke miljøer som finnes. En enum forhindrer skrivefeil og muliggjør uttømmende håndtering med switch.

Dette kodeeksempelet er ren Dart uten Flutter-avhengigheter, så det kan kjøres hvor som helst.

enum Environment { dev, staging, prod }

String labelFor(Environment env) {
  switch (env) {
    case Environment.dev:
      return 'Development';
    case Environment.staging:
      return 'Staging';
    case Environment.prod:
      return 'Production';
  }
}

void main() {
  for (final env in Environment.values) {
    print('${env.name} -> ${labelFor(env)}');
  }
}

Lese Dart-define ved kompileringstidspunktet

Flutter tilbyr konstanter som bestemmes ved kompilering, gjennom String.fromEnvironment, bool.fromEnvironment og int.fromEnvironment. De sender dem inn med --dart-define ved bygging.

  • Verdiene bygges inn i binærfilen; de leses IKKE fra enheten under kjøring.
  • Oppgi alltid en defaultValue, slik at en manglende definisjon feiler på en forutsigbar måte.

Fordi disse er const, kan de evalueres også i const-kontekster.

const String apiUrl = String.fromEnvironment(
  'API_URL',
  defaultValue: 'http://localhost:8080',
);

const String envName = String.fromEnvironment(
  'ENV',
  defaultValue: 'dev',
);

const bool analyticsEnabled = bool.fromEnvironment(
  'ANALYTICS',
  defaultValue: false,
);

void main() {
  print('env=$envName url=$apiUrl analytics=$analyticsEnabled');
}

Bygge et AppConfig-objekt

Hvis De sprer kall til String.fromEnvironment rundt i kodebasen, mister De kontrollen. Samle dem i ett uforanderlig AppConfig som De løser én gang og sender videre.

Da holdes alle forskjeller mellom flavorene på ett sted som er enkelt å revidere, og testing blir trivielt: De oppretter ganske enkelt et AppConfig med verdiene De ønsker.

class AppConfig {
  final String envName;
  final String apiUrl;
  final bool analyticsEnabled;

  const AppConfig({
    required this.envName,
    required this.apiUrl,
    required this.analyticsEnabled,
  });

  factory AppConfig.fromEnvironment() {
    return const AppConfig(
      envName: String.fromEnvironment('ENV', defaultValue: 'dev'),
      apiUrl: String.fromEnvironment('API_URL',
          defaultValue: 'http://localhost:8080'),
      analyticsEnabled:
          bool.fromEnvironment('ANALYTICS', defaultValue: false),
    );
  }

  bool get isProd => envName == 'prod';
}

void main() {
  final config = AppConfig.fromEnvironment();
  print('Running in ${config.envName} -> ${config.apiUrl}');
}

Sende Dart-define på kommandolinjen

De injiserer konfigurasjon når De kjører eller bygger. Hver --dart-define angir én nøkkel. Koble den sammen med den native flavoren via --flavor.

  • --flavor staging velger den native varianten (ID, ikon og navn).
  • --dart-define forsyner AppConfig med verdier.

Det er feilutsatt å skrive dette hver gang, så team lagrer det i skript eller filer (neste scene).

flutter run \
  --flavor staging \
  --target lib/main.dart \
  --dart-define=ENV=staging \
  --dart-define=API_URL=https://staging.api.acme.com \
  --dart-define=ANALYTICS=true

flutter build apk \
  --release \
  --flavor prod \
  --dart-define=ENV=prod \
  --dart-define=API_URL=https://api.acme.com \
  --dart-define=ANALYTICS=true

Dart-define-from-file for ryddigere CI

Lange kjeder med --dart-define er sårbare. Flutter støtter --dart-define-from-file, som leser en JSON-fil (eller en fil i .env-format) med nøkkel/verdi-par.

  • Ha én fil per miljø: config/dev.json, config/staging.json, config/prod.json.
  • Legg inn filer uten hemmeligheter i versjonskontroll, og injiser filer med hemmeligheter i CI fra et sikkert lager.

Et eksempel på config/prod.json og tilhørende kommando vises. Nøklene samsvarer én til én med oppslagene via fromEnvironment.

// config/prod.json
{
  "ENV": "prod",
  "API_URL": "https://api.acme.com",
  "ANALYTICS": true
}

// Invocation:
// flutter build appbundle --release \
//   --flavor prod \
//   --dart-define-from-file=config/prod.json

Android: productFlavors

På Android deklarerer De flavors i android/app/build.gradle. Hver flavor overstyrer suffikset til app-ID-en og navnet, slik at bygg kan installeres side om side.

  • applicationIdSuffix legges til basis-ID-en, for eksempel com.acme.app.dev.
  • resValue overstyrer startprogrammets etikett for hver flavor.

Det kreves en oppføring for flavorDimensions før flavorene kan listes opp.

android {
    flavorDimensions "env"
    productFlavors {
        dev {
            dimension "env"
            applicationIdSuffix ".dev"
            resValue "string", "app_name", "Acme Dev"
        }
        staging {
            dimension "env"
            applicationIdSuffix ".staging"
            resValue "string", "app_name", "Acme Staging"
        }
        prod {
            dimension "env"
            resValue "string", "app_name", "Acme"
        }
    }
}

iOS: Schemes og xcconfig

På iOS tilsvarer flavors Xcode-schemes som støttes av byggingskonfigurasjoner og .xcconfig-filer. Hvert scheme angir en egen PRODUCT_BUNDLE_IDENTIFIER og et visningsnavn.

  • Opprett konfigurasjoner som Debug-dev, Release-prod og så videre.
  • En .xcconfig per miljø overstyrer bundle-ID-en og DISPLAY_NAME, som leses i Info.plist via $(DISPLAY_NAME).

Flutter kobler --flavor prod til Xcode-schemet som heter prod.

// ios/Flutter/staging.xcconfig
#include "Generated.xcconfig"
PRODUCT_BUNDLE_IDENTIFIER = com.acme.app.staging
DISPLAY_NAME = Acme Staging

// In Info.plist:
// <key>CFBundleDisplayName</key>
// <string>$(DISPLAY_NAME)</string>

Ressurser per miljø

Flavors trenger ofte ulike ressurser: et farget DEV-banner, et appikon for staging eller en annen Firebase-konfigurasjon.

  • Organiser ressurser i mapper som assets/dev/ og assets/prod/, og velg banen under kjøring fra AppConfig.
  • For native ikoner finner Android automatisk ressurser i src/dev/res, mens iOS bruker ressurskataloger per konfigurasjon.
  • Plasser flavor-spesifikke google-services.json-filer under android/app/src/<flavor>/.

Dart-siden utleder ganske enkelt ressursbanen fra det aktive miljøet.

class AssetPaths {
  final String envName;
  const AssetPaths(this.envName);

  String get logo => 'assets/$envName/logo.png';
  String get configBanner =>
      envName == 'prod' ? '' : 'assets/$envName/banner.png';
}

void main() {
  for (final env in ['dev', 'staging', 'prod']) {
    final paths = AssetPaths(env);
    print('$env logo: ${paths.logo}');
  }
}

Koble AppConfig inn i main()

Løs konfigurasjonen én gang ved oppstart, og gjør den tilgjengelig for widgettreet (via en InheritedWidget, provider eller en service locator). Unngå å lese fromEnvironment dypt inne i widgetene.

  • Bygg konfigurasjonen før runApp.
  • Vis et synlig miljøbanner for bygg som ikke er prod, slik at QA ikke blir forvirret.

Kodeeksempelet nedenfor er Flutter-rammeverkskode, så det kan ikke kjøres alene, men viser det kanoniske startpunktet.

import 'package:flutter/material.dart';

void main() {
  final config = AppConfig.fromEnvironment();
  runApp(MyApp(config: config));
}

class MyApp extends StatelessWidget {
  final AppConfig config;
  const MyApp({super.key, required this.config});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Acme (${config.envName})',
      debugShowCheckedModeBanner: !config.isProd,
      home: const Scaffold(body: Center(child: Text('Home'))),
    );
  }
}

Hurtigsjekk: Hvor ligger konfigurasjonen

De har konfigurert dev-, staging- og prod-flavors. Test forståelsen Deres av hvordan Dart-define-verdier fungerer.

Oppsummering

De har bygget en komplett strategi for flavor- og miljøkonfigurasjon:

  • To lag: native flavors (Android productFlavors, iOS schemes/xcconfig) angir appens identitet, mens dart-define angir kjøringskonfigurasjonen.
  • Typesikkerhet: én Environment-enum og et uforanderlig AppConfig som løses én gang via AppConfig.fromEnvironment().
  • Injisering: send --flavor sammen med --dart-define, eller skaler ryddig med --dart-define-from-file=config/<env>.json.
  • Ressurser: mapper per miljø og native ressurs-overstyringer for ikoner, bannere og Firebase-konfigurasjoner.
  • Viktig innsikt: dart-define bestemmes ved kompilering og bygges inn i binærfilen, så en endring i konfigurasjonen krever alltid en ny bygging.

Gevinsten er at dev, staging og prod installeres side om side, er fullstendig isolert og umulig å forveksle.

Gratis å komme i gang

Lær deg Dart med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
22
Leksjoner
88

Ofte stilte spørsmål

Er leksjonen «Build-varianter og miljøkonfigurasjon» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Mobilutvikling med Flutter, inkludert «Build-varianter og miljøkonfigurasjon», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Mobilutvikling med Flutter inneholder totalt 4 leksjoner.

Hva lærer jeg i «Build-varianter og miljøkonfigurasjon»?

Definer dev-, staging- og prod-varianter med miljøspesifikke ressurser og Dart-define-konfigurasjoner. Du øver på Mobilutvikling med Flutter med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Mobilutvikling med Flutter?

Ingen tidligere erfaring er nødvendig. Mobilutvikling med Flutter på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 1 av 4.

Hvor lang tid tar leksjonen «Build-varianter og miljøkonfigurasjon»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Mobilutvikling med Flutter-leksjonen?

Ja. Alle Mobilutvikling med Flutter-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Build-varianter og miljøkonfigurasjon
  2. Automatiserte pipelines med Fastlane og GitHub Actions
  3. Krasjrapportering og symboliserte stack traces
  4. Remote Config, feature flags og trinnvise utrullinger
← Tilbake til Mobilutvikling med Flutter