Skip to content

Flutter still doesn't support modern Android Q-style render-behind-navbar styling #69999

Closed

Description

@Zee2

Use case

As introduced back in 2019, Android Q brought new gesture navigation to Android. This involves a new way of rendering the bottom navigation UI. Instead of rendering the bottom nav as a separate opaque element (that could be fullscreened/hidden, depending on the app's needs), the new gesture nav element is draw on top of the app, with a dynamic contrast effect to make sure the gesture nav bar is always visible.

Please see this blog post for details: https://medium.com/androiddevelopers/gesture-navigation-going-edge-to-edge-812f62e4e83e

As far as I can tell, in Nov 2020, more than a year after this behavior was standardized in Android, it is still not possible to have this modern Android Q-style navigation rendering. In several issues, it has been stated that the Flutter engine is incapable of rendering behind the navbar (aside from when the navbar has been made transparent for fullscreen apps).

Some previous issues that saw no resolution:

#34678
#35748
#40974

image

image

There is a PR that was recently merged here, flutter-team-archive/engine#21730, that seems to reference these issues; however, I've tested master on a modern Android device and it is still not possible to use a fully transparent, permanent gesture nav bar.

Most notable, Flutter Gallery itself does not even implement this behavior, with an outstanding issue that has been open since August 2019! flutter-team-archive/gallery#643

Proposal

This is what modern Android 11 with a native app can do with the gesture nav, vs what Flutter does right now:

image

Here is the modern Android clock app:

You can clearly see how the nav bar is draw on top of the app, with the app drawing across the entire screen.

Given the huge number of comments on all of the issues related to this problem, it is really surprising to me that not only do the official Flutter demos, examples, and galleries not implement this type of navbar (which has been officially recommended for over a year), but that it seems to be actually impossible to implement in Flutter.

Activity

  1. Zee2 commented on Nov 9, 2020

    @Zee2
    Author

    I was tempted to make this a "bug report" because of the severity of this issue, but it isn't really a "bug"; moreso, it is just that Flutter still does not implement Android "best practices". It really makes Flutter apps stand out as somehow non-native or "broken", when modern native apps all use this transparent nav system.

  2. added
    a: layoutSystemChrome and Framework's Layout Issues
    p: material_uimaterial_ui package in flutter/packages
    frameworkflutter/packages/flutter repository. See also f: labels.
    c: proposalA detailed proposal for a change to Flutter
    on Nov 9, 2020
  3. rydmike commented on Dec 4, 2020

    @rydmike
    Contributor

    Totally agree with you @Zee2, I've followed #34678 since its inception and from the same period the issue #35748, they are 1.5 years old and also the slightly newer version in #40974.

    I just started Googling and searching again in hope to find a solution to this, I had not looked for a while and thought it has to be solved by now... and then I landed here and was sad to see we have not really gotten anywhere yet.

    Not fully supporting the correct style of Android system navigation design on Flutter Android apps, does make them seem a lot more none native and foreign on new phones, regardless of if you have a phone where you can configure it to use the nav-gesture bar or optionally the old style buttons. In both cases they still appear with a transparent background, the buttons case like shown in #35748, where the current Flutter bottom navbar issue if you force enable it via Android manifest is also once again described, and then the issues with more modern gesture-bar style like you show above.

    Since Android is one of the main targets of Flutter apps, I now also consider this topic more of an issue than a feature request at this stage, the current situation makes it pretty much impossible to make Flutter apps look they way they should on Android.


    cc: @mariamhas Sure this is not web issue, but I think this issue, now being 1.5 years old, could benefit from some more love and attention by the Android team. So if you have a chance to get it some attention, I think it would be appreciated by the community. The issue is spread over 4 reports (including this one) all with a fair amount of upvotes.

    In my opinion this issue currently degrades the look and feel of Flutter Android apps to 2nd class citizens on modern Android phones. And with modern, I mean about 1.5 year old Android phones and newer, so not even necessarily so modern anymore.

    cc: @timsneath In case you happen to pass by and have time to take a look, please consider this long outstanding issue regarding resource allocation and prioritization, just to show Flutter for Android specific designs some love too. 💙

  4. Zee2 commented on Dec 4, 2020

    @Zee2
    Author

    @rydmike Thanks so much for the write-up and context. You said everything I wanted to say, but more eloquently :) I really hope the Flutter team gets to this ASAP. It is really quite a shame that this behavior is still impossible in Flutter, and it really sticks out like a sore thumb in modern Android.

  5. not-holar commented on Dec 6, 2020

    @not-holar

    Not the nicest solution and definitely not a replacement for official support, but

    you can add this to onCreate of your activity

    window.setDecorFitsSystemWindows(false)
    

    and this to your theme in styles.xml

    <item name="android:navigationBarColor">@android:color/transparent</item>
    

    Please note, this requires you to target sdk 30
    See: #69999 (comment)

    You will also most likely need to add resizeToAvoidBottomInset: false to your Scaffolds

  6. abc873693 commented on Jan 3, 2021

    @abc873693

    Not the nicest solution and definitely not a replacement for official support, but

    you can add this to onCreate of your activity

    window.setDecorFitsSystemWindows(false)
    

    and this to your theme in styles.xml

    <item name="android:navigationBarColor">@android:color/transparent</item>
    

    Please note, this requires you to target sdk 30

    You will also most likely need to add resizeToAvoidBottomInset: false to your Scaffolds

    This feature only work above API 30

    Must be add

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                window.setDecorFitsSystemWindows(false)
            }
  7. notDmDrl commented on Jan 8, 2021

    @notDmDrl

    I tried what @not-holar suggested, but it seemed to work only during splash screen.

    What i did additionally was simply adding AnnotatedRegion<SystemUiOverlayStyle> as the first child of MaterialApp's home property:

    MaterialApp(
        home: AnnotatedRegion<SystemUiOverlayStyle>(
            value: const SystemUiOverlayStyle(
                statusBarColor: Colors.transparent,
                systemNavigationBarColor: Colors.transparent,
                systemNavigationBarIconBrightness: Brightness.light, 
                statusBarIconBrightness: Brightness.light,
          ),
          child: ... home widget
    )

    This was tested on a Pixel 2XL running Android 11 (gesture and 3-button navigation) and Flutter beta 1.24.0-10.2.pre

    Note: Using gesture navigation resulted in transparent navbar with code above, 3-button navigation was translucent.
    Also since iOS already had transparent status and navbars, i hope that AnnotatedRegion<SystemUiOverlayStyle> will not mess up how it looks there (unfortunately i'm not able to test this right now).


    Here is what it looks like in my app:

    Scrolled over "Report a bug" tile:

    Scrolled all the way to the bottom of screen:

    It also worked well with BottomNavigationBar, no changes needed.

  8. rydmike commented on Jan 12, 2021

    @rydmike
    Contributor

    @notDmDrl Thanks the info sounds both interesting and promising. I have tried the AnnotatedRegion too, before and just did now again, but I can't get transparent system navigation bar with it alone to work on Android. For the status bar it works OK, it has always worked when I have used it.

    I suppose I need the configurations mentioned by @not-holar and @abc873693 as well for the system nav bar to become transparent, but for us mere mortal Flutter devs that do not speak any Android native config file magic, the info provided does not give me enough context to understand where to add the mentioned config options based on the given examples. I found the file styles.xml, two actually:

    I have this file: \android\app\src\main\res\values\styles.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <!-- Theme applied to the Android Window while the process is starting -->
        <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
            <!-- Show a splash screen on the activity. Automatically removed when
                 Flutter draws its first frame -->
            <item name="android:windowBackground">@drawable/launch_background</item>
        </style>
        <!-- Theme applied to the Android Window as soon as the process has started.
             This theme determines the color of the Android Window while your
             Flutter UI initializes, as well as behind your Flutter UI while its
             running.
             
             This Theme is only used starting with V2 of Flutter's Android embedding. -->
        <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
            <item name="android:windowBackground">@android:color/white</item>
        </style>
    </resources>

    and this one: \android\app\src\main\res\values-night\styles.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
        <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
            <!-- Show a splash screen on the activity. Automatically removed when
                 Flutter draws its first frame -->
            <item name="android:windowBackground">@drawable/launch_background</item>
        </style>
        <!-- Theme applied to the Android Window as soon as the process has started.
             This theme determines the color of the Android Window while your
             Flutter UI initializes, as well as behind your Flutter UI while its
             running.
             
             This Theme is only used starting with V2 of Flutter's Android embedding. -->
        <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
            <item name="android:windowBackground">?android:colorBackground</item>
        </style>
    </resources>

    So at least for this case I found two files where I assume this

    <item name="android:navigationBarColor">@android:color/transparent</item>

    belongs, but where exactly should I put it?


    When it comes to this one:

    you can add this to onCreate of your activity

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                window.setDecorFitsSystemWindows(false)
            }
    

    Where exactly do I put it? File and place in the file too please. Verbose examples work better than terse one lines in this case.

    Exact info on where to find the file is also appreciated, the structure of the dark arts magical Flutter /android embedder folder is a very deep rabbit hole, for us not familiar with what it hides where.


    I would love to get this to look and work the way it should on newer Android phones.

    Even if it requires Android side config "hacks". Well OK I'm sure they are not hacks, but to me they feel so since I can't do it from Flutter and I don't have any Android native knowledge. Still this is fine as long as it works after configured once. Currently the Flutter Android UI really looks looks so ancient without the transparent system nav bar, regardless of if you setup Android to use the old style system navbar buttons or new gesture bar, they should both be transparent with content scrolling behind them.

    Pity and weird this cannot be setup from Flutter though, it's not like this design is a new thing on Android anymore.

  9. notDmDrl commented on Jan 12, 2021

    @notDmDrl

    I found the file styles.xml, two actually

    @rydmike, well, I only have one styles.xml but I think you can just paste needed code to both.

    Here is mine NormalTheme in styles.xml:

    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
            <item name="android:statusBarColor">@android:color/transparent</item>
            <item name="android:navigationBarColor">@android:color/transparent</item>
    </style>

    Where exactly do I put it? File and place in the file too please.

    Here is also mine MainActivity.kt which basically only has code @not-holar and @abc873693 provided:

    package com.your.app
    
    import android.os.Build
    import android.os.Bundle
    import io.flutter.embedding.android.FlutterFragmentActivity
    
    class MainActivity: FlutterFragmentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                window.setDecorFitsSystemWindows(false)
            }
        }
    }

    Hope this helps 😄

  10. rydmike commented on Jan 12, 2021

    @rydmike
    Contributor

    @notDmDrl Thanks much appreciated, sadly I cannot get it to work.

    The style.xml setup I did is probably OK, yeah I think just do the same for both files that I found should work. They seem to refer to style config for light and dark mode, if I'm guessing right. So same for both should be fine, since it is transparent we want.

    But my MainActivity.kt is different than the one you showed above. I have a FlutterActivity and no FlutterFragmentActivity and it does not compile/build if I add the same onCreate:

    package com.example.example
    import io.flutter.embedding.android.FlutterActivity
    class MainActivity: FlutterActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                window.setDecorFitsSystemWindows(false)
            }
        }
    }
    

    The build bombs and results in this error:

    Launching lib\example5\main.dart on sdk gphone x86 64 arm64 in debug mode...
    Running Gradle task 'assembleDebug'...
    e: ...\example\android\app\src\main\kotlin\com\example\example\MainActivity.kt: (6, 5): 'onCreate' overrides nothing
    e: ...\example\android\app\src\main\kotlin\com\example\example\MainActivity.kt: (6, 47): Unresolved reference: Bundle
    e: ...\example\android\app\src\main\kotlin\com\example\example\MainActivity.kt: (9, 13): Unresolved reference: Build
    e: ...\example\android\app\src\main\kotlin\com\example\example\MainActivity.kt: (9, 38): Unresolved reference: Build
    e: ...\example\android\app\src\main\kotlin\com\example\example\MainActivity.kt: (10, 20): Unresolved reference: setDecorFitsSystemWindows
    
    FAILURE: Build failed with an exception.
    * What went wrong:
    Execution failed for task ':app:compileDebugKotlin'.
    > Compilation error. See log for more details
    
    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    * Get more help at https://help.gradle.org
    BUILD FAILED in 7s
    Exception: Gradle task assembleDebug failed with exit code 1
    

    My MainActivity.kt originally looked like this, and if I change it back to it, the Android app is built and runs fine:

    package com.example.example
    import io.flutter.embedding.android.FlutterActivity
    class MainActivity: FlutterActivity() {
    }
    

    With this, but both the style.xml files modified and using the Annotated region in Flutter, the system navbar is still not transparent, not even on even Android API 30.

    I guess I have different Flutter embedder version in this test app than you do. Like I said, these Flutter embedders seems like black magic... 😄

  11. notDmDrl commented on Jan 12, 2021

    @notDmDrl

    @rydmike, You forgot to import android.os.Build and android.os.Bundle 🙃

    Add

    import android.os.Build
    import android.os.Bundle

    before import io.flutter.embedding.android.FlutterActivity

  12. 2 remaining items

  13. not-holar commented on Jan 13, 2021

    @not-holar

    @rydmike try

    package com.example.example
    
    import android.os.Bundle
    import android.os.Build
    import androidx.annotation.NonNull
    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode.transparent
    import io.flutter.embedding.engine.FlutterEngine
    import io.flutter.plugins.GeneratedPluginRegistrant
    
    class MainActivity: FlutterActivity() {
        override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
            GeneratedPluginRegistrant.registerWith(flutterEngine);
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                window.setDecorFitsSystemWindows(false)
            }
        }
    }
  14. rydmike commented on Jan 13, 2021

    @rydmike
    Contributor

    @notDmDrl Thanks again for all the guidance, it really helped! 😃

    Yes I made the same conclusion about the source of the problem. Sadly nothing I tried seemed to remedy it. Target SDK and compiled SDK set to 29. Opened the Android folder in Studio as well, as expected it complains about the same thing.

    image

    Yes I cleared the cache too. I even deleted the the Android folder and create a new one with flutter create ., did not help. So
    I created a fresh new project in latest Flutter stable with just the default counter app as well, but the same issues prevailed.

    Hmm, go figure... 😕 nothing seemed to help and I was about to give up and just post the above...

    Then I thought SDK 29+ does that mean 29 or higher OR higher than 29? So I changed target SDK and compiled sdk to 30, and wow, not it works! Hooray, thanks!

    This means I can finally make something that looks like shown below, even with old style buttons, and I can theme the translucent Android system navigation bar with the current Flutter theme and change it dynamically as the Flutter app theme changes. This is cool, and it works with Flutter Material BottomNavigationBar too. This demoed with example A) below.

    I can also change the opacity of the system navbar from within the app. If you make it fully transparent a white/dark semi transparent scrim seems to be automatically applied, but if you use a color, like here using the primary color branded background color, you can make the opacity close to 0 and then you only see the buttons. At 0 opacity you get the scrim applied, but still translucent, and of course at 1 it is fully opaque, and in this case using the primary color branded background color. Nice! Just what I was looking for. It is shown in example B) below.

    All demoed in these GIF recordings:

    A) Themed system navbar B) Varied opacity system navbar
    navbar_style_works4 navbar_style_slide_opacity6

    (Sorry the demo GIFs are a bit janky, I had to reduce the framerate on the GIFs a lot to get their size below 10MB each)

    Pity this can't be fully setup from Flutter, but this works well enough for now.

    On a phone that is too old for transparent system navbar, this method should still give it the none transparent primary color branded background color. Which is not too bad looking either. I still need to verify this in practice with some real devices too though.

    Humble thanks again @notDmDrl and @not-holar for the assist! 👍🏻

  15. notDmDrl commented on Jan 13, 2021

    @notDmDrl

    @rydmike, I'm glad that everything works 😃 Also those demo gifs are looking realy cool.

    I think AnnotatedRegion<SystemUiOverlayStyle> with a few lines of code required for styles.xml and MainAktivity.kt are the best way for implementing render-behind-navbar. Although if eventually there'll be a posibility to skip styles.xml and MainAktivity.kt, that would be nice 😃.


    Then I thought SDK 29+ does that mean 29 or higher OR higher than 29? So I changed target SDK and compiled sdk to 30, and wow, now it works!

    My bad😅, changed original comment to have correct info.

  16. marcinsiedlik commented on Feb 19, 2021

    @marcinsiedlik

    Android offers backwards compatible equivalent of

    window.setDecorFitsSystemWindows(false)

    After adding implementation 'androidx.core:core-ktx:1.5.0-beta01' to build.gradle could call it like this:

    WindowCompat.setDecorFitsSystemWindows(window, false)

    I haven't tested it with flutter but use it native android, should work just fine

  17. self-assigned this
    on Mar 15, 2021
  18. fredgrott commented on Apr 12, 2021

    @fredgrott

    note the styles lines need to go in res/values-21 styles file and the windowcompat import block needs an @RequiresApi(Build.VERSION_CODES.LOLLIPOP) annotation

  19. NaikSoftware commented on May 18, 2021

    @NaikSoftware

    This solution window.setDecorFitsSystemWindows(false) breaks MediaQuery.viewInsets.bottom. When keyboard appears on Android 11+ then after smooth animation bottom inset jumps on 16 points up on the end (Pixel 5).

  20. Zee2 commented on Jul 8, 2021

    @Zee2
    Author

    Hallelujah! It is done! After so many years :)

  21. hectorAguero commented on Jul 8, 2021

    @hectorAguero

    So, this snippet works for me in the latest master release.

    Why not make both SystemChrome options the default one, also I see the systemNavigationBarColor of SystemUiOverlayStyle.light() or dark() with a systemNavigationBarColor: Color(0xFF000000) and because of that I need the copyWith method and override with Color(0x00000000) or Colors.transparent to render-behind-navbar works.

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
        SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light
            .copyWith(systemNavigationBarColor: Color(0x00000000)));
        return MaterialApp(
            title: 'Material App',
            home: Scaffold(
              appBar: AppBar(
                title: Text('Material App Bar'),
              ),
              body: ListView.builder(
                  itemCount: 50,
                  itemBuilder: (context, index) => Container(
                      height: 100,
                      color: index % 2 == 0 ? Colors.white : Colors.grey.shade900)),
            ));
      }
    }
    
    `
  22. LasseRosenow commented on Jul 11, 2021

    @LasseRosenow
    Contributor

    @hectorAguero So I created a issue regarding your idea of making edge-to-edge the new default :)

    #86248

  23. NaikSoftware commented on Jul 28, 2021

    @NaikSoftware

    How to disable edge-to-edge now?)

  24. github-actions commented on Aug 11, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  25. locked as resolved and limited conversation to collaborators on Aug 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

a: layoutSystemChrome and Framework's Layout Issuesc: proposalA detailed proposal for a change to Flutterframeworkflutter/packages/flutter repository. See also f: labels.p: material_uimaterial_ui package in flutter/packages

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions