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 000000000..db77bb4b7 Binary files /dev/null and b/android_splash_screen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ 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 000000000..17987b79b Binary files /dev/null and b/android_splash_screen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android_splash_screen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android_splash_screen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..09d439148 Binary files /dev/null and b/android_splash_screen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ 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 000000000..d5f1c8d34 Binary files /dev/null and b/android_splash_screen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ 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 000000000..4d6372eeb Binary files /dev/null and b/android_splash_screen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ 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 000000000..72e87ff7f Binary files /dev/null and b/android_splash_screen/images/androidIcon.png differ diff --git a/android_splash_screen/lib/main.dart b/android_splash_screen/lib/main.dart new file mode 100644 index 000000000..dbafcb4af --- /dev/null +++ b/android_splash_screen/lib/main.dart @@ -0,0 +1,115 @@ +/* + * 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. + */ + +import 'package:flutter/material.dart'; + +Future 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"