From fa0b54bb9e262a88781a9a7580ec4196cd528a4a Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 18 Oct 2021 14:45:11 -0700 Subject: [PATCH] Add android_splash_screen (#903) Adds a Flutter app designed for devices running at least Android 12 that exemplifies how to implement an animated splash screen with the SplashScreen API. --- android_splash_screen/.gitignore | 71 ++++++ android_splash_screen/.metadata | 10 + android_splash_screen/README.md | 35 +++ android_splash_screen/analysis_options.yaml | 19 ++ android_splash_screen/android/.gitignore | 11 + .../android/app/build.gradle | 71 ++++++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 41 ++++ .../splash_screen_sample/MainActivity.kt | 211 ++++++++++++++++++ .../app/src/main/res/drawable/android.xml | 24 ++ .../main/res/drawable/android_background.xml | 22 ++ .../main/res/drawable/splashscreen_icon.xml | 80 +++++++ .../app/src/main/res/layout/main_activity.xml | 58 +++++ .../src/main/res/layout/main_activity_2.xml | 76 +++++++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 33 +++ .../android/app/src/main/res/values/color.xml | 21 ++ .../app/src/main/res/values/dimens.xml | 17 ++ .../app/src/main/res/values/styles.xml | 33 +++ .../app/src/profile/AndroidManifest.xml | 7 + android_splash_screen/android/build.gradle | 29 +++ .../android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 6 + android_splash_screen/android/settings.gradle | 11 + android_splash_screen/images/androidIcon.png | Bin 0 -> 33569 bytes android_splash_screen/lib/main.dart | 115 ++++++++++ android_splash_screen/pubspec.yaml | 23 ++ android_splash_screen/test/widget_test.dart | 9 + tool/flutter_ci_script_beta.sh | 1 + tool/flutter_ci_script_dev.sh | 1 + tool/flutter_ci_script_stable.sh | 1 + 35 files changed, 1046 insertions(+) create mode 100644 android_splash_screen/.gitignore create mode 100644 android_splash_screen/.metadata create mode 100644 android_splash_screen/README.md create mode 100644 android_splash_screen/analysis_options.yaml create mode 100644 android_splash_screen/android/.gitignore create mode 100644 android_splash_screen/android/app/build.gradle create mode 100644 android_splash_screen/android/app/src/debug/AndroidManifest.xml create mode 100644 android_splash_screen/android/app/src/main/AndroidManifest.xml create mode 100644 android_splash_screen/android/app/src/main/kotlin/com/example/splash_screen_sample/MainActivity.kt create mode 100644 android_splash_screen/android/app/src/main/res/drawable/android.xml create mode 100644 android_splash_screen/android/app/src/main/res/drawable/android_background.xml create mode 100644 android_splash_screen/android/app/src/main/res/drawable/splashscreen_icon.xml create mode 100644 android_splash_screen/android/app/src/main/res/layout/main_activity.xml create mode 100644 android_splash_screen/android/app/src/main/res/layout/main_activity_2.xml create mode 100644 android_splash_screen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android_splash_screen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android_splash_screen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android_splash_screen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android_splash_screen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android_splash_screen/android/app/src/main/res/values-night/styles.xml create mode 100644 android_splash_screen/android/app/src/main/res/values/color.xml create mode 100644 android_splash_screen/android/app/src/main/res/values/dimens.xml create mode 100644 android_splash_screen/android/app/src/main/res/values/styles.xml create mode 100644 android_splash_screen/android/app/src/profile/AndroidManifest.xml create mode 100644 android_splash_screen/android/build.gradle create mode 100644 android_splash_screen/android/gradle.properties create mode 100644 android_splash_screen/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android_splash_screen/android/settings.gradle create mode 100644 android_splash_screen/images/androidIcon.png create mode 100644 android_splash_screen/lib/main.dart create mode 100644 android_splash_screen/pubspec.yaml create mode 100644 android_splash_screen/test/widget_test.dart diff --git a/android_splash_screen/.gitignore b/android_splash_screen/.gitignore new file mode 100644 index 000000000..fa1413672 --- /dev/null +++ b/android_splash_screen/.gitignore @@ -0,0 +1,71 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +local.properties + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.gradle +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +.dart_tool/ +.packages +build/ +# If you're building an application, you may want to check-in your pubspec.lock +pubspec.lock + +# Directory created by dartdoc +# If you don't generate documentation locally you can remove this line. +doc/api/ + +# Avoid committing generated Javascript files: +*.dart.js +*.info.json # Produced by the --dump-info flag. +*.js # When generated by dart2js. Don't specify *.js if your + # project includes source files written in JavaScript. +*.js_ +*.js.deps +*.js.map + diff --git a/android_splash_screen/.metadata b/android_splash_screen/.metadata new file mode 100644 index 000000000..56bfc2c4d --- /dev/null +++ b/android_splash_screen/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: f4abaa0735eba4dfd8f33f73363911d63931fe03 + channel: stable + +project_type: app diff --git a/android_splash_screen/README.md b/android_splash_screen/README.md new file mode 100644 index 000000000..54c46db31 --- /dev/null +++ b/android_splash_screen/README.md @@ -0,0 +1,35 @@ +# Splash Screen Sample + +A Flutter app that exemplifies how to implement an animated splash screen for Android devices running at least Android 12, the version that supplies the new [SplashScreen API](https://developer.android.com/about/versions/12/features/splash-screen). + +**NOTE:** There is a pub package available to implement static splash screens in your Flutter app: [flutter_native_splash](https://pub.dev/packages/flutter_native_splash). + +## Goals +- Demonstrate the compatibility of animated splash screens and Flutter apps running on Android +- Demonstrate the smoothness achievable by a splash screen as a transition to the Flutter UI + +## The important bits + +### Remove deprecated code + +When creating a Flutter app, the Android code generated may include the deprecated implementation of a splash screen. This includes a definition of `io.flutter.embedding.android.SplashScreenDrawable` in the ` /android/app/src/main/AndroidManifest.xml` file and an implementation of `provideSplashScreen()` in the `/android/app/src/main/kotlin/MainActivity.kt` file. Make sure to remove this code. + +**NOTE:** This should no longer be a concern as of Flutter 2.5. + +### Modify Android build files +In order to support the Android 12 SplashScreen API, you need to: +1. Update the Android `compileSdkVersion` to 31 in the `/android/app/build.gradle` file, and +2. Update the `ext.kotlin_version` to the latest Kotlin extension version (1.5.31 at the time of publication) in the `/android/build.gradle` file. + +### Timing the splash screen animation +In order to ensure a smooth transition between the animated splash screen and the Flutter UI displaying for the first time, be sure to handle both the case where the Flutter UI is ready to be displayed before the animation finishes and vice versa. This can be done by overriding `onFlutterUiDisplayed()` and `onFlutterUiNoLongerDisplayed()` in `/android/app/src/main/kotlin/com/example/splash-screen-sample/MainActivity.kt`, the implementation of `FlutterActivity` in this project. + +## Questions/Issues + +If you have a general question about splash screens or their implementation in Flutter, the best places to go are: + +* [Android 12 Splash Screen Documentation](https://developer.android.com/about/versions/12/features/splash-screen) +* [Flutter Guidance on Adding a Splash Screen to Your App](https://flutter.dev/docs/development/ui/advanced/splash-screen?tab=android-splash-alignment-kotlin-tab) + +If you run into an issue with the sample itself, please file an issue +in the [main Flutter repo](https://github.com/flutter/flutter/issues). diff --git a/android_splash_screen/analysis_options.yaml b/android_splash_screen/analysis_options.yaml new file mode 100644 index 000000000..85f6fbe91 --- /dev/null +++ b/android_splash_screen/analysis_options.yaml @@ -0,0 +1,19 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + strong-mode: + implicit-casts: false + implicit-dynamic: false + +linter: + rules: + avoid_types_on_closure_parameters: true + avoid_void_async: true + cancel_subscriptions: true + close_sinks: true + directives_ordering: true + package_api_docs: true + package_prefixed_library_names: true + test_types_in_equals: true + throw_in_finally: true + unnecessary_statements: true diff --git a/android_splash_screen/android/.gitignore b/android_splash_screen/android/.gitignore new file mode 100644 index 000000000..0a741cb43 --- /dev/null +++ b/android_splash_screen/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/android_splash_screen/android/app/build.gradle b/android_splash_screen/android/app/build.gradle new file mode 100644 index 000000000..8c01625b4 --- /dev/null +++ b/android_splash_screen/android/app/build.gradle @@ -0,0 +1,71 @@ +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 plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.splash_screen_sample" + minSdkVersion 21 + targetSdkVersion 30 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + 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 '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "androidx.core:core-splashscreen:1.0.0-alpha02" + implementation "androidx.core:core:1.5.0-alpha05" + implementation "androidx.core:core-ktx:1.6.0" + implementation 'androidx.constraintlayout:constraintlayout:1.1.2' + implementation "androidx.core:core-animation:1.0.0-alpha02" + implementation "androidx.interpolator:interpolator:1.0.0" + def appcompat_version = "1.3.1" + + implementation "androidx.appcompat:appcompat:$appcompat_version" + // For loading and tinting drawables on older versions of the platform + implementation "androidx.appcompat:appcompat-resources:$appcompat_version" + implementation "androidx.constraintlayout:constraintlayout:2.1.0" +} diff --git a/android_splash_screen/android/app/src/debug/AndroidManifest.xml b/android_splash_screen/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..0dea87e8b --- /dev/null +++ b/android_splash_screen/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android_splash_screen/android/app/src/main/AndroidManifest.xml b/android_splash_screen/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..ad41ca2a4 --- /dev/null +++ b/android_splash_screen/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/android_splash_screen/android/app/src/main/kotlin/com/example/splash_screen_sample/MainActivity.kt b/android_splash_screen/android/app/src/main/kotlin/com/example/splash_screen_sample/MainActivity.kt new file mode 100644 index 000000000..d3efc96d7 --- /dev/null +++ b/android_splash_screen/android/app/src/main/kotlin/com/example/splash_screen_sample/MainActivity.kt @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.splash_screen_sample + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.os.Bundle +import android.transition.AutoTransition +import android.transition.Transition +import android.transition.TransitionManager +import android.view.animation.AccelerateDecelerateInterpolator +import android.view.View +import android.widget.FrameLayout +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.animation.doOnEnd +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.core.splashscreen.SplashScreenViewProvider +import androidx.core.view.postDelayed +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.interpolator.view.animation.FastOutLinearInInterpolator +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() { + + var flutterUIReady : Boolean = false + var initialAnimationFinished : Boolean = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // This activity will be handling the splash screen transition. + val splashScreen = installSplashScreen() + + // The splash screen goes edge to edge, so for a smooth transition to our app, also + // want to draw edge to edge. + WindowCompat.setDecorFitsSystemWindows(window, false) + val insetsController = WindowCompat.getInsetsController(window, window.decorView) + insetsController?.isAppearanceLightNavigationBars = true + insetsController?.isAppearanceLightStatusBars = true + + // The content view needs to be set before calling setOnExitAnimationListener + // to ensure that the SplashScreenView is attached to the right view root. + val rootLayout = findViewById(android.R.id.content) as FrameLayout + View.inflate(this, R.layout.main_activity_2, rootLayout) + + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.container)) { view, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + view.setPadding(insets.left, insets.top, insets.right, insets.bottom) + windowInsets.inset(insets) + } + + // Setting an OnExitAnimationListener on the splash screen indicates + // to the system that the application will handle the exit animation. + // The listener will be called once the app is ready. + splashScreen.setOnExitAnimationListener { splashScreenViewProvider -> + onSplashScreenExit(splashScreenViewProvider) + } + } + + override fun onFlutterUiDisplayed(){ + flutterUIReady = true + + if (initialAnimationFinished) { + hideSplashScreenAnimation() + } + } + + override fun onFlutterUiNoLongerDisplayed(){ + flutterUIReady = false + } + + /** + * Hides the splash screen only when the entire animation has finished and the Flutter UI is ready to display. + */ + private fun hideSplashScreenAnimation(){ + val splashView = findViewById(R.id.container) as ConstraintLayout + splashView + .animate() + .alpha(0.0f) + .setDuration(SPLASHSCREEN_FINAL_ANIMATION_ALPHA_ANIMATION_DURATION) + } + + /** + * Handles the transition from the splash screen to the application. + */ + private fun onSplashScreenExit(splashScreenViewProvider: SplashScreenViewProvider) { + val accelerateInterpolator = FastOutLinearInInterpolator() + val splashScreenView = splashScreenViewProvider.view + val iconView = splashScreenViewProvider.iconView + + // Change the alpha of the main view. + val alpha = ValueAnimator.ofInt(255, 0) + alpha.duration = SPLASHSCREEN_ALPHA_ANIMATION_DURATION + alpha.interpolator = accelerateInterpolator + + // And translate the icon down. + val translationY = ObjectAnimator.ofFloat( + iconView, + View.TRANSLATION_Y, + iconView.translationY, + splashScreenView.height.toFloat() + ) + translationY.duration = SPLASHSCREEN_TY_ANIMATION_DURATION + translationY.interpolator = accelerateInterpolator + + // And play all of the animation together. + val animatorSet = AnimatorSet() + animatorSet.playTogether(alpha) + + // Apply layout constraints of starting frame of animation to + // FrameLayout's container for the TransitionManager to know + // where to start the transition. + val root = findViewById(R.id.container) + val set1 = ConstraintSet().apply { + clone(this@MainActivity, R.layout.main_activity) + } + set1.applyTo(root) + + // Retrieve layout constraints of final frame of animation + // for TransitionManager to know where to end the transition. + val set2 = ConstraintSet().apply { + clone(this@MainActivity, R.layout.main_activity_2) + } + + var transitionStarted = false + val autoTransition = AutoTransition().apply { + interpolator = AccelerateDecelerateInterpolator() + } + autoTransition.addListener(object: Transition.TransitionListener { + override fun onTransitionEnd(transition: Transition) { + initialAnimationFinished = true + + if (flutterUIReady) { + hideSplashScreenAnimation() + } + } + override fun onTransitionCancel(transition: Transition){} + override fun onTransitionPause(transition: Transition) {} + override fun onTransitionResume(transition: Transition) {} + override fun onTransitionStart(transition: Transition) {} + }) + + val alphaUpdateListener: (ValueAnimator) -> Unit = { valueAnimator -> + if (!transitionStarted && valueAnimator.animatedFraction > 0.5) { + transitionStarted = true + + TransitionManager.beginDelayedTransition(root, autoTransition) + iconView.visibility = View.GONE + + // Apply constraints of final frame of animation to + // FrameLayout's container once the transition is in progress. + set2.applyTo(root) + } + splashScreenView.background.alpha = valueAnimator.animatedValue as Int + } + alpha.addUpdateListener(alphaUpdateListener) + + // Once the application is finished, remove the splash screen from our view + // hierarchy. + animatorSet.doOnEnd { + splashScreenViewProvider.remove() + } + + waitForAnimatedIconToFinish(splashScreenViewProvider, splashScreenView) { + animatorSet.start() + } + } + + /** + * Wait until the AVD animation is finished before starting the splash screen dismiss animation. + */ + private fun SplashScreenViewProvider.remainingAnimationDuration() = iconAnimationStartMillis + + iconAnimationDurationMillis - System.currentTimeMillis() + + private fun waitForAnimatedIconToFinish( + splashScreenViewProvider: SplashScreenViewProvider, + view: View, + onAnimationFinished: () -> Unit + ) { + // If wanting to wait for our Animated Vector Drawable to finish animating, can compute + // the remaining time to delay the start of the exit animation. + val delayMillis: Long = + if (WAIT_FOR_AVD_TO_FINISH) splashScreenViewProvider.remainingAnimationDuration() else 0 + view.postDelayed(delayMillis, onAnimationFinished) + } + + private companion object { + const val SPLASHSCREEN_ALPHA_ANIMATION_DURATION = 500 as Long + const val SPLASHSCREEN_TY_ANIMATION_DURATION = 500 as Long + const val SPLASHSCREEN_FINAL_ANIMATION_ALPHA_ANIMATION_DURATION = 250 as Long + const val WAIT_FOR_AVD_TO_FINISH = false + } +} diff --git a/android_splash_screen/android/app/src/main/res/drawable/android.xml b/android_splash_screen/android/app/src/main/res/drawable/android.xml new file mode 100644 index 000000000..cf1152ba6 --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/drawable/android.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/android_splash_screen/android/app/src/main/res/drawable/android_background.xml b/android_splash_screen/android/app/src/main/res/drawable/android_background.xml new file mode 100644 index 000000000..091aab817 --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/drawable/android_background.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/android_splash_screen/android/app/src/main/res/drawable/splashscreen_icon.xml b/android_splash_screen/android/app/src/main/res/drawable/splashscreen_icon.xml new file mode 100644 index 000000000..d7185482f --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/drawable/splashscreen_icon.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android_splash_screen/android/app/src/main/res/layout/main_activity.xml b/android_splash_screen/android/app/src/main/res/layout/main_activity.xml new file mode 100644 index 000000000..5e0f370c7 --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/layout/main_activity.xml @@ -0,0 +1,58 @@ + + + + + + + + + + diff --git a/android_splash_screen/android/app/src/main/res/layout/main_activity_2.xml b/android_splash_screen/android/app/src/main/res/layout/main_activity_2.xml new file mode 100644 index 000000000..474a52084 --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/layout/main_activity_2.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + diff --git a/android_splash_screen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android_splash_screen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/android_splash_screen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android_splash_screen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/android_splash_screen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android_splash_screen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/android_splash_screen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android_splash_screen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/android_splash_screen/android/app/src/main/res/values-night/styles.xml b/android_splash_screen/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..31895e302 --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/android_splash_screen/android/app/src/main/res/values/color.xml b/android_splash_screen/android/app/src/main/res/values/color.xml new file mode 100644 index 000000000..21b791d2f --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/values/color.xml @@ -0,0 +1,21 @@ + + + + #034565 + #034565 + #FFFFFF + diff --git a/android_splash_screen/android/app/src/main/res/values/dimens.xml b/android_splash_screen/android/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000..f76be91ae --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/values/dimens.xml @@ -0,0 +1,17 @@ + + + + 30dp + 10dp + diff --git a/android_splash_screen/android/app/src/main/res/values/styles.xml b/android_splash_screen/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..994813030 --- /dev/null +++ b/android_splash_screen/android/app/src/main/res/values/styles.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/android_splash_screen/android/app/src/profile/AndroidManifest.xml b/android_splash_screen/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..0dea87e8b --- /dev/null +++ b/android_splash_screen/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android_splash_screen/android/build.gradle b/android_splash_screen/android/build.gradle new file mode 100644 index 000000000..d084b6f47 --- /dev/null +++ b/android_splash_screen/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + ext.kotlin_version = '1.5.31' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android_splash_screen/android/gradle.properties b/android_splash_screen/android/gradle.properties new file mode 100644 index 000000000..94adc3a3f --- /dev/null +++ b/android_splash_screen/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android_splash_screen/android/gradle/wrapper/gradle-wrapper.properties b/android_splash_screen/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..bc6a58afd --- /dev/null +++ b/android_splash_screen/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/android_splash_screen/android/settings.gradle b/android_splash_screen/android/settings.gradle new file mode 100644 index 000000000..44e62bcf0 --- /dev/null +++ b/android_splash_screen/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/android_splash_screen/images/androidIcon.png b/android_splash_screen/images/androidIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..72e87ff7f1525004a6ca140e8c5f2c30b1075f56 GIT binary patch literal 33569 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
  • 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 main() async { + runApp(const MyApp()); +} + +/* Main widget that contains the Flutter starter app. */ +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(top: 42, bottom: 250), + child: Align( + alignment: Alignment.topCenter, child: CustomAppBar())), + const Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), + ); + } +} + +/* A Flutter implementation of the last frame of the splashscreen animation */ +class CustomAppBar extends StatelessWidget { + const CustomAppBar({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + Widget titleSection = Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 12, right: 4), + child: ClipRRect( + borderRadius: BorderRadius.circular(36.0), + child: Image.asset( + 'images/androidIcon.png', + width: 72.0, + height: 72.0, + fit: BoxFit.fill, + ), + ), + ), + const Padding( + padding: EdgeInsets.only(top: 3), + child: Text("Super Splash Screen Demo", + style: TextStyle(color: Colors.black54, fontSize: 24)), + ), + ], + ); + return titleSection; + } +} diff --git a/android_splash_screen/pubspec.yaml b/android_splash_screen/pubspec.yaml new file mode 100644 index 000000000..4b53126cc --- /dev/null +++ b/android_splash_screen/pubspec.yaml @@ -0,0 +1,23 @@ +name: splash_screen_sample +description: A sample Flutter app with animated splash screen on Android 12. + +publish_to: 'none' + +version: 1.0.0+1 + +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.4 + +flutter: + uses-material-design: true + assets: + - images/androidIcon.png diff --git a/android_splash_screen/test/widget_test.dart b/android_splash_screen/test/widget_test.dart new file mode 100644 index 000000000..99fe91029 --- /dev/null +++ b/android_splash_screen/test/widget_test.dart @@ -0,0 +1,9 @@ +// Copyright 2020 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('This test always passes', (tester) async {}); +} diff --git a/tool/flutter_ci_script_beta.sh b/tool/flutter_ci_script_beta.sh index 1730f07ef..96e1df85f 100755 --- a/tool/flutter_ci_script_beta.sh +++ b/tool/flutter_ci_script_beta.sh @@ -13,6 +13,7 @@ declare -ar PROJECT_NAMES=( "add_to_app/multiple_flutters/multiple_flutters_module" "add_to_app/plugin/flutter_module_using_plugin" "add_to_app/prebuilt_module/flutter_module" + "android_splash_screen" "animations" "experimental/desktop_photo_search" "experimental/federated_plugin/federated_plugin" diff --git a/tool/flutter_ci_script_dev.sh b/tool/flutter_ci_script_dev.sh index 7fae8e217..8e010e113 100755 --- a/tool/flutter_ci_script_dev.sh +++ b/tool/flutter_ci_script_dev.sh @@ -13,6 +13,7 @@ declare -ar PROJECT_NAMES=( "add_to_app/multiple_flutters/multiple_flutters_module" "add_to_app/plugin/flutter_module_using_plugin" "add_to_app/prebuilt_module/flutter_module" + "android_splash_screen" "animations" "experimental/desktop_photo_search" "experimental/federated_plugin/federated_plugin" diff --git a/tool/flutter_ci_script_stable.sh b/tool/flutter_ci_script_stable.sh index 2733b2b5f..cf2801ec8 100755 --- a/tool/flutter_ci_script_stable.sh +++ b/tool/flutter_ci_script_stable.sh @@ -13,6 +13,7 @@ declare -ar PROJECT_NAMES=( "add_to_app/multiple_flutters/multiple_flutters_module" "add_to_app/plugin/flutter_module_using_plugin" "add_to_app/prebuilt_module/flutter_module" + "android_splash_screen" "animations" "experimental/desktop_photo_search" "experimental/federated_plugin/federated_plugin"