Search code examples
androidfloating-action-buttonandroid-coordinatorlayoutbottomnavigationviewmaterial-components-android

Arrange a BottomNavigationView, a FAB, and a FrameLayout in a CoordinatorLayout and make it work with Snackbar


I want to place a FrameLayout for hosting Fragments, a BottomNavigationView and a FAB inside a CoordinatorLayout. I would like to display a Snackbar over the BottomNavigationView and FAB to move up and down to accommodate the Snackbar. I have come up with the following layout but I am unable to get Snackbar and FAB behavior correctly.

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".landingpage.MainActivity">

<com.google.android.material.appbar.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.AppBarOverlay">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/content_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <FrameLayout
        android:id="@+id/fragment_cont"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_main_activity_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>

<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
    android:id="@+id/extended_fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_marginEnd="16dp"
    android:layout_marginBottom="72dp"
    android:contentDescription="@string/cont_desc"
    android:text="@string/chat"
    app:icon="@drawable/ic_baseline_chat_24" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

If I set LayoutParams like in this post then that margin 72dp margin is also displayed throwing FAB way up in the layout. If I don't use the margin the FAB will be stacked over the BottomNavigationView. I can use addCallback to set FAB margins programattically. Then also it will jump and then align itself to the right position. Overall it is not a good experience UX wise.


Solution

  • I would like to display a Snackbar over the BottomNavigationView and FAB to move up and down to accommodate the Snackbar. I have come up with the following layout but I am unable to get Snackbar and FAB behavior correctly.

    You get a bad behavior because you're trying to achieve something which is not common or maybe, not implemented this way by using BottomNavigationView and FAB together. It's actually more common to use FAB & BottomAppBar at the bottom of a layout.


    Then also it will jump and then align itself to the right position. Overall it is not a good experience UX wise.

    Although it's not a good experience for users from a UX view, however, here are the approaches I may suggest:

    Best approach : Using another CoordinatorLayout inside LinearLayout (Supported the FAB animation):

    enter image description here

    Code:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:title="Test" />
        </com.google.android.material.appbar.AppBarLayout>
    
        <androidx.core.widget.NestedScrollView
            android:id="@+id/content_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <FrameLayout
                android:id="@+id/fragment_cont"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
    
        </androidx.core.widget.NestedScrollView>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <androidx.coordinatorlayout.widget.CoordinatorLayout
                android:id="@+id/SnackBar"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1">
                <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
                    android:id="@+id/extended_fab"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="bottom|end"
                    android:layout_margin="16dp"
                    android:text="Chat"
                    android:textColor="@color/white"
                    app:icon="@drawable/ic_baseline_send_24"
                    app:iconTint="@color/white" />
    
            </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
            <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/bottom_navigation"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                app:layout_insetEdge="bottom"
                app:menu="@menu/escrow_menu" />
        </LinearLayout>
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    

    And in Kotlin side:

    extended_fab.setOnClickListener {
    
                val snack: Snackbar = Snackbar.make(SnackBar, " Successfully ...!", Snackbar.LENGTH_SHORT)
                snack.show()
            }
    

    2. BottomAppBar & FAB & setAnchorView() method without FAB animation

    • Anchored to FAB

    enter image description here

    Code:

    <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/coordinator"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">
    
        <androidx.core.widget.NestedScrollView
            android:id="@+id/content_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <!-- your FrameLayout maybe -->
    
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal|center"
                android:text="SHOW SnakeBar"
                app:layout_anchor="@+id/content_layout"
                app:layout_anchorGravity="center" />
    
    
        </androidx.core.widget.NestedScrollView>
    
        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:title="Test" />
        </com.google.android.material.appbar.AppBarLayout>
    
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="true"
            android:focusable="true"
            app:layout_anchor="@id/bar" />
    
        <com.google.android.material.bottomappbar.BottomAppBar
            android:id="@+id/bar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:layout_gravity="bottom"
            android:backgroundTint="@color/colorPrimaryDark">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
    
                <TextView
                    style="?android:attr/borderlessButtonStyle"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:background="?android:attr/selectableItemBackground"
                    android:drawableTop="@drawable/ic_baseline_send_24"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:text="Personal"
                    android:textColor="#FFFFFF">
    
                </TextView>
    
                <TextView
                    style="?android:attr/borderlessButtonStyle"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:background="?android:attr/selectableItemBackground"
                    android:drawableTop="@drawable/ic_baseline_send_24"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:text="Personal"
                    android:textColor="#FFFFFF">
    
                </TextView>
    
                <TextView
                    style="?android:attr/borderlessButtonStyle"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:background="?android:attr/selectableItemBackground"
                    android:drawableTop="@drawable/ic_baseline_send_24"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:textColor="#FFFFFF"
                    android:visibility="invisible">
    
                </TextView>
    
                <TextView
                    style="?android:attr/borderlessButtonStyle"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:background="?android:attr/selectableItemBackground"
                    android:drawableTop="@drawable/ic_baseline_send_24"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:text="Personal"
                    android:textColor="#FFFFFF">
    
                </TextView>
    
                <TextView
                    style="?android:attr/borderlessButtonStyle"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:background="?android:attr/selectableItemBackground"
                    android:drawableTop="@drawable/ic_baseline_send_24"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:text="Personal"
                    android:textColor="#FFFFFF">
    
                </TextView>
    
            </LinearLayout>
    
        </com.google.android.material.bottomappbar.BottomAppBar>
    
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    

    3. Using FAB - BottomNavigationView - ConstraintLayout without FAB animation

    enter image description here

    Code:

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/floating_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:layout_margin="16dp"
            app:backgroundTint="@color/colorPrimary"
            app:elevation="10dp"
            app:layout_constraintBottom_toTopOf="@id/navigation"
            app:layout_constraintLeft_toRightOf="@id/navigation"
            app:layout_constraintRight_toLeftOf="@id/navigation"
            app:layout_constraintTop_toBottomOf="@id/navigation"
            app:layout_constraintTop_toTopOf="@id/navigation"
            app:layout_insetEdge="bottom" />
    
        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/navigation"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@color/colorPrimaryDark"
            android:visibility="visible"
            app:itemIconTint="@color/white"
            app:itemTextColor="@color/white"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:menu="@menu/escrow_menu" />
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="290dp"
            android:layout_marginBottom="485dp"
            android:text="Button"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>