Isabela Castilho
Isabela Castilho

Reputation: 83

Native android splash screen with Lottie Animation in a Flutter app

I've animated a splash screen logo in After Effects in hopes that I'd only add the LottieAnimationView to the background layer-list of the native splash screen, but it doesn't work.

I don't want a fake splash screen with timers. I really just want to show this animation while the app is initialized, so I'm not creating a Flutter widget for that.

How can a Lottie animation be shown in a layer-list? Is it possible? Can it be done without the need of an Android view (because I really never messed with that)?

Here's what I've tried:

<item>
    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/animation_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:lottie_fileName="logo.json"
        app:lottie_loop="true"
        app:lottie_autoPlay="true" />
</item> 

Upvotes: 4

Views: 2610

Answers (2)

Collin Jackson
Collin Jackson

Reputation: 116848

This example was suggested by jon on a related question.

package com.vcaen.splashscreen.lottie

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.os.Bundle
import android.view.View
import android.view.ViewAnimationUtils
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import com.airbnb.lottie.LottieAnimationView
import java.lang.Integer.max
import java.time.Clock
import java.time.Instant
import java.time.temporal.ChronoUnit

/**
 * This class shows how the splash screen animated icon can be
 * synchronized with a Lottie animation.
 */
class LottieActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {

        // First, you need to get access to the `SplashScreen` instance.
        // You do that by calling `installSplashScreen()` preferably before
        //         `super.onCreate()`. On top of creating the instance, it also
        // set your Application theme to the one set in `postSplashScreenTheme`.
        val splashScreen = installSplashScreen()

        super.onCreate(savedInstanceState)

        // Our content view contains the Lottie animation
        setContentView(R.layout.activity_lottie)

        // We set the OnExitAnimationListener to customize our splash screen animation.
        // This will allow us to take over the splash screen removal animation.
        splashScreen.setOnExitAnimationListener { vp ->
            val lottieView = findViewById<LottieAnimationView>(R.id.animationView)
            lottieView.enableMergePathsForKitKatAndAbove(true)

            // We compute the delay to wait for the end of the splash screen icon
            // animation.
            val splashScreenAnimationEndTime =
                Instant.ofEpochMilli(vp.iconAnimationStartMillis + vp.iconAnimationDurationMillis)
            val delay = Instant.now(Clock.systemUTC()).until(
                splashScreenAnimationEndTime,
                ChronoUnit.MILLIS
            )

            // Once the delay expires, we start the lottie animation
            lottieView.postDelayed({
                vp.view.alpha = 0f
                vp.iconView.alpha = 0f
                lottieView!!.playAnimation()
            }, delay)

            // Finally we dismiss display our app content using a
            // nice circular reveal
            lottieView.addAnimatorListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator?) {
                    val contentView = findViewById<View>(android.R.id.content)
                    val imageView = findViewById<ImageView>(R.id.imageView)

                    val animator = ViewAnimationUtils.createCircularReveal(
                        imageView,
                        contentView.width / 2,
                        contentView.height / 2,
                        0f,
                        max(contentView.width, contentView.height).toFloat()
                    ).setDuration(600)

                    imageView.visibility = View.VISIBLE
                    animator.start()
                }
            })
        }
    }

    override fun onResume() {
        super.onResume()
        // We display our landing activity edge to edge just like the splash screen
        // to have a seamless transition from the system splash screen.
        // This is done in onResume() so we are sure that our Activity is attached
        // to its window.
        WindowCompat.setDecorFitsSystemWindows(window, false)
    }

    override fun onStop() {
        super.onStop()
        finish()
    }
}

Note that the authors discourage using Lottie for splash screens.

Upvotes: 0

Can Kaplan
Can Kaplan

Reputation: 156

AbedElaziz Shehadeh explained how to do this on Medium on 8 Dec 2020:

  1. Start by adding Lottie dependency to your project's build.gradle file found at /android/app/ (I added constraint layout dependency as well).

    dependencies {
        ...
        implementation "com.airbnb.android:lottie:3.5.0"
        implementation "com.android.support.constraint:constraint-layout:2.0.4"
        ...
    }
    
  2. In AndroidManifest.xml remove meta data tag with the name io.flutter.embedding.android.SplashScreenDrawable and replace LaunchTheme under activity tag with NormalTheme so your file will look like the following:

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

    You can remove LaunchTheme from /android/app/res/values/styles.xml as you will not need it any more.

  3. Create a raw directory under /android/app/res/values and copy the .json file, whether you created your own or downloaded a free sample from the link above. In this tutorial, it’s named splash_screen.json.

  4. In order to use the .json file and display the animation view, we need to create a splash view class with its layout. Under /android/app/res, create a new directory called layout (if it does not exist) and then create a new resource file called splash_view.xml. Open the XML file and ensure it looks like the following:

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

For this demo, I set the animation to auto play, with a speed of 1.0. And I don’t want it to loop. You can play with the different values as you wish. The most important part is app:lottie_rawRes that indicates we want to use the json file we added in raw directory. Now, we need to create the splash view class. You can do that by going to /android/app/src/main/kotlin/YOUR-PACKAGE-NAME/ and create a new kotlin class. Call it SplashView then ensure it looks like the following:

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

As you can see, this view is inflatting splash_view layout. The final step is to let MainActivity know about our custom splash view.

  1. Go to /android/app/src/main/kotlin/YOUR-PACKAGE-NAME/ and click on MainActivity.kt. FlutterActivity provides a method called provideSplashScreen and we just need to implement it as follows:

    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.android.SplashScreen
    
    class MainActivity: FlutterActivity() {
        override fun provideSplashScreen(): SplashScreen? = SplashView()
    }
    

The project directory should look like this now: project directory image

That's all for Android. Just run the app and you should see the animated screen at app launch.

Upvotes: 0

Related Questions