flutter 配置不同的開發(fā)環(huán)境(qa/dev/prod)

在移動開發(fā)工作中,我們?yōu)橐粋€項目至少需要配置兩個獨立的開發(fā)環(huán)境:“development開發(fā)環(huán)境” 和 “生產環(huán)境production ”?!∵@樣方便我們在開發(fā)環(huán)境里自由地開發(fā)和測試新的功能,而對生產環(huán)境的用戶不造成絲毫的影響。
以flutter 項目開發(fā)為例:

一、初始化項目

首先我們看入口文件
lib/main.dart

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: '我的項目',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: '我的主頁'),
    );
  }
}

然后,我們給項目建立一個新的主頁,注意這里和項目默認的模板有差異
lib/my_home_page.dart

import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Build flavors'),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // Every value is hardcoded in the app.
            // No development or production variants exist yet.
            new Text('This is the production app.'),
            new Text('Backend API url is https://api.example.com/'),
          ],
        ),
      ),
    );
  }
}

二、把 app 區(qū)分不同的環(huán)境變量

我們舉例,以下的數據是需要和生產環(huán)境區(qū)分開來的
1. APP的標題:開發(fā)環(huán)境為“我的項目 DEV” 生產環(huán)境為“我的項目”
2. 第一個text widget:開發(fā)環(huán)境為“這是正在開發(fā)的APP” 生產環(huán)境為 “這是一個正式上線的APP”
3. API 接口調用:開發(fā)環(huán)境為 https://dev-api.example.com/ ,生產環(huán)境為https://api.example.com/ 。

三、創(chuàng)建配置信息對象

創(chuàng)建一個新文件用來保存所有環(huán)境特定配置信息
lib/app_config.dart

import 'package:meta/meta.dart';

class AppConfig {
  AppConfig({
    @required this.appName,
    @required this.flavorName,
    @required this.apiBaseUrl,
  });

  final String appName;
  final String flavorName;
  final String apiBaseUrl;
}

四、把 AppConfig 轉化為 InheritedWidget類

為了讓我們的AppConfig類成為InheritedWidget,我們將繼承InheritedWidget類,提供獲取實例的靜態(tài)方法“of”,并且重載了“updateShouldNotify”方法。
lib/app_config.dart

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

class AppConfig extends InheritedWidget {
  AppConfig({
    @required this.appName,
    @required this.flavorName,
    @required this.apiBaseUrl,
    @required Widget child,
  }) : super(child: child);

  final String appName;
  final String flavorName;
  final String apiBaseUrl;

  static AppConfig of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(AppConfig);
  }

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => false;
}

需要注意的是:

  • 子構造函數的參數將成為我們的整個MaterialApp實例,我們用AppConfig對象包裝我們的應用程序。
  • 我們創(chuàng)建了一個名為“of”的靜態(tài)方法。 這是InheritedWidgets的慣例。 它使我們能夠在需要時調用AppConfig.of(context)來獲取特定于環(huán)境的配置。
  • 在updateShouldNotify方法中,我們只返回false。 這是因為我們的AppConfig在創(chuàng)建后不會改變。
    接下來,我們?yōu)閮蓚€環(huán)境創(chuàng)建文件。

四.、為不同的環(huán)境創(chuàng)建不同的啟動文件

我們將為每個環(huán)境創(chuàng)建各自的“啟動文件”。 在我們的例子中,我們只有兩個環(huán)境,開發(fā)和生產,所以我們的文件將是main_dev.dart和main_prod.dart。 在每個文件中,我們將使用各自的配置數據創(chuàng)建一個AppConfig類的實例。 我們將MyApp的新實例傳遞給我們的AppConfig widget,以便我們應用中的任何widget都可以輕松獲取配置的實例。 然后,我們將調用runApp,它將成為我們整個應用程序的入口點。
lib/main_dev.dart

import 'package:build_flavors/app_config.dart';
import 'package:build_flavors/main.dart';
import 'package:flutter/material.dart';

void main() {
  var configuredApp = new AppConfig(
    appName: 'Build flavors DEV',
    flavorName: 'development',
    apiBaseUrl: 'https://dev-api.example.com/',
    child: new MyApp(),
  );
  
  runApp(configuredApp);
}

生產應用程序啟動程序文件與開發(fā)文件完全相同,但具有不同的配置值。
lib/main.dart

import 'package:build_flavors/app_config.dart';
import 'package:build_flavors/my_home_page.dart';
import 'package:flutter/material.dart';

// We can remove this line here:
// void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Call AppConfig.of(context) anywhere to obtain the 
    // environment specific configuration 
    var config = AppConfig.of(context);
    
    return new MaterialApp(
      title: config.appName,
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(),
    );
  }
}

在這里,我們只是獲取應用配置實例,并根據我們當前的環(huán)境正確設置我們的MaterialApp標題。 我們刪除了void main()=> runApp(new MyApp())行,因為我們的環(huán)境特定的啟動程序文件將覆蓋該行。
由于我們的整個應用程序都包裝在AppConfig widget中(它繼承了InheritedWidget 類),所以我們可以在任何地方通過調用AppConfig.of(context)來獲取的配置實例。
lib/my_home_page.dart

import 'package:build_flavors/app_config.dart';
import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var config = AppConfig.of(context);
    
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(config.appName),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text('This is the ${config.flavorName} app.'),
            new Text('Backend API url is ${config.apiBaseUrl}'),
          ],
        ),
      ),
    );
  }
}

我們的主頁widget與之前基本相同,但具有特定環(huán)境的值。 像以前一樣,我們通過調用AppConfig.of(context)來獲得AppConfig對象的實例。

五、在不同的環(huán)境下運行APP

我們可以通過運行帶 --target 或者 -t 參數來運行不同的環(huán)境。
因此,在我們的例子中:

  • 運行開發(fā)版本,采用:
flutter run -t lib / main_dev.dart
  • 運行生產版本,采用
flutter run -t lib / main_prod.dart

要在Android上創(chuàng)建一個release版本,我們可以 運行

flutter build apk -t lib / main_ <environment> .dart

我們將為不同的環(huán)境生成對應的APK。 要在iOS上構建版本,只需將apk替換為ios。是不是很方便?

六、 在IntelliJ IDEA / Android Studio等ide中設置運行環(huán)境

如果您在Android Studio或IntelliJ IDEA的開發(fā)ide里與Flutter插件一起使用,可以輕松創(chuàng)建運行單獨環(huán)境所需的運行配置。 首先,點擊運行按鈕旁邊下拉菜單中的編輯配置。


run-configuration-1.png

然后,單擊+按鈕創(chuàng)建一個新的運行配置。 在列表中選擇Flutter


run-configuration-2.png

對于開發(fā)環(huán)境,輸入dev作為名稱。如果需要和你的小伙伴的版本控制中包含此運行配置,請確保選中共享復選框。 然后,為Dart入口點選擇lib / main_dev.dart文件。
Screenshot from 2018-05-18 16-41-38.png

下面這樣是不是很酷?:)


run-configuration-final.png

特別聲明:本文完全參考大牛# Iiro Krankka 的文章 《Separating build environments in Flutter apps》
完整的項目地址在 https://github.com/FlutterRocks/separating_build_environments

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容