flutter 開發 android的完整流程

本文主要以一個簡單的android應用程序的開發與發布過程對flutter進行一個比較基礎的介紹。很多過程其實就是按照官方文檔做的,只是中間會穿插一些遇到的問題以及解決方案。

1. 安裝flutter

以macOS為例。

1.1 國內的鏡像配置

flutter的sdk也好,使用過程中也好,都需要從遠程下載,所以我們配置國內的源來加速這些過程。

1.1.1 配置源

根據使用的shell不同,編輯不同的rc文件即可,我使用的是zsh,所以編輯~/.zshrc文件,增加一個源的配置。下面給出國內的一些源,根據自己的速度測試情況來使用。不管選擇哪個鏡像,只需要把對應的代碼加入到rc文件中即可。

Flutter 社區

社區主鏡像,采用多種方式同步 Flutter 開發者資源(推薦)。

export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

上海交大 Linux 用戶組

使用反向代理方式建立的 Flutter 鏡像,數據與站源實時同步。 Pub API 返回值未做處理,可能造成無法訪問的情況。

export PUB_HOSTED_URL=https://dart-pub.mirrors.sjtug.sjtu.edu.cn
export FLUTTER_STORAGE_BASE_URL=https://mirrors.sjtug.sjtu.edu.cn

清華大學 TUNA 協會

定時與 Flutter 社區 Storage 鏡像同步,Pub API 采取定時主動抓取策略,鏡像配置了完善的失敗回源策略(推薦)。

export PUB_HOSTED_URL=https://mirrors.tuna.tsinghua.edu.cn/dart-pub
export FLUTTER_STORAGE_BASE_URL=https://mirrors.tuna.tsinghua.edu.cn/flutter

CNNIC

基于 TUNA 協會的鏡像服務,數據策略與 TUNA 一致,通過非教育網的域名訪問。

export PUB_HOSTED_URL=http://mirrors.cnnic.cn/dart-pub
export FLUTTER_STORAGE_BASE_URL=http://mirrors.cnnic.cn/flutter

騰訊云開源鏡像站

定時(每天凌晨)與 TUNA 協會鏡像同步,數據有延遲,訪問速度有待反饋。

export PUB_HOSTED_URL=https://mirrors.cloud.tencent.com/dart-pub
export FLUTTER_STORAGE_BASE_URL=https://mirrors.cloud.tencent.com/flutter

1.1.2 下載sdk

從國內下載sdk,只需要從上述鏡像中選擇一個,然后訪問FLUTTER_STORAGE_BASE_URL對應的地址加/flutter_infra/releases/目錄去下載即可。

編寫本文時,使用的版本是flutter_macos_v1.12.13+hotfix.5-stable, 已經上傳到百度云了: https://pan.baidu.com/s/1jwEjfzJAaUUrFVHPFZOkog

1.2 解壓并設置環境變量

我的sdk下載位置是 ~/Downloads/flutter_macos_v1.12.13+hotfix.5-stable.zip ,sdk準備解壓到的位置是 ~/codes/private/flutter-projects/sdks, 所以在命令行執行命令 :

cd  ~/codes/private/flutter-projects/sdks
unzip ~/Downloads/flutter_macos_v1.12.13+hotfix.5-stable.zip

命令完成后,我們在rc文件里增加flutter相關的命令到環境變量:

export PATH=$PATH:/Users/z/codes/private/flutter-projects/sdks/flutter/bin/

保存rc文件后,執行source ~/.zshrc使環境變量對當前終端生效。

1.3 運行flutter doctor檢查環境是否正常

在命令行執行flutter doctor命令,如果正常的話,會有類似輸出:

  ╔════════════════════════════════════════════════════════════════════════════╗
  ║                 Welcome to Flutter! - https://flutter.dev                  ║
  ║                                                                            ║
  ║ The Flutter tool uses Google Analytics to anonymously report feature usage ║
  ║ statistics and basic crash reports. This data is used to help improve      ║
  ║ Flutter tools over time.                                                   ║
  ║                                                                            ║
  ║ Flutter tool analytics are not sent on the very first run. To disable      ║
  ║ reporting, type 'flutter config --no-analytics'. To display the current    ║
  ║ setting, type 'flutter config'. If you opt out of analytics, an opt-out    ║
  ║ event will be sent, and then no further information will be sent by the    ║
  ║ Flutter tool.                                                              ║
  ║                                                                            ║
  ║ By downloading the Flutter SDK, you agree to the Google Terms of Service.  ║
  ║ Note: The Google Privacy Policy describes how data is handled in this      ║
  ║ service.                                                                   ║
  ║                                                                            ║
  ║ Moreover, Flutter includes the Dart SDK, which may send usage metrics and  ║
  ║ crash reports to Google.                                                   ║
  ║                                                                            ║
  ║ Read about data we send with crash reports:                                ║
  ║ https://github.com/flutter/flutter/wiki/Flutter-CLI-crash-reporting        ║
  ║                                                                            ║
  ║ See Google's privacy policy:                                               ║
  ║ https://www.google.com/intl/en/policies/privacy/                           ║
  ╚════════════════════════════════════════════════════════════════════════════╝


Doctor summary (to see all details, run flutter doctor -v):
[?] Flutter (Channel stable, v1.12.13+hotfix.5, on Mac OS X 10.15.2 19C57, locale zh-Hans-CN)
[?] Android toolchain - develop for Android devices
    ? Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/setup/#android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, set ANDROID_HOME to that location.
      You may also want to add it to your PATH environment variable.

[?] Xcode - develop for iOS and macOS
    ? Xcode installation is incomplete; a full installation is necessary for iOS development.
      Download at: https://developer.apple.com/xcode/download/
      Or install Xcode via the App Store.
      Once installed, run:
        sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
        sudo xcodebuild -runFirstLaunch
    ? CocoaPods not installed.
        CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/platform-plugins
      To install:
        sudo gem install cocoapods
[!] Android Studio (not installed)
[!] VS Code (version 1.41.1)
    ? Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[!] Connected device
    ! No devices available

! Doctor found issues in 5 categories.

里面的問題我們慢慢解決,只要看到類似輸出,我們前面的步驟就算完成了。

1.4 安裝android開發環境

flutter需要完整的Android Studio開發環境以提供全部的依賴支持。

1.4.1 安裝android studio

a. 下載并安裝Android Studio
b. 啟動Android Studio, 然后通過 ‘Android Studio Setup Wizard’安裝最新的 Android SDK, Android SDK Platform-Tools, 以及 Android SDK Build-Tools, 他們都是 Flutter開發 Android所需要的。
c. 打開avd編輯器,創建一個虛擬設備,如果不知道步驟,可以參考如下內容:

  1. Enable VM acceleration on your machine.
  2. Launch Android Studio > Tools > Android > AVD Manager and select Create Virtual Device. (The Android submenu is only present when inside an Android project.)
  3. Choose a device definition and select Next.
  4. Select one or more system images for the Android versions you want to > emulate, and select Next. An x86 or x86_64 image is recommended.
  5. Under Emulated Performance, select Hardware - GLES 2.0 to enable hardware acceleration.
  6. Verify the AVD configuration is correct, and select Finish.
    For details on the above steps, see Managing AVDs.
  7. In Android Virtual Device Manager, click Run in the toolbar. The emulator starts up and displays the default canvas for your selected OS version and device.

1.5 安裝代碼開發工具

1.5.1 VS Code

下載vscode,然后搜索flutter以及dart的擴展進行安裝即可,安裝后可能需要重啟vscode。


安裝后,cmd+shift+p,然后輸入Run Flutter Doctor驗證安裝是否正確。如果vscode找不到sdk,那么會彈出錯誤,選擇手動指定我們之前下載的flutter sdk的路徑即可。

2. 編寫代碼

2.1 創建工程

在vscode里面,cmd+shift+p,然后輸入Flutter New Project,然后根據提示輸入名稱創建一個project,創建成功后,會直接打開main.dart。

image.png

2.2 啟動工程

保持編輯器打開main.dart的狀態,切換到debug頁簽點擊debug flutter app即可啟動調試。
如果卡在Running gradle assembleDebug,那應該是因為gradle源的問題,我們可以修改到阿里云的源。
a. 修改項目中android/build.gradle文件,把兩個repository配置修改一下

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        // google()
        // jcenter()
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/jcenter' }
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        // google()
        // jcenter()
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/jcenter' }
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

b. 修改Flutter的配置文件, 該文件在Flutter安裝目錄/packages/flutter_tools/gradle/flutter.gradle

buildscript {
    repositories {
        //修改的地方
        //google()
        //jcenter()
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/jcenter' }
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
    }
}

我在啟動的時候,還遇到了一個奇怪的問題,一直卡在Installing build/app/outputs/apk/app.apk...,關掉調試工具,在控制臺輸入flutter run --verbose后,成功運行了app,之后再退出這個命令,回到vscode中打開調試,就正常了。

image.png

2.3 hot-reload

修改main.dart,把文字改成你已經點擊了這么多次按鈕,保存文件,更新直接會同步到設備上:

image.png

2.4 編寫一個入門程序

2.4.1 編寫一個hello world

替換main.dart為一下內容:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'hello world',
      home: Scaffold(
        appBar: AppBar(
          title: Text('標題'),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

如果服務還開著的話,模擬器應該直接會變成如下界面


2.4.2 添加依賴的使用

依賴的添加是在pubspec.yamldependencies里面編輯的,我們打開剛才項目的pubspec.yaml增加一行對english_words的依賴。 更多的依賴可以到dart的依賴庫里面找 https://pub.dev/


如果服務還在運行的話,應該會發現控制臺自動執行了flutter pub get來安裝我們最新聲明的依賴。如果沒有,那么就手動執行這個命令即可。

然后我們修改main.dart如下:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();
    return MaterialApp(
      title: 'hello world',
      home: Scaffold(
        appBar: AppBar(
          title: Text('標題'),
        ),
        body: Center(
          child: Text(wordPair.asPascalCase),
        ),
      ),
    );
  }
}

如果一切正常的話,應該就可以看到如下的一個隨機名字的輸出了:


image.png

2.4.3 一個名字列表的app

我們寫一個稍微復雜一點的app:寫一個列表,列表可以無限滾動,邊滾動,邊加載新的隨機字符串,同時列表每個元素中間都用分割線分開。
完整代碼如下:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }
}

class RandomWords extends StatefulWidget {
  @override
  RandomWordsState createState() => RandomWordsState();
}

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];  /* 存放內容的列表 */
  final _biggerFont = const TextStyle(fontSize: 18.0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );
  }

  Widget _buildSuggestions() {
    return ListView.builder(
        // itemCount: 20,  /* 如果是有限列表,那么通過指定itemCount即可設置列表元素數量。 */
        padding: const EdgeInsets.all(2.0),
        itemBuilder: /*itemBuilder是渲染列表元素的方法。 ListView在滾動的時候或者初始化的時候,會自動計算需要獲取哪些位置的數據,然后通過調用這個方法來獲取內容*/
         (context, i) {
          if (i.isOdd) return Divider(); /*如果是奇數,就渲染一個分割線*/

          final index = i ~/ 2; /*~/運算是除去2之后的整數部分*/
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10)); /*如果現在要取的列表的數據下標已經超過了已有元素的個數,就再往list里面增加10個數據*/
          }
          return _buildRow(_suggestions[index]); /* 渲染一行數據 */
        });
  }

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }
}

細節在代碼里面都注釋了,這里再解釋一下整體的思路:
我們修改myapp的build方法,直接渲染RandomWords組件,然后RandomWords組件的build方法里面會渲染Scaffold組件,然后在body中調用_buildSuggestions方法來渲染ListView。RandomWordsState里面使用_suggestions列表存放數據內容,使用_biggerFont存放字體樣式。_buildSuggestions方法里面編寫了ListView的渲染邏輯(如果是奇數就渲染分割線,如果是偶數就渲染數據元素,數據元素的渲染由_buildRow方法提供。 如果發現渲染元素的時候,元素不夠用,那么就加載十個元素到_suggestions列表中,然后把對應位置的數據進行渲染)

3 構建程序

3.1 檢查 App Manifest

查看默認應用程序清單文件(位于<app dir>/android/app/src/main/中的AndroidManifest.xml文件),并驗證這些值是否正確,特別是:

  • application: 編輯 application 標簽, 這是應用的名稱。

  • uses-permission: 如果您的應用程序代碼不需要Internet訪問,請刪除android.permission.INTERNET權限。標準模板包含此標記是為了啟用Flutter工具和正在運行的應用程序之間的通信。

3.2 查看構建配置

查看”build.gradle”,它位于<app dir>/android/app/,驗證這些值是否正確,尤其是:

  • defaultConfig:

    • applicationId: 指定始終唯一的 (Application Id)appid

    • versionCode & versionName: 指定應用程序版本號和版本號字符串。有關詳細信息,請參考版本文檔

    • minSdkVersion & targetSdkVersion: 指定最低的API級別以及應用程序設計運行的API級別。有關詳細信息,請參閱版本文檔中的API級別部分。

3.3 添加啟動圖標

當一個新的Flutter應用程序被創建時,它有一個默認的啟動器圖標。要自定義此圖標:

  1. 查看Android啟動圖標 設計指南,然后創建圖標。

  2. <app dir>/android/app/src/main/res/目錄中,將圖標文件放入使用配置限定符命名的文件夾中。默認mipmap-文件夾演示正確的命名約定。

  3. AndroidManifest.xml中,將application標記的android:icon屬性更新為引用上一步中的圖標(例如 <application android:icon="@mipmap/ic_launcher" ...)。

  4. 要驗證圖標是否已被替換,請運行您的應用程序并檢查應用圖標

3.4 app簽名

3.4.1 創建 keystore

如果您有現有keystore,請跳至下一步。如果沒有,請通過在運行以下命令來創建一個: keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

注意:保持文件私密; 不要將它加入到公共源代碼控制中。

注意: keytool可能不在你的系統路徑中。它是Java JDK的一部分,它是作為Android Studio的一部分安裝的。有關具體路徑,請百度。

3.4.2 引用應用程序中的keystore

創建一個名為<app dir>/android/key.properties的文件,其中包含對密鑰庫的引用:

storePassword=<前面創建秘鑰時對應的store密碼>
keyPassword=<前面創建秘鑰時對應的key密碼>
keyAlias=key
storeFile=<jks文件的路徑,例如/Users/<user name>/key.jks>

注意: 保持文件私密; 不要將它加入公共源代碼控制中

3.4.3 在gradle中配置簽名

通過編輯<app dir>/android/app/build.gradle文件為您的應用配置簽名

  1. 替換:

    android {
    
    

    為:

    def keystorePropertiesFile = rootProject.file("key.properties")
    def keystoreProperties = new Properties()
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    
    android {
    
    
  2. 替換:

    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
        }
    }
    
    

    為:

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
    
    

    現在,您的應用的release版本將自動進行簽名。

3.5 開啟混淆

默認情況下 flutter 不會開啟 Android 的混淆。

如果使用了第三方 Java 或 Android 庫,也許你想減小 apk 文件的大小或者防止代碼被逆向破解。

3.5.1 配置混淆

創建 /android/app/proguard-rules.pro 文件,并添加以下規則:

#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }

上述配置只混淆了 Flutter 引擎庫,任何其他庫(比如 Firebase)需要添加與之對應的規則。

3.5.2 開啟混淆/壓縮

打開 /android/app/build.gradle 文件,定位到 buildTypes 塊。

release 配置中將 minifyEnableduseProguard 設為 true,再將混淆文件指向上一步創建的文件。

android {

    ...

    buildTypes {

        release {

            signingConfig signingConfigs.release

            minifyEnabled true
            useProguard true

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

        }
    }
}

3.6 解決一些問題

3.6.1 mac上的權限問題

下一步執行構建的時候,可能會報一些命令是從網上下載的,是否要刪除之類的提示,這個時候我們需要通過finder定位到具體報錯的文件位置,然后按住ctrl再點擊文件,就會出現運行選項,點擊運行后,這個文件后面就不會報錯了。

3.6.2 關于android包名的問題

我們前面創建app的時候,vscode默認創建的android包名為com.example.xxxxx,如果需要修改的話,要全局搜索這個包名然后替換,有非常非常多的地方需要替換,如果替換錯誤,可能會導致app無法打包或者無法正確安裝、運行。

3.7 構建一個發布版(release)APK

本節介紹如何構建發布版(release)APK。如果您完成了前一節中的簽名步驟,則會對APK進行簽名。

使用命令行:

  1. cd <app dir> (<app dir> 為您的工程目錄).
  2. 運行flutter build apk (flutter build 默認會包含 --release選項).

打包好的發布APK位于<app dir>/build/app/outputs/apk/app-release.apk

3.8 在設備上安裝發行版APK

按照以下步驟在已連接的Android設備上安裝上一步中構建的APK

使用命令行:

  1. 用USB您的Android設備連接到您的電腦
  2. cd <app dir> .
  3. 運行 flutter install .

3.9 將APK發布到Google Play商店

將應用的release版發布到Google Play商店的詳細說明,請參閱 Google Play publishing documentation. (國內不存在的,但你可以發布到國內的各種應用商店)

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容