Capacitor: White screen after splash screen

Created on 6 Nov 2018  路  23Comments  路  Source: ionic-team/capacitor

Capacitor displays my custom splash screen as normal, however it will always display a white screen next, before finally navigating to the first route page.
Routing module:
const routes: Routes = [ { path: '', redirectTo: '/welcome', pathMatch: 'full' }, { path: 'app', loadChildren: './pages/tabs/tabs.module#TabsPageModule', canActivate: [AuthGuardService] }, { path: 'welcome', loadChildren: './pages/welcome/welcome.module#WelcomePageModule' }, ]

init function from app.ts

`async initializeApp() {

    this.platform.ready().then(async () => {

      try {

        this.statusBarService.setStatusBar('light');

        SplashScreen.hide();

        console.log('App is ready to go!');

      } catch (error) {
        console.log('Capacitor Warn: Some plugins do not yet have web support');
      }
    });
  }`

It is not a nice look for the app. Is there a particular reason it does this? Maybe something to do with the lazy loading?

Most helpful comment

@jcesarmobile I am still having this issue. My app has this config, yet the white webview always blinks before rendering my initial page. I have created a splash screen page with animation and so this blinking white really ruins the affect!

`{
  "appId": "com.app.ping",
  "appName": "Ping",
  "bundledWebRuntime": false,
  "webDir": "www",
  "server": {
    "allowNavigation": [
      "capacitor://localhost",
      "ionic://localhost",
      "http://localhost",
      "http://localhost:8080",
      "http://localhost:8100"
    ]
  },
  "plugins": {
    "SplashScreen": {
      "launchAutoHide": false,
      "showSpinner": true,
      "androidSplashResourceName": "splash",
      "androidScaleType": "CENTER_CROP",
      "androidSpinnerStyle": "small",
      "iosSpinnerStyle": "small",
      "spinnerColor": "#ffffff",
      "backgroundColor": "#34ace0"
    },
    "PushNotifications": {
      "presentationOptions": [
        "sound"
      ]
    }
  },
  "npmClient": "npm"
}
`

All 23 comments

The splash screen is configured to dismiss after 3 seconds, so it will disappear and show the webview, which might be white depending on the app.

In next release there will be a new configuration option launchAutoHide that you can set to false to not autohide, so you can manually hide it as in your code https://github.com/ionic-team/capacitor/pull/956

@jcesarmobile I am still having this issue. My app has this config, yet the white webview always blinks before rendering my initial page. I have created a splash screen page with animation and so this blinking white really ruins the affect!

`{
  "appId": "com.app.ping",
  "appName": "Ping",
  "bundledWebRuntime": false,
  "webDir": "www",
  "server": {
    "allowNavigation": [
      "capacitor://localhost",
      "ionic://localhost",
      "http://localhost",
      "http://localhost:8080",
      "http://localhost:8100"
    ]
  },
  "plugins": {
    "SplashScreen": {
      "launchAutoHide": false,
      "showSpinner": true,
      "androidSplashResourceName": "splash",
      "androidScaleType": "CENTER_CROP",
      "androidSpinnerStyle": "small",
      "iosSpinnerStyle": "small",
      "spinnerColor": "#ffffff",
      "backgroundColor": "#34ace0"
    },
    "PushNotifications": {
      "presentationOptions": [
        "sound"
      ]
    }
  },
  "npmClient": "npm"
}
`

@oliverandersencox did you fix this problem?

Facing the same issue.

adding "launchShowDuration": 3000 fixed my issue

I had this issue, I tried several configuration with no result.
At the end I used this workaround in app.component.ts to avoid the white screen:

this.platform.ready().then(() => {
      setTimeout(() => {
        SplashScreen.hide();
      }, 2000);
}

@ulver2812 I also ended up going with this solution but 500ms works well

Doesn't work. still having this issue after splash screen.

still the same issue!! No solution yet?

Any updates on this. Still the same issue tried all above solutions :)

I confirm the same issue on vuejs mobile web app.

inside capacitor.config.json file, i changed launchShowDuration property value from 0 to 2000 and FadeSplashScreen property value from 'true' to 'false' and it worked. Below is the complete config file.

{
  "appId": "com.your_app",
  "appName": "Your App name",
  "bundledWebRuntime": false,
  "npmClient": "npm",
  "webDir": "www",
  "plugins": {
    "SplashScreen": {
      "launchShowDuration": 2000
    }
  },
  "cordova": {
    "preferences": {
      "AutoHideSplashScreen": "false",
      "ShowSplashScreen": "true",
      "ShowSplashScreenSpinner": "false",
      "ScrollEnabled": "false",
      "android-minSdkVersion": "19",
      "BackupWebStorage": "none",
      "SplashMaintainAspectRatio": "true",
      "FadeSplashScreenDuration": "300",
      "SplashShowOnlyFirstTime": "false",
      "SplashScreen": "screen",
      "SplashScreenDelay": "3000",
      "FadeSplashScreen": "false",
      "AndroidPersistentFileLocation": "Compatibility"
    }
  }
}

Is there an official recommendation for this? I just spend 3 hours trying to fix thinking it was an optimization problem on the angular part but no, everything load in the background but after I call hide() a white screen there before the web view is visible.

You could at a background to html like this:

html {
  background: #000; /* or any other color */
}

Try to pick a color that is the same as (or close to) your final background color.

This is more of a workaround than a real fix, but at least it is a little better than a bright white flash in my opinion.

Is there an official recommendation for this? I just spend 3 hours trying to fix thinking it was an optimization problem on the angular part but no, everything load in the background but after I call hide() a white screen there before the web view is visible.

@distante I had spent more than 24 hours for this. Just gave up.. This must be fixed by the core team !!!

@jcesarmobile That options don't work for me. I should try 'FadeSplashScreen' option again!

What's happening is this:

If you call hide when your app is mounted, at that point the web view is fully loaded, but it has not yet performed the first paint. This can take 100 milliseconds or so, so you end up seeing a blank web view briefly.

The solution to add a short delay (200 milliseconds or so) before actually calling SplashScreen.hide().

// At the point at which your app is fully mounted
setTimeout(() => { Splash.hide(); }, 150);

What's happening is this:

If you call hide when your app is mounted, at that point the web view is fully loaded, but it has not yet performed the first paint. This can take 100 milliseconds or so, so you end up seeing a blank web view briefly.

The solution to add a short delay (200 milliseconds or so) before actually calling SplashScreen.hide().

// At the point at which your app is fully mounted
setTimeout(() => { Splash.hide(); }, 150);

Uhmm, so using like window.load events could help be more precise if the paint takes too long?

Good idea! Put this in the main .js file.

ts document.addEventListener('DOMContentLoaded', async () => { const splashscreen = Plugins.SplashScreen as SplashScreenPluginWeb; await splashscreen.hide(); });

Thanks for the tip @distante, I have added hideOnAppLoaded() to my upcoming splash screen plugin, it does this for you.

Only using DOMContentLoaded is still not foolproof and could still result in problems.

The most foolproof way would be the following:

  1. Change the background-color of <html>. See: https://github.com/ionic-team/capacitor/issues/960#issuecomment-709215809

  2. Listen to DOMContentLoaded as @aparajita suggested.

  3. Now check if this prevents your app from flickering.

  4. If it does? Nice, you're done. If it does not, you probably have a somewhat heavy app. Read on.

  5. Within the DOMContentLoaded eventlistener, add a setTimeout and just experiment with it, to find a duration that works for you. (Be a little generous. If for example 250ms is perfect for you, use 350ms or so to support low-end devices also)

So in the end you will end up with something like this:

html {
  background: #000; /* or any other color */
}
window.addEventListener('DOMContentLoaded', () => {
  setTimeout(() => {
    SplashScreen.hide({
      fadeOutDuration: 250,
    });
  }, 250); // this could range from 0 to probably no more than 2000, just experiment a little
});

What I did was to set an observable in my home page that emits each time my home is checked by angular:

Home Page:

  private componentChecked$$ = new Subject<void>();

  public ngAfterViewChecked(): void {
    this.componentChecked$$.next();
  }

then, I subscribe to it and I wait until it does not emit anything in 100ms (so when it is stable)

  private subscribeToFistEndComponentChecked(): void {
    this.componentChecked$$.pipe(debounceTime(100), first()).subscribe(() => {
      this.globalEvents.emitHomeFinishLoading();
      this.logger.log('End Loading!');
    });
  }

I just hide the splashscreen the first time emitHomeFinishLoading is called. It is not pretty but works all the time. It always wait until the home page is rendered in prod and developer builds for example (with different durations).

As an extra info, I use all my components with changeDetection: ChangeDetectionStrategy.OnPush

@tafelnl I went back and read the documentation on the DOMContentLoaded event, it turns out the window 'load' event is a better choice, because that is not fire until all dependent resources have been loaded (e.g. stylesheets and images), whereas DOMContentLoaded is fired when only the DOM has been loaded.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MatanYadaev picture MatanYadaev  路  3Comments

json-derulo picture json-derulo  路  3Comments

natevw picture natevw  路  3Comments

Kepro picture Kepro  路  3Comments

TayKara picture TayKara  路  3Comments