npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

capacitor-android-system-bars

v1.2.6

Published

Unified plugin to handle Android system bars (status bar & navigation bar), webview sizing, edge-to-edge display, and fullscreen mode with full compatibility for Android API 21-35+

Readme

capacitor-android-system-bars

Unified plugin to handle Android system bars (status bar & navigation bar), webview sizing, edge-to-edge display, and fullscreen mode with full compatibility for Android API 21-35+.

Replaces:

  • @capawesome/capacitor-android-edge-to-edge-support
  • @capacitor/status-bar
  • Custom fullscreen implementations

🚀 Features

  • Android API 21-35+ Compatibility - Full support from Android 5.0 to Android 15+
  • Edge-to-Edge Display - Native Android 15+ edge-to-edge with backward compatibility
  • Unified System Bars API - Single method to control both status and navigation bars
  • Status Bar Control - Color, styling, and visibility management
  • Navigation Bar Control - Background color and icon styling (with Android 15+ deprecation handling)
  • Fullscreen Mode - Immersive and lean fullscreen modes with professional state management
  • WebView Padding - Automatic webview padding management for Android < 35
  • Lifecycle Handling - Automatic state restoration after screen lock/unlock
  • Modern APIs - Uses latest WindowInsetsController with proper deprecation handling

Install

npm install capacitor-android-system-bars
npx cap sync

Android Configuration

AndroidManifest.xml

The plugin works with ANY windowSoftInputMode setting. No specific configuration is required.

For the best keyboard experience, we recommend using the default behavior (don't specify windowSoftInputMode):

<activity
    android:name=".MainActivity"
    android:theme="@style/AppTheme.NoActionBarLaunch">
    <!-- No windowSoftInputMode needed - Android will use default adjustResize -->
</activity>

Advanced: Custom Keyboard Behavior (Optional)

If you need full control over keyboard handling for specific use cases:

<activity
    android:name=".MainActivity"
    android:windowSoftInputMode="adjustNothing"
    android:theme="@style/AppTheme.NoActionBarLaunch">
</activity>

⚠️ Note: Using adjustNothing requires you to handle keyboard visibility manually with JavaScript using the @capacitor/keyboard plugin. For most apps, the default adjustResize behavior provides better user experience as Android automatically handles keyboard positioning.

Ionic/Angular CSS Configuration

If you're using Ionic with Android 35+ (Android 15), you may need to add CSS to prevent modals and popovers from inheriting window insets padding. Add this to your src/global.scss:

// Fix Android 35+ modal/popover padding issue
// Android 35+ Modal Fix - Prevent header/footer from inheriting window insets
ion-modal {
  ion-header {
    // Remove extra padding from header that comes from window insets
    padding-top: 0 !important;

    ion-toolbar {
      padding-top: 0 !important;
      --padding-top: 0 !important;
      --min-height: 56px; // Standard toolbar height

      ion-title {
        padding-top: 0 !important;
        padding-bottom: 0 !important;
      }

      ion-buttons {
        padding-top: 0 !important;
        padding-bottom: 0 !important;
      }
    }
  }

  ion-footer {
    // Remove extra padding from footer that comes from window insets
    padding-bottom: 0 !important;

    ion-toolbar {
      padding-bottom: 0 !important;
      --padding-bottom: 0 !important;
      --min-height: 56px;
    }
  }
}

💡 Why? On Android 35+, the plugin enables edge-to-edge mode which causes modals to inherit window insets. This CSS prevents unwanted padding in modal headers/footers.

Usage

Initialize the Plugin

import { AndroidSystemBars } from 'capacitor-android-system-bars';

export class AppComponent {
  async ngOnInit() {
    await this.initializeSystemBars();
  }

  async initializeSystemBars() {
    try {
      const info = await AndroidSystemBars.initialize();
      console.log('Android API Level:', info.apiLevel);
      console.log('Edge-to-edge supported:', info.supportsEdgeToEdge);

      // Configure based on Android version
      if (info.isAndroid35Plus) {
        // Android 15+: Use overlay mode
        await AndroidSystemBars.setOverlay({ overlay: true });
      }

      // Set both status and navigation bars in ONE call!
      const isDark = document.body.classList.contains('dark');
      await AndroidSystemBars.setSystemBarsStyle({
        style: isDark ? 'DARK' : 'LIGHT',
        color: isDark ? '#111827' : '#f5efef',
      });
    } catch (error) {
      console.error('Error initializing system bars:', error);
    }
  }
}

Theme Integration (Ionic)

// theme.service.ts
import { AndroidSystemBars } from 'capacitor-android-system-bars';

export class ThemeService {
  async toggleTheme() {
    const isDark = document.body.classList.toggle('dark');

    // Update BOTH system bars in ONE call!
    await AndroidSystemBars.setSystemBarsStyle({
      style: isDark ? 'DARK' : 'LIGHT',
      color: isDark ? '#111827' : '#ffffff',
    });
  }
}

Navigation Bar Control

import { AndroidSystemBars } from 'capacitor-android-system-bars';

export class NavigationBarService {
  async setNavigationBarTheme(isDark: boolean) {
    const info = await AndroidSystemBars.initialize();

    await AndroidSystemBars.setNavigationBarStyle({
      style: isDark ? 'DARK' : 'LIGHT',
      // Color is ignored on Android 15+ (automatically transparent)
      color: info.isAndroid35Plus ? undefined : isDark ? '#111827' : '#ffffff',
    });
  }
}

💡 Pro Tip: For most use cases, use setSystemBarsStyle() to control both bars in one call instead of individual methods.

⚠️ Android 15+ Notice: Navigation bar color control is deprecated in Android 15+. The navigation bar is automatically transparent and apps should design for edge-to-edge layouts. See NAVIGATION_BAR_USAGE.md for detailed migration guide.

Fullscreen Mode

import { AndroidSystemBars } from 'capacitor-android-system-bars';

export class FullscreenService {
  async enterFullscreen() {
    await AndroidSystemBars.enterFullscreen({ mode: 'IMMERSIVE' });
  }

  async exitFullscreen() {
    const isDark = document.body.classList.contains('dark');

    // Exit fullscreen with explicit restoration configuration
    await AndroidSystemBars.exitFullscreen({
      restore: {
        style: isDark ? 'DARK' : 'LIGHT',
        color: isDark ? '#111827' : '#ffffff',
      },
    });
  }

  async checkFullscreenStatus() {
    const { active } = await AndroidSystemBars.isFullscreenActive();
    return active;
  }

  async forceExitFullscreen() {
    // Emergency exit if normal exit fails
    await AndroidSystemBars.forceExitFullscreen();
  }
}

Unified System Bars API (Recommended)

For most use cases, use the new setSystemBarsStyle() method to control both status and navigation bars in a single call:

import { AndroidSystemBars } from 'capacitor-android-system-bars';

export class ThemeService {
  async setAppTheme(theme: 'light' | 'dark') {
    const config =
      theme === 'dark' ? { style: 'DARK' as const, color: '#111827' } : { style: 'LIGHT' as const, color: '#ffffff' };

    // One call sets both status AND navigation bars!
    await AndroidSystemBars.setSystemBarsStyle(config);
  }

  async setCustomTheme() {
    // Different styles for each bar
    await AndroidSystemBars.setSystemBarsStyle({
      statusBar: { style: 'LIGHT', color: '#ffffff' },
      navigationBar: { style: 'DARK', color: '#000000' },
    });
  }
}

🎯 Best Practice: Use setSystemBarsStyle() for theme changes and setStatusBarStyle()/setNavigationBarStyle() for individual bar control.

API Compatibility Matrix

| Android Version | API Level | Edge-to-Edge | System UI Flags | WindowInsets API | Notes | | --------------- | --------- | ------------ | --------------- | ---------------- | ----------------------------- | | 15+ | 35+ | ✅ Native | ✅ Deprecated | ✅ Required | Full edge-to-edge enforcement | | 14 | 34 | ⚠️ Partial | ✅ Works | ✅ Available | Transitional support | | 11-13 | 30-33 | ⚠️ Manual | ✅ Works | ✅ Available | Manual webview padding | | 8.1-10 | 27-29 | ❌ No | ✅ Primary | ⚠️ Limited | Use System UI flags only | | 5.0-8.0 | 21-26 | ❌ No | ✅ Primary | ❌ No | Legacy System UI flags |

Migration Guide

From v1.2.0 (API Redesign)

The API has been redesigned for improved clarity and reduced code complexity. All old methods are still supported with deprecation warnings.

Theme Changes (2 calls → 1 call)

Before:

// Required TWO separate calls
await AndroidSystemBars.setStyle({ style: 'DARK', color: '#111827' });
await AndroidSystemBars.setNavigationBarStyle({ style: 'DARK', color: '#111827' });

After:

// ONE call for both bars!
await AndroidSystemBars.setSystemBarsStyle({
  style: 'DARK',
  color: '#111827',
});

Fullscreen Exit (Confusing → Clear)

Before:

// Confusing: what do these parameters apply to?
await AndroidSystemBars.exitFullscreen({
  style: 'LIGHT',
  color: '#ffffff',
});

After:

// Crystal clear restoration intent
await AndroidSystemBars.exitFullscreen({
  restore: {
    style: 'LIGHT', // Clearly applies to both bars
    color: '#ffffff', // Clearly applies to both bars
  },
});

Method Naming (Ambiguous → Specific)

Before:

await AndroidSystemBars.setStyle({ style: 'DARK' }); // What bar?
await AndroidSystemBars.hide(); // Hide what?
await AndroidSystemBars.show(); // Show what?

After:

await AndroidSystemBars.setStatusBarStyle({ style: 'DARK' }); // Clear!
await AndroidSystemBars.hideStatusBar(); // Clear!
await AndroidSystemBars.showStatusBar(); // Clear!

From @capawesome/capacitor-android-edge-to-edge-support

// Before
import { EdgeToEdge } from '@capawesome/capacitor-android-edge-to-edge-support';
await EdgeToEdge.enable();

// After
import { AndroidSystemBars } from 'capacitor-android-system-bars';
const info = await AndroidSystemBars.initialize();
if (info.isAndroid35Plus) {
  await AndroidSystemBars.setOverlay({ overlay: true });
}

From @capacitor/status-bar

// Before
import { StatusBar, Style } from '@capacitor/status-bar';
await StatusBar.setStyle({ style: Style.Dark });

// After (v1.2.0+)
import { AndroidSystemBars } from 'capacitor-android-system-bars';
await AndroidSystemBars.setSystemBarsStyle({ style: 'DARK' }); // Sets both bars!

// Or for status bar only
await AndroidSystemBars.setStatusBarStyle({ style: 'DARK' });

📖 For detailed migration examples and the complete new API guide, see NEW_API_DESIGN.md

API

initialize()

initialize() => Promise<InitializeResult>

Initialize plugin and get device info

Returns: Promise<InitializeResult>


setSystemBarsStyle(...)

setSystemBarsStyle(options: SetSystemBarsStyleOptions) => Promise<void>

Set both status bar AND navigation bar style/color in one call This is the recommended method for most use cases

| Param | Type | | ------------- | ------------------------------------------------------------------------------- | | options | SetSystemBarsStyleOptions |


setStatusBarStyle(...)

setStatusBarStyle(options: SetStatusBarStyleOptions) => Promise<void>

Set ONLY status bar style and color

| Param | Type | | ------------- | ----------------------------------------------------------------------------- | | options | SetStatusBarStyleOptions |


setNavigationBarStyle(...)

setNavigationBarStyle(options: SetNavigationBarStyleOptions) => Promise<void>

Set ONLY navigation bar style and color

| Param | Type | | ------------- | ------------------------------------------------------------------------------------- | | options | SetNavigationBarStyleOptions |


hideStatusBar()

hideStatusBar() => Promise<void>

Hide status bar


showStatusBar()

showStatusBar() => Promise<void>

Show status bar


hideNavigationBar()

hideNavigationBar() => Promise<void>

Hide navigation bar


showNavigationBar()

showNavigationBar() => Promise<void>

Show navigation bar


enterFullscreen(...)

enterFullscreen(options: EnterFullscreenOptions) => Promise<void>

Enter fullscreen mode (hides both status and navigation bars)

| Param | Type | | ------------- | ------------------------------------------------------------------------- | | options | EnterFullscreenOptions |


exitFullscreen(...)

exitFullscreen(options?: ExitFullscreenOptions | undefined) => Promise<void>

Exit fullscreen mode and restore system bars

| Param | Type | | ------------- | ----------------------------------------------------------------------- | | options | ExitFullscreenOptions |


isFullscreenActive()

isFullscreenActive() => Promise<{ active: boolean; }>

Check if fullscreen mode is currently active

Returns: Promise<{ active: boolean; }>


forceExitFullscreen()

forceExitFullscreen() => Promise<void>

Force exit fullscreen mode (emergency fallback)


setOverlay(...)

setOverlay(options: SetOverlayOptions) => Promise<void>

Set overlay mode (Android 35+ only)

| Param | Type | | ------------- | --------------------------------------------------------------- | | options | SetOverlayOptions |


getInsets()

getInsets() => Promise<InsetsResult>

Get current window insets information

Returns: Promise<InsetsResult>


setStyle(...)

setStyle(options: SetStatusBarStyleOptions) => Promise<void>

| Param | Type | | ------------- | ----------------------------------------------------------------------------- | | options | SetStatusBarStyleOptions |


hide()

hide() => Promise<void>

show()

show() => Promise<void>

Interfaces

InitializeResult

| Prop | Type | Description | | -------------------------- | -------------------- | --------------------------------------------- | | apiLevel | number | Android API level | | isAndroid35Plus | boolean | Whether device is running Android 35+ | | supportsEdgeToEdge | boolean | Whether device supports edge-to-edge natively | | supportsWindowInsets | boolean | Whether device supports WindowInsets API | | statusBarHeight | number | Status bar height in pixels | | navigationBarHeight | number | Navigation bar height in pixels |

SetSystemBarsStyleOptions

| Prop | Type | Description | | ------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | statusBar | { style?: 'LIGHT' | 'DARK' | 'DEFAULT'; color?: string; } | Status bar configuration | | navigationBar | { style?: 'LIGHT' | 'DARK' | 'DEFAULT'; color?: string; } | Navigation bar configuration | | style | 'LIGHT' | 'DARK' | 'DEFAULT' | Apply same style to both bars (shorthand) If specified, overrides individual statusBar/navigationBar style | | color | string | Apply same color to both bars (shorthand) If specified, overrides individual statusBar/navigationBar color |

SetStatusBarStyleOptions

| Prop | Type | Description | | ----------- | ------------------------------------------- | -------------------------------------------------------------- | | style | 'LIGHT' | 'DARK' | 'DEFAULT' | Status bar style | | color | string | Status bar background color (hex format: #RRGGBB or #AARRGGBB) |

SetNavigationBarStyleOptions

| Prop | Type | Description | | ----------- | ------------------------------------------- | ------------------------------------------------------------------ | | style | 'LIGHT' | 'DARK' | 'DEFAULT' | Navigation bar style | | color | string | Navigation bar background color (hex format: #RRGGBB or #AARRGGBB) |

EnterFullscreenOptions

| Prop | Type | Description | | ---------- | ---------------------------------- | -------------------- | | mode | 'IMMERSIVE' | 'LEAN' | Fullscreen mode type |

ExitFullscreenOptions

| Prop | Type | Description | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | | restore | { statusBar?: { style?: 'LIGHT' | 'DARK' | 'DEFAULT'; color?: string; }; navigationBar?: { style?: 'LIGHT' | 'DARK' | 'DEFAULT'; color?: string; }; style?: 'LIGHT' | 'DARK' | 'DEFAULT'; color?: string; } | System bars configuration to restore after exiting fullscreen If not provided, will restore to system default |

SetOverlayOptions

| Prop | Type | Description | | ------------- | -------------------- | ------------------------------------------------- | | overlay | boolean | Whether to enable overlay mode (Android 35+ only) |

InsetsResult

| Prop | Type | Description | | -------------------------- | -------------------- | ---------------------------------- | | top | number | Top inset (status bar area) | | bottom | number | Bottom inset (navigation bar area) | | left | number | Left inset | | right | number | Right inset | | statusBarVisible | boolean | Whether status bar is visible | | navigationBarVisible | boolean | Whether navigation bar is visible |

Troubleshooting

Common Issues

  1. Content hidden under status bar (Android < 35)

    • The plugin automatically applies webview padding
    • No manual padding needed
  2. Screen unlock resets system UI

    • Plugin automatically handles lifecycle events
    • State is restored automatically
  3. Keyboard covering input fields

    • Recommended: Don't specify windowSoftInputMode in AndroidManifest.xml (let Android use default)
    • Android will automatically handle keyboard positioning with adjustResize mode
    • Advanced: Use adjustNothing + @capacitor/keyboard plugin for manual control
    • See KEYBOARD_MODE_EXPLANATION.md for details
  4. Modal headers/footers have extra padding (Android 35+)

    • This is expected behavior with edge-to-edge mode
    • Fix by adding CSS to your src/global.scss:
      ion-modal,
      ion-popover {
        --ion-safe-area-top: 0 !important;
        --ion-safe-area-bottom: 0 !important;
        ion-header {
          padding-top: 0 !important;
        }
        ion-footer {
          padding-bottom: 0 !important;
        }
      }
    • See ANDROID_35_MODAL_PADDING_FIX.md for complete solution
  5. AdMob banner positioning issues

    • Plugin coordinates with system bar state changes
    • No additional configuration needed
  6. Using deprecated methods

    • Old methods like setStyle(), hide(), show() still work but show deprecation warnings
    • Migrate to new unified API (setSystemBarsStyle(), hideStatusBar(), showStatusBar()) for better clarity

Contributing

See CONTRIBUTING.md for details.

License

MIT © Wael M.Elsaid