상세 컨텐츠

본문 제목

[Flutter] Flavor를 통한 빌드 변형하기

IT/응용

by SINAFLA 2021. 9. 8. 13:18

본문

반응형

  • 프로젝트를 진행하다 보면 하나의 프로젝트를 이용하여 여러 개의 결과물을 추출해야 되는 경우가 생깁니다.
  • 개발 / QA / 배포 버전을 분리하거나 하나의 앱을 이용하여 무료 / 유료 버전을 구분하여 프리미엄 기능을 제공하여야 하는 경우를 예로 들 수 있습니다.
  • flutter 명령어를 입력해서 build나 run 명령어를 이용해서 프로젝트를 구동하게 되면 -h 명령어를 이용해 기존에 사용 방법 외의 —flavor 명령어를 적용할 수 있는 것을 확인할 수 있습니다.
  • —flavor 명령어는 개발자가 입력한 main이나 빌드 설정에 따라 프로젝트가 실행됩니다.
  • flutter 명령어로 andorid app 배포할 경우엔 flutter의 분리 방법 말고도 android의 분리 방법도 같이 적용해야 합니다.

 

Flutter의 Build 변형하기

  1. 개발인지 운영 인지에 대한 정보를 전역으로 관리를 해야합니다. lib 폴더 하위에 environment.dart 파일을 생성합니다.
  2. 생성 후 아래의 코드를 입력합니다.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

enum BuildType {
  development,
  production,
}

class Environment {
  static Environment _instance;

  static Environment get instance => _instance;

  final BuildType _buildType;

  static BuildType get buildType => instance._buildType;

  const Environment._internal(this._buildType);

  factory Environment.newInstance(BuildType buildType) {
    assert(buildType != null);

    if(_instance == null) {
      _instance = Environment._internal(buildType);
    }
    return _instance;
  }

  bool get isDebuggle => _buildType == BuildType.development;

  void run (Widget app) {
    runApp(app);
  }
}
  • BuildType은 개발인지 운영 인지의 상태 값을 주기 위해 선언합니다.
  1. 개발에 반영할 소스를 development.dart 파일을 lib 폴더 하위에 생성하여 코드를 작성합니다.

 

 

import 'dart:convert';

import './environment.dart';

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

class Development extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          body: SubDevelopment()
      ),
    );
  }
}

class SubDevelopment extends StatefulWidget {
  @override
  _SubDevelopmentState createState() => _SubDevelopmentState();
}

class _SubDevelopmentState extends State<SubDevelopment> {
  String text = '';

  @override
  void initState() {
    getValue();
    super.initState();
  }

  void getValue() async {
    var value = await json.decode(await rootBundle.loadString('config/config_dev.json'));
    setState(() {
      text = value['property'] ?? '';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: Text(
          '$text',
          style: TextStyle(
            fontSize: 32,
            fontWeight: FontWeight.bold,
            color: Colors.black,
          ),
        ),
      ),
    );
  }
}

main() => Environment.newInstance(BuildType.development).run(Development());
  1. 운영에 반영할 소스를 production.dart 파일을 lib 폴더 하위에 생성하여 코드를 작성합니다.

 

 

import 'dart:convert';

import './environment.dart';

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

class Production extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SubProduction()
      ),
    );
  }
}

class SubProduction extends StatefulWidget {
  @override
  _SubProductionState createState() => _SubProductionState();
}

class _SubProductionState extends State<SubProduction> {
  String text = '';

  @override
  void initState() {
    getValue();
    super.initState();
  }

  void getValue() async {
    var value = await json.decode(await rootBundle.loadString('config/config_proc.json'));
    setState(() {
      text = value['property'] ?? '';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: Text(
          '$text',
          style: TextStyle(
            fontSize: 32,
            fontWeight: FontWeight.bold,
            color: Colors.black,
          ),
        ),
      ),
    );
  }
}

main() => Environment.newInstance(BuildType.production).run(Production());

 

  • environment.dart 소스 파일에서 newInstance() 메소드는 개발인지, 운영인지 상태 값을 가져와 초기화를 해줍니다.
  • 초기화를 한 후 run() 메소드를 호출하면 runApp() 메소드를 이용해 앱이 실행됩니다. 실행시키고 싶은 위젯이 있는 경우 run 메소드의 파라미터로 Widget을 추가하면 개발자가 추가한 위젯이 실행됩니다.

 

 

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion 30

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.ina.flutter_flavor"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    flavorDimensions "build-type"
    productFlavors {
        development {
            dimension "build-type"
            applicationIdSuffix ".dev"
        }
        production {
            dimension "build-type"
        }
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

 

  • Flutter 프로젝트의 android → app → build.gradle 파일을 열어서 android { } 안에 중간에 flavorDimensions과 아래의 productFlavors {} 내용을 입력해줍니다.
  • build.gradle 에 추가한 내용은 flutter 명령어로 apk을 추출할 때 gradle의 설정에 따라 안드로이드 apk가 생성되기 때문에 flutter의 flavor와 맞춰줍니다.

 

 

반응형

관련글 더보기

댓글 영역