[譯] 在 Flutter 專案中使用 Lottie 構建原生 Splash 啟動頁|技術點評

Hoarfroster發表於2021-03-13

我們當然可以直接使用 Dart 程式碼為 Flutter 應用程式新增動畫啟動效果,但是,Flutter 應用程式在 Android 和 iOS 中以 FlutterActivity 或 FlutterViewController 的形式啟動,會讓 Flutter 應用程式在實際繪製第一幀之前已經耗費了一段時間。因此,在應用啟動時設定啟動畫面將會帶來更好的使用者體驗。

值得一提的是,在 Flutter 的官方文件中我們可以輕鬆地將靜態影像新增為啟動頁,並且這個頁面上面有充足的文件資訊提供給我們使用。我們事實上只需將影像新增到 Android 的 drawable 資料夾中和 iOS 的資原始檔夾中,然後在 Android 的 styles.xml 和 iOS 的 LaunchScreen.storyboard 中使用它們即可。但是,在針對如何使用 Lottie 等其他庫實現應用程式啟動頁面動畫的功能,我並不能找到相關的參考資料,而這些就是我將在本文中講述的內容。

為什麼我們要使用 Lottie?

Lottie 是一個支援多平臺(包括 Android 與 iOS)的庫,用於通過 Bodymovin 解析 Adobe After Effects 匯出的 JSON 格式的動畫,並以本地方式呈現。這意味著動畫是由專業的設計人員設計的,並使用的是 JSON 檔案匯出,讓我們開發人員無需再額外付出什麼努力,輕輕鬆鬆完成動畫的渲染。在本教程中,我們將使用由 LottieFiles 建立的免費示例檔案,可以在這裡中找到該原檔案。讓我們開始我們的 Flutter + Lottie 之旅吧。

首先讓我們先建立一個新的 Flutter 專案,然後執行以下步驟:

Android

  1. 先新增 Lottie 依賴到你的專案的 app/build.gradle 檔案中(相對於 Flutter 應用程式則是 android/app/build.gradle 檔案)(在這裡我也同樣新增了 Constraint Layout)
dependencies {
   ...
   implementation "com.airbnb.android:lottie:3.5.0" # 當前版本 3.6.0
   implementation "androidx.constraintlayout:constraintlayout:2.0.4"
   # 譯者注:原文為 implementation "com.android.support.constraint:constraint-layout:2.0.4" 但 Lottie 2.8+ 只支援 AndroidX 專案
   ...
}
複製程式碼
  1. AndroidManifest.xml 中刪去 name 為 io.flutter.embedding.android.SplashScreenDrawable 的 <meta-data> 標記並替換 activity 標籤下面的 LaunchThemeNormalTheme,現在你的檔案是這樣的:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.abedelazizshe.flutter_lottie_splash_app">
    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
            android:name="io.flutter.app.FlutterApplication"
            android:label="flutter_lottie_splash_app"
            android:icon="@mipmap/ic_launcher">
        <activity
                android:name=".MainActivity"
                android:launchMode="singleTop"
                android:theme="@style/NormalTheme"
                android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
                android:hardwareAccelerated="true"
                android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
                    android:name="io.flutter.embedding.android.NormalTheme"
                    android:resource="@style/NormalTheme"
            />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
                android:name="flutterEmbedding"
                android:value="2" />
    </application>
</manifest>
複製程式碼

你可以在 /android/app/res/values/styles.xml 資料夾中刪除 LaunchTheme 的定義,因為你將不再需要它。

  1. /android/app/res/values 資料夾下建立一個 raw 目錄,並複製生成的 .json 檔案(無論你是建立自己的檔案還是從上面的連結下載了免費示例)。 在本案例中,JSON 資料夾的名字應該是 splash_screen.json

  2. 為了使用 .json 檔案並顯示動畫檢視,我們需要建立具有其佈局的啟動檢視類。 在 /android/app/res 下,建立一個名為 layout 的新目錄(如果不存在的話),然後建立一個名為 splash_view.xml 的新的佈局資原始檔。 開啟這個 XML 檔案,修改檔案的程式碼為:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.airbnb.lottie.LottieAnimationView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:lottie_autoPlay="true"
        app:lottie_rawRes="@raw/splash_screen"
        app:lottie_loop="false"
        app:lottie_speed="1.00" />

</androidx.constraintlayout.widget.ConstraintLayout>
複製程式碼

在這個示例中,我將動畫設定為自動播放,設定的播放速度為 1.0,並且禁止迴圈播放功能。你可以根據需要使用不同的值。 最重要的部分是 app:lottie_rawRes 屬性,它定義了我們要使用在 raw 目錄中新增的 JSON 檔案。現在,我們需要建立啟動檢視的類。讓我們在 /android/app/src/main/kotlin/YOUR-PACKAGE-NAME 中來建立一個新的 Kotlin 類。將這個類命名為 SplashView,然後修改它的內容為:

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import io.flutter.embedding.android.SplashScreen

class SplashView : SplashScreen {
    override fun createSplashView(context: Context, savedInstanceState: Bundle?): View? =
            LayoutInflater.from(context).inflate(R.layout.splash_view, null, false)

    override fun transitionToFlutter(onTransitionComplete: Runnable) {
        onTransitionComplete.run()
    }
}
複製程式碼

如你所見,此檢視 Inflate 了 splash_view 佈局檔案。最後一步是告訴 MainActivity 我們的自定義啟動檢視。

5.轉到 /android/app/src/main/kotlin/YOUR-PACKAGE-NAME 資料夾,然後單擊 MainActivity.ktFlutterActivity 提供了一種稱為 provideSplashScreen 的方法,修改程式碼為:

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.SplashScreen

class MainActivity: FlutterActivity() {

    override fun provideSplashScreen(): SplashScreen? = SplashView()
}
複製程式碼

現在專案的目錄應該像這樣:

Android 專案目錄

這就是 Android 下 Lottie 構建 Splash 啟動頁的方法,只需要跑一下這個應用程式然後你就能夠看到應用程式啟動時候的動畫了。

Android 中使用了動畫的 Splash 頁面

iOS

讓我們在 iOS 中新增啟動頁:

  1. 開啟專案所在的目錄,單擊 ios 資料夾,然後雙擊 Runner.xcworkspace 開啟你的專案。

2.單擊 Main.storyboard,你將看到帶有一個螢幕的佈局編輯器。 我們需要新增一個新的 ViewController,這將是我們的啟動頁(你可以通過單擊右上角的 + 號來做到這一點。點選之後,螢幕中將彈出一個視窗。我們在輸入框處輸入 View Controller 搜尋並將這個控制元件拖動到編輯器中即可),如以下螢幕快照所示:

新增一個新的 View Controller

  1. 完成了第二步之後,你會看到兩個螢幕。選擇新的 View Controller 然後點選 attributes inspector,然後再點選 is initial view controller .

設定 Splash View Controller 為起始的 View Controller

  1. 我們需要在 ios/Podfile 檔案中新增 Lottie 依賴;
pod 'lottie-ios'
複製程式碼

這個檔案中現在應該是這樣的:(編者注:可能你已經修改了一部分設定了,這裡只是一個案例)

#platform :ios, '9.0' 

target 'Runner' do  
   use_frameworks!  
  
   pod 'lottie-ios' 

end
複製程式碼

然後執行這個應用程式(確保命令列當前在 ios 目錄中。如果不是,那麼就使用 cd 命令將你的目錄移動到 ios 目錄中)

pod install
複製程式碼
  1. 使用 Xcode 將你的生成的 .json 檔案拖到中的根目錄中(請選擇 Copy items if needed 選項),這個檔案可能是你自己建立的檔案,也有可能是你從上面的連結下載了免費樣本。在本案例中它的名字是 splash_screen.json

6.在已經新增了依賴項和 splash_screen.json 檔案的情況下,我們可以建立我們的初始檢視控制器,該控制器將處理顯示的動畫。開啟你的 ios 專案,在專案根目錄處(相對於 Flutter 根目錄:/ios/Runner)建立一個新的名為 SplashViewController 的 Swift 檔案。在類中編寫任何內容之前,我們先來修改一下 AppDelegate.swift 以建立 FlutterEngine。 如果你跳過了這個步驟,則動畫啟動畫面的動畫播放完了以後不能跳轉至 FlutterViewController

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    
    lazy var flutterEngine = FlutterEngine(name: "MyApp")
    
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Runs the default Dart entrypoint with a default Flutter route.
        flutterEngine.run()
        // Used to connect plugins (only if you have plugins with iOS platform code).
        GeneratedPluginRegistrant.register(with: self.flutterEngine)

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
複製程式碼

在這裡我們建立了一個名為 MyAppFlutterEngine(你可以自己決定它的名稱),然後在 applicationdidFinishLaunchingWithOptions 中執行了這個引擎並在引擎中註冊了外掛。需要注意的是預設程式碼是 GeneratePluginRegistrant.register(with:self),請確保它已向 self.flutterEngine 註冊。

  1. 做完了這些,現在我們可以準備 SplashViewController 以顯示動畫。導航到 Flutter 的 View Controller 處,修改程式碼為:
import UIKit
import Lottie

public class SplashViewController: UIViewController {
    
    private var animationView: AnimationView?
    
    public override func viewDidAppear(_ animated: Bool) {
        animationView = .init(name: "splash_screen")
        animationView!.frame = view.bounds
        animationView!.contentMode = .scaleAspectFit
        animationView!.loopMode = .playOnce
        animationView!.animationSpeed = 1.00
        view.addSubview(animationView!)
        animationView!.play{ (finished) in
            self.startFlutterApp()
        }
    }
    
    func startFlutterApp() {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let flutterEngine = appDelegate.flutterEngine
        let flutterViewController =
            FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
        
        flutterViewController.modalPresentationStyle = .custom
        flutterViewController.modalTransitionStyle = .crossDissolve
        
        present(flutterViewController, animated: true, completion: nil)
        
    }
}
複製程式碼

viewDidAppear 中,我們使用新增的 splash_screen.json 檔案初始化動畫檢視。你可以修改諸如 loopModeanimationSpeed 等播放設定。在動畫播放結束後,我們將啟動我們的 Flutter 應用程式。

為了獲取 FlutterViewController,我們必須獲取我們建立並在 AppDelegate.swift 執行的 FlutterEngine 的例項。

let appDelegate = UIApplication.shared.delegate as! AppDelegate
let flutterEngine = appDelegate.flutterEngine
        
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
複製程式碼

然後使用 present(completion :) 啟動檢視控制器。

  1. 現在是時候將第 2 步建立的 ViewControllerSplashViewController 類相連結了。單擊 Main.storyboard 並選擇新的 ViewController,然後從 identity inspector 中選擇 SplashViewController,如螢幕快照所示:

連結到 SplashViewController

  1. 最後一步是設定 Main.storyboard 的主介面,替換掉 LauncherScreen.storyboard。單擊 Runner,選擇 General 選項卡,在 deployment info 下,從下拉選單中將 Main interface 設定為 Main,如螢幕快照所示:

設定 Main interface 為 Main

生成並執行該應用程式,你應該能夠看到動畫的啟動頁了:

就是這樣,你現在已經生成了針對 Android 和 iOS 應用程式的動畫啟動頁。有關完整的原始碼和演示應用程式可以在這裡獲取到:

AbedElazizShe/flutter_lottie_splash_app

如果你有任何疑問,或者有更好的方法可以解決此問題,請記得發表評論嗷。

  • 本文正在參與「掘金 2021 春招闖關活動」, 點選檢視 活動詳情

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章