flutter_native_splash icon indicating copy to clipboard operation
flutter_native_splash copied to clipboard

smooth transition from native splash sceen to an animated page - how to do?

Open FetFrumos opened this issue 1 year ago • 10 comments

I am working on a Flutter app for both Android and iOS, and I'm using the flutter_native_splash plugin to create a static splash screen. However, my client wants an animated splash screen immediately following the static one. The plugin doesn't support animations directly, so we agreed on a setup where a static splash screen is displayed first, followed by a custom animated splash screen built within Flutter.

The static splash screen is set up with flutter_native_splash, which scales the image to fit different device screens, but I'm unsure how to perfectly align the image size of the animated splash screen to match the static one.

What are the best practices to synchronize the image dimensions between the static image used by flutter_native_splash and the subsequent animated image in Flutter?

is this even possible? any advice - I will be very grateful

FetFrumos avatar Apr 17 '24 17:04 FetFrumos

@FetFrumos have you found a way to sync the static and dynamic images ?

ziadsarour avatar May 21 '24 02:05 ziadsarour

I thought we could discuss how we could contribute to this open source project, haha^^

CHUNG-HAO avatar Jun 02 '24 14:06 CHUNG-HAO

Same! My client want a "fade in/out" animation

BriceFab avatar Jun 11 '24 12:06 BriceFab

This should be an easy one - at least a fade out on calling FlutterNativeSplash.remove(); right?

wiesnery avatar Aug 13 '24 07:08 wiesnery

Well maybe not - have you checke the code in - https://github.com/jonbhanson/flutter_native_splash/blob/master/lib/flutter_native_splash.dart ? This seems like it only prevents the first frame to draw - so to make a smooth animation, we would need to find the currently displayed image and fade away. Have you tried with a dynamic screenshot maybe?

wiesnery avatar Aug 13 '24 08:08 wiesnery

@BriceFab How did you eventually solve this problem? Thank you.

CHUNG-HAO avatar Aug 13 '24 15:08 CHUNG-HAO

@FetFrumos @CHUNG-HAO

I have maybe found a solution but :

  • not tested on devices with devicePixelRatio != 3
  • on Android >= 12 there is a slide out + fade out transition on the native splash and I don't know how to prevent this

Create 2 splash images, one for iOS and Android < 12 and another one for Android >= 12 :

  • assets/images/splash.png : 1440x1440 for iOS and Android < 12
  • assets/images/splash_android12.png : 1080x1080 for Android >= 12

Here is an explanation on how you have to export your images :

Capture d’écran 2024-09-16 à 21 07 12



assets/images/splash.png assets/images/splash_android12.png
Mobile Image Mobile Android12

Change your pubspec.yaml to :

dependencies:
  device_info_plus: 10.1.2

...

flutter_native_splash:
  color: "#FFFFFF"
  image: assets/images/splash/image.png
  fullscreen: false
  web: false
  ios: true
  info_plist_files:
    - ios/Runner/Info-Debug.plist
    - ios/Runner/Info-Release.plist
  android: true
  android_12:
    color: "#FFFFFF"
    image: assets/images/splash/image_android12.png
    icon_background_color: "#FFFFFF"

Do not forget to generate splash using dart run flutter_native_splash:create

Create your splash route :

class RouteAppSplash extends StatefulWidget {
  const RouteAppSplash({super.key});

  @override
  State<RouteAppSplash> createState() => _RouteAppSplashState();
}

class _RouteAppSplashState extends State<RouteAppSplash> {
  final _completer = Completer();
  var _logoImage = '';
  var _logoScale = 1.0;

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((timestamp) {
      // replace native splash by the custom one
      // scale and image are computed based on device
      _hydrate();

      _completer.future.then((_) async {
        // * 1. run your animation
        // * 2. redirect to your main screen
      });
    });
  }

  Future<void> _hydrate() async {
    if (Platform.isIOS) {
      _logoImage = 'assets/images/splash.png'; // 1440x1440
      _logoScale = 360 / MediaQuery.sizeOf(context).width; // @1x width divided by logical screen width
    } else if (Platform.isAndroid) {
      final deviceInfo = DeviceInfoPlugin();
      final androidInfo = await deviceInfo.androidInfo;
      final version = int.tryParse(androidInfo.version.release ?? '') ?? 0;

      if (version >= 12) {
        _logoImage = 'assets/images/splash_android12.png'; // 1080x1080
        _logoScale = .75;
      } else if (Platform.isAndroid) {
        _logoImage = 'assets/images/splash.png'; // 1440x1440
        _logoScale = 1;
      }
    }

    if (mounted) {
      await precacheImage(AssetImage(_logoImage), context); // precache to avoid blink
    }

    setState(() {});
    FlutterNativeSplash.remove(); // remove native splash
    _completer.complete(); // notify that custom splash is displayed
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: Transform.scale(
          scale: _logoScale,
          child: Image.asset(
            _logoImage,
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

ziadsarour avatar Sep 16 '24 19:09 ziadsarour

@ziadsarour It's a little unclear where you got the FlutterNativeSplash.remove() line from. Is this a custom static method? And how did you manage to avoid the slide-out + smooth disappear transition on the native splash screen in the ios system? On the ios emulator this transition is still present

chahohbily avatar Oct 01 '24 03:10 chahohbily

@FetFrumos @CHUNG-HAO

I have maybe found a solution but :

  • not tested on devices with devicePixelRatio != 3
  • on Android >= 12 there is a slide out + fade out transition on the native splash and I don't know how to prevent this

Create 2 splash images, one for iOS and Android < 12 and another one for Android >= 12 :

  • assets/images/splash.png : 1440x1440 for iOS and Android < 12
  • assets/images/splash_android12.png : 1080x1080 for Android >= 12

Here is an explanation on how you have to export your images :

Change your pubspec.yaml to :

dependencies:
  device_info_plus: 10.1.2

...

flutter_native_splash:
  color: "#FFFFFF"
  image: assets/images/splash/image.png
  fullscreen: false
  web: false
  ios: true
  info_plist_files:
    - ios/Runner/Info-Debug.plist
    - ios/Runner/Info-Release.plist
  android: true
  android_12:
    color: "#FFFFFF"
    image: assets/images/splash/image_android12.png
    icon_background_color: "#FFFFFF"

Do not forget to generate splash using dart run flutter_native_splash:create

Create your splash route :

class RouteAppSplash extends StatefulWidget {
  const RouteAppSplash({super.key});

  @override
  State<RouteAppSplash> createState() => _RouteAppSplashState();
}

class _RouteAppSplashState extends State<RouteAppSplash> {
  final _completer = Completer();
  var _logoImage = '';
  var _logoScale = 1.0;

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((timestamp) {
      // replace native splash by the custom one
      // scale and image are computed based on device
      _hydrate();

      _completer.future.then((_) async {
        // * 1. run your animation
        // * 2. redirect to your main screen
      });
    });
  }

  Future<void> _hydrate() async {
    if (Platform.isIOS) {
      _logoImage = 'assets/images/splash.png'; // 1440x1440
      _logoScale = 360 / MediaQuery.sizeOf(context).width; // @1x width divided by logical screen width
    } else if (Platform.isAndroid) {
      final deviceInfo = DeviceInfoPlugin();
      final androidInfo = await deviceInfo.androidInfo;
      final version = int.tryParse(androidInfo.version.release ?? '') ?? 0;

      if (version >= 12) {
        _logoImage = 'assets/images/splash_android12.png'; // 1080x1080
        _logoScale = .75;
      } else if (Platform.isAndroid) {
        _logoImage = 'assets/images/splash.png'; // 1440x1440
        _logoScale = 1;
      }
    }

    if (mounted) {
      await precacheImage(AssetImage(_logoImage), context); // precache to avoid blink
    }

    setState(() {});
    FlutterNativeSplash.remove(); // remove native splash
    _completer.complete(); // notify that custom splash is displayed
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: Transform.scale(
          scale: _logoScale,
          child: Image.asset(
            _logoImage,
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

if this stuff works should be part of the package

iapicca avatar Oct 27 '24 17:10 iapicca

@ziadsarour It's a little unclear where you got the FlutterNativeSplash.remove() line from. Is this a custom static method? And how did you manage to avoid the slide-out + smooth disappear transition on the native splash screen in the ios system? On the ios emulator this transition is still present

FlutterNativeSplash.remove() is part of the package. I don't have (and never had) a slide-out + smooth disappear transition on iOS, it might be due to your code/configuration

ziadsarour avatar Nov 01 '24 19:11 ziadsarour