Skip to content

Commit

Permalink
Check the file type and exit with error if it is not a PNG. Updated d…
Browse files Browse the repository at this point in the history
…ocumentation with more FAQs. Fixed bug that was preventing copying of images for web. Fixes #192. Changed the example to show a secondary splash screen. Check that android, web, and ios folders exist before updating respective splash screen.
  • Loading branch information
jonbhanson committed Aug 3, 2021
1 parent d4b5f98 commit f8c56be
Show file tree
Hide file tree
Showing 21 changed files with 299 additions and 158 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,10 @@
## [1.2.1] - (2021-Jul-19)
* Check the file type and exit with error if it is not a PNG.
* Updated documentation with more FAQs.
* Fixed bug that was preventing copying of images for web. Fixes [#192](https://github.com/jonbhanson/flutter_native_splash/issues/192).
* Changed the example to show a secondary splash screen.
* Check that android, web, and ios folders exist before updating respective splash screen.

## [1.2.0] - (2021-Jun-09)

* Added beta support for Android 12. Closes [#175](https://github.com/jonbhanson/flutter_native_splash/issues/175).
Expand Down
65 changes: 15 additions & 50 deletions README.md
Expand Up @@ -17,7 +17,7 @@ First, add `flutter_native_splash` as a dev dependency in your pubspec.yaml file

```yaml
dev_dependencies:
flutter_native_splash: ^1.2.0
flutter_native_splash: ^1.2.1
```

Don't forget to `flutter pub get`.
Expand Down Expand Up @@ -50,7 +50,7 @@ flutter_native_splash:
# the leading # character.

# The image parameter allows you to specify an image used in the splash screen. It must be a
# png file.
# png file and should be sized for 4x pixel density.
#image: assets/splash.png

# The color_dark, background_image_dark, and image_dark are parameters that set the background
Expand Down Expand Up @@ -120,7 +120,7 @@ To specify the yaml file location just add --path with the command in the termin
flutter pub run flutter_native_splash:create --path=path/to/my/file.yaml
```

# Android 12 beta support
# Beta support for Android 12

Android 12 has a [new method](https://developer.android.com/about/versions/12/features/splash-screen) of adding splash screens, which consists of specifying the window background, animated app icon, and the icon background. Android 12 also supports legacy splash screens as they have been implemented in Flutter and this package. At this time, this package will provide beta support for Android 12 with a legacy implementation.

Expand All @@ -133,61 +133,26 @@ The package will add a `styles.xml` in `values-v31` and `values-night-v31` resou

This package will add support for the new Android 12 splash screens in the future. However, I will wait to see how Flutter adapts to the new splash screen format so that this package can complement Flutter's implementation and avoid reinventing the wheel.

At this time, the splash screen may jump when using `fullscreen` with Android 12.

# Recommendations
## Secondary splash screen:
The native splash screen is displayed while the native app loads the Flutter framework. Once Flutter loads, there may still be resources that need to be loaded before your app is ready. For this reason, you should consider implementing a Flutter splash screen that is displayed while these resources load. Here is a code example of a secondary Flutter splash screen, or use a package from [pub.dev](https://pub.dev).

```dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder(
// Replace the 3 second delay with your initialization code:
future: Future.delayed(Duration(seconds: 3)),
builder: (context, AsyncSnapshot snapshot) {
// Show splash screen while waiting for app resources to load:
if (snapshot.connectionState == ConnectionState.waiting) {
return MaterialApp(home: Splash());
} else {
// Loading is done, return the app:
return MaterialApp(
home: Scaffold(body: Center(child: Text('App loaded'))),
);
}
},
);
}
}
class Splash extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Icon(
Icons.apartment_outlined,
size: MediaQuery.of(context).size.width * 0.785,
),
),
);
}
}
```



## Material resources:
* If you want to use a Material Icon as your splash image, download an icon in [(material.io/resources/icons)](https://material.io/resources/icons/) as **PNG** for **Android**. I recommend using the biggest icon in `drawable-xxxhdpi` folder which was just downloaded for better results.

* Material Colors are available in [material.io/resources/color](https://material.io/resources/color/#!/)
The native splash screen is displayed while the native app loads the Flutter framework. Once Flutter loads, there are probably still resources that need to be loaded before your app is ready. For this reason, you should consider implementing a secondary Flutter splash screen or placeholders that display while these resources load. The [example](https://github.com/jonbhanson/flutter_native_splash/blob/master/example/lib/main.dart) show an implimentation of a secondary splash screen.

# FAQs
## Can I change the duration of the splash screen?
The native splash screen is displayed while the native app loads the Flutter framework. Because the resources in your app cannot load while the native splash screen is displayed, the native splash screen must be as fast as possible. However, if you want a longer splash screen, see the [secondary splash screen](#secondary-splash-screen) recommendation.
The native splash screen is displayed while the native app loads the Flutter framework. Because the resources in your app cannot load while the native splash screen is displayed, the native splash screen must be as fast as possible. Note that delaying the user experience is a poor design decision.

## Are animations/lottie/GIF images supported?
Not at this time. However, you may want to consider a secondary splash screen that supports animation. See the [secondary splash screen](#secondary-splash-screen) recommendation.

## I got the error AAPT: error: style attribute 'android:attr/windowSplashScreenBackground' not found
This attribute is only found in Android 12, so if you are getting this error, it means your project is not fully set up for Android 12. Did you [update your app's build configuration](https://developer.android.com/about/versions/12/setup-sdk#config)?

## I see a white screen between splash screen and app
1. It may be caused by an [iOS splash caching bug](https://stackoverflow.com/questions/33002829/ios-keeping-old-launch-screen-and-app-icon-after-update), which can be solved by uninstalling your app, powering off your device, power back on, and then try reinstalling.
2. It may be caused by the delay due to initialization in your app. To test this, make a test where your `main()` returns a `Container(color: Colors.black);`. If the white flash goes away, it will show that your content is not loading fast enough. You could solve this by creating a [secondary splash screen](https://pub.dev/packages/flutter_native_splash#secondary-splash-screen) or loading a barebones version of your app with placeholders, and then populate the placeholders as content loads.

# Notes
* If splash screen was not updated properly on iOS or if you experience a white screen before splash screen, run `flutter clean` and recompile your app. If that does not solve the problem, delete your app from the device, power down the device, power up device, install and launch app as per [this stackoverflow thread](https://stackoverflow.com/questions/33002829/ios-keeping-old-launch-screen-and-app-icon-after-update).

Expand All @@ -209,7 +174,7 @@ Not at this time. However, you may want to consider a secondary splash screen t

## Web
* A `web/splash` folder will be created for splash screen images and CSS files.
* Your splash image will be resized to `1x`, `2x`, and `3x` sizes and placed in `web/splash/img`.
* Your splash image will be resized to `1x`, `2x`, `3x`, and `4x` sizes and placed in `web/splash/img`.
* The splash style sheet will be added to the app's `web/index.html`, as well as the HTML for the splash pictures.

# Removing
Expand Down
6 changes: 4 additions & 2 deletions example/README.md
Expand Up @@ -4,7 +4,7 @@ A new Flutter application for testing a splash screen.

## Getting Started

This is Flutter's example application. Run it now and you will see that it has Flutter's default white splash screen.
This is Flutter's example application. Run it now and you will see that it has Flutter's default white splash screen, followed by a secondary Flutter splash screen that is displayed after Flutter loads while the app is loading resources.

The pubspec.yaml file has been modified to add a color and icon to the splash screen. To apply these modification, run the following command in the terminal:

Expand All @@ -20,7 +20,9 @@ flutter pub get
flutter pub run flutter_native_splash:create --path=red.yaml
```

The updated splash screen will now appear when you run the app.
The updated splash screen will now appear when you run the app, followed by the secondary splash screen.

Note that with a default configuration, Android has a momentary fade artifact between the native splash and secondary splash screens. In this example, the `android/app/src/main/java/com/example/example/MainActivity.java` has been modified to remove this fade artifact.

A few resources to get you started if this is your first Flutter project:

Expand Down
@@ -1,6 +1,105 @@
package com.example.example;

import android.animation.Animator;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

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

public class MainActivity extends FlutterActivity {

// By default, Flutter makes a 1/2 second fade between the native splash screen
// and the first Flutter frame. However, loading an image for a secondary splash
// screen takes a moment, causing a flash that can be observed in spite of the fade.
// To prevent this, a CustomSplashScreen is used that keeps the splash screen
// visible over the Flutter app 1/2 second to conceal the flash from the image loading.
// To restore the Flutter default behavior, remove the contents of MainActivity and
// the CustomSplashScreen class.

@Nullable
@Override
public SplashScreen provideSplashScreen() {
Drawable manifestSplashDrawable = getSplashScreenFromManifest();
if (manifestSplashDrawable != null) {
return new CustomSplashScreen(manifestSplashDrawable);
} else {
return null;
}
}

/* package */ static final String SPLASH_SCREEN_META_DATA_KEY =
"io.flutter.embedding.android.SplashScreenDrawable";

@Nullable
private Drawable getSplashScreenFromManifest() {
try {
Bundle metaData = getMetaData();
int splashScreenId = metaData != null ? metaData.getInt(SPLASH_SCREEN_META_DATA_KEY) : 0;
return splashScreenId != 0
? Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP
? getResources().getDrawable(splashScreenId, getTheme())
: getResources().getDrawable(splashScreenId)
: null;
} catch (PackageManager.NameNotFoundException e) {
// This is never expected to happen.
return null;
}
}
}

class CustomSplashScreen implements SplashScreen {
private final Drawable drawable;
private io.flutter.embedding.android.DrawableSplashScreen.DrawableSplashScreenView splashView;

public CustomSplashScreen(@NonNull Drawable drawable) {
this.drawable = drawable;
}

@Nullable
@Override
public View createSplashView(@NonNull Context context, @Nullable Bundle savedInstanceState) {
splashView = new io.flutter.embedding.android.DrawableSplashScreen.DrawableSplashScreenView(context);
splashView.setSplashDrawable(drawable, ImageView.ScaleType.FIT_XY);
return splashView;
}

@Override
public void transitionToFlutter(@NonNull Runnable onTransitionComplete) {
if (splashView == null) {
onTransitionComplete.run();
return;
}

splashView
.animate()
.alpha(1.0f)
.setDuration(500)
.setListener(
new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}

@Override
public void onAnimationEnd(Animator animation) {
onTransitionComplete.run();
}

@Override
public void onAnimationCancel(Animator animation) {
onTransitionComplete.run();
}

@Override
public void onAnimationRepeat(Animator animation) {}
});
}
}
2 changes: 1 addition & 1 deletion example/android/build.gradle
Expand Up @@ -5,7 +5,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath 'com.android.tools.build:gradle:4.1.3'
}
}

Expand Down
Binary file added example/assets/1.5x/splash.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/assets/2.0x/splash.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/assets/3.0x/splash.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/assets/4.0x/splash.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/assets/splash.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 49 additions & 15 deletions example/lib/main.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -12,21 +13,32 @@ class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
return FutureBuilder(
future: Init.instance.initialize(),
builder: (context, AsyncSnapshot snapshot) {
// Show splash screen while waiting for app resources to load:
if (snapshot.connectionState == ConnectionState.waiting) {
return MaterialApp(home: Splash());
} else {
// Loading is done, return the app:
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
},
);
}
}
Expand Down Expand Up @@ -115,3 +127,25 @@ class _MyHomePageState extends State<MyHomePage> {
);
}
}

class Splash extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xe1f5fe).withOpacity(1.0),
body: Center(child: Image.asset('assets/splash.png')),
);
}
}

class Init {
Init._();
static final instance = Init._();

Future initialize() async {
// This is where you can initialize the resources needed by your app while
// the splash screen is displayed. Remove the following example because
// delaying the user experience is a bad design practice!
await Future.delayed(Duration(seconds: 3));
}
}
4 changes: 2 additions & 2 deletions example/pubspec.lock
Expand Up @@ -14,7 +14,7 @@ packages:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.2.0"
async:
dependency: transitive
description:
Expand Down Expand Up @@ -89,7 +89,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.1.9"
version: "1.2.1"
flutter_test:
dependency: "direct dev"
description: flutter
Expand Down
14 changes: 8 additions & 6 deletions example/pubspec.yaml
Expand Up @@ -47,9 +47,8 @@ flutter:
uses-material-design: true

# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- assets/

# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
Expand Down Expand Up @@ -97,7 +96,7 @@ flutter_native_splash:
# the leading # character.

# The image parameter allows you to specify an image used in the splash screen. It must be a
# png file.
# png file and should be sized for 4x pixel density.
image: assets/logo_lockup_flutter_vertical.png

# The color_dark, background_image_dark, and image_dark are parameters that set the background
Expand All @@ -109,7 +108,7 @@ flutter_native_splash:
#background_image_dark: "assets/dark-background.png"
image_dark: assets/logo_lockup_flutter_vertical_wht.png

# The android and ios parameters can be used to disable generating a splash screen on a given
# The android, ios and web parameters can be used to disable generating a splash screen on a given
# platform.
#android: false
#ios: false
Expand Down Expand Up @@ -146,4 +145,7 @@ flutter_native_splash:
# do not remove any spaces:
#info_plist_files:
# - 'ios/Runner/Info-Debug.plist'
# - 'ios/Runner/Info-Release.plist'
# - 'ios/Runner/Info-Release.plist'

# To enable support for Android 12, set the following parameter to true. Defaults to false.
#android12: true

0 comments on commit f8c56be

Please sign in to comment.