Created
April 1, 2026 12:24
-
-
Save dipendra-sharma/e351fe8735cfc58a4857aa90e83ca5b8 to your computer and use it in GitHub Desktop.
Flutter AppScaffold — transparent status bar + nav bar with correct icon brightness on iOS and all Android navigation modes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:flutter/services.dart'; | |
| /// Drop-in replacement for [Scaffold] that enforces: | |
| /// - Transparent status bar (never takes AppBar background color) | |
| /// - Transparent system navigation bar on Android with correct icon brightness | |
| /// - Correct status bar icon brightness on both iOS and Android | |
| /// - Body extends behind bottom navigation bar (use [EdgePaddedListView] | |
| /// inside body to ensure list content is not hidden) | |
| class AppScaffold extends StatelessWidget { | |
| const AppScaffold({ | |
| super.key, | |
| this.appBar, | |
| required this.body, | |
| this.bottomNavigationBar, | |
| this.floatingActionButton, | |
| this.floatingActionButtonLocation, | |
| this.backgroundColor, | |
| this.resizeToAvoidBottomInset = true, | |
| this.extendBodyBehindAppBar, | |
| }); | |
| final PreferredSizeWidget? appBar; | |
| final Widget body; | |
| final Widget? bottomNavigationBar; | |
| final Widget? floatingActionButton; | |
| final FloatingActionButtonLocation? floatingActionButtonLocation; | |
| final Color? backgroundColor; | |
| final bool resizeToAvoidBottomInset; | |
| /// Override for [Scaffold.extendBodyBehindAppBar]. | |
| /// | |
| /// Defaults to `true` when [appBar] is null (body fills the whole screen), | |
| /// and `false` when [appBar] is provided. Set this explicitly when you need | |
| /// a transparent/blurred AppBar with the body drawn behind it. | |
| final bool? extendBodyBehindAppBar; | |
| /// Builds a [SystemUiOverlayStyle] that keeps both status bar and navigation | |
| /// bar transparent with icon brightness matching [themeBrightness]. | |
| /// | |
| /// iOS and Android have opposite brightness conventions: | |
| /// - [statusBarBrightness] (iOS only): Brightness.light → dark icons on light bg | |
| /// - [statusBarIconBrightness] (Android): Brightness.dark → dark icons on light bg | |
| static SystemUiOverlayStyle _overlayStyle(Brightness themeBrightness) { | |
| final iconBrightness = themeBrightness == Brightness.light | |
| ? Brightness.dark | |
| : Brightness.light; | |
| return SystemUiOverlayStyle( | |
| statusBarColor: Colors.transparent, | |
| statusBarBrightness: themeBrightness, // iOS only — NOT inverted | |
| statusBarIconBrightness: iconBrightness, // Android — inverted | |
| systemNavigationBarColor: Colors.transparent, | |
| systemNavigationBarDividerColor: Colors.transparent, | |
| systemNavigationBarIconBrightness: iconBrightness, | |
| systemNavigationBarContrastEnforced: false, // disable auto-scrim Android 10+ | |
| ); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| final theme = Theme.of(context); | |
| final overlayStyle = _overlayStyle(theme.brightness); | |
| // Patch AppBarTheme so any AppBar passed to this scaffold uses our overlay | |
| // style instead of its default (which sets statusBarColor = AppBar bg color). | |
| final patchedTheme = theme.copyWith( | |
| appBarTheme: theme.appBarTheme.copyWith( | |
| systemOverlayStyle: overlayStyle, | |
| ), | |
| ); | |
| return AnnotatedRegion<SystemUiOverlayStyle>( | |
| value: overlayStyle, | |
| child: Theme( | |
| data: patchedTheme, | |
| child: Scaffold( | |
| extendBodyBehindAppBar: extendBodyBehindAppBar ?? (appBar == null), | |
| extendBody: true, | |
| appBar: appBar, | |
| body: body, | |
| bottomNavigationBar: bottomNavigationBar, | |
| floatingActionButton: floatingActionButton, | |
| floatingActionButtonLocation: floatingActionButtonLocation, | |
| backgroundColor: backgroundColor, | |
| resizeToAvoidBottomInset: resizeToAvoidBottomInset, | |
| ), | |
| ), | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment