Question

Android FloatingActionButton Visibility and Restricted API Warnings in Kotlin

kotlinandroidandroid-9.0-pieandroid-jetpack

Question

When using com.google.android.material.floatingactionbutton.FloatingActionButton in a Kotlin Android app, assigning to visibility produces this warning:

VisibilityAwareImageButton.setVisibility can only be called from the same library group (groupId=com.google.android.material)
import android.view.View
import com.google.android.material.floatingactionbutton.FloatingActionButton

class MainActivity : AppCompatActivity() {
    lateinit var demoFab: FloatingActionButton

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        demoFab = findViewById(R.id.demoFab)
        demoFab.visibility = View.VISIBLE // Warning appears here
    }
}

Why does this happen, and what is the correct way to show or hide a Material FloatingActionButton?

Short Answer

You will learn what Android restricted-API warnings mean, why Kotlin property syntax can expose an internal setVisibility() override, and how to choose between FloatingActionButton.show()/hide() and normal View visibility values.

Concept

Android libraries can mark APIs as restricted with annotations such as @RestrictTo. This tells Android Studio's lint checks that an API is intended for use only by the library itself, not by application code.

A Material FloatingActionButton is built on internal implementation classes, including a visibility-aware image button. In older Material and Android Studio preview combinations, Kotlin code such as this:

demoFab.visibility = View.VISIBLE

is translated to a call to setVisibility(View.VISIBLE). The IDE may resolve that call to an overridden method declared on an internal Material class. Because that override is restricted, lint reports a warning even though changing a view's visibility is normally a public Android View operation.

The important distinction is:

  • View.VISIBLE, View.INVISIBLE, and View.GONE are valid public constants.
  • The warning is about the particular setVisibility() method lint believes Kotlin is calling.
  • FloatingActionButton provides public show() and hide() methods specifically for its visibility behavior and animations.

For a Material FAB, prefer its public behavior methods when the intent is to show or hide the button. This also lets the component manage its animation and internal state correctly.

Mental Model

Think of a FloatingActionButton as a car built from many internal parts.

  • View.visibility is like the normal dashboard control for whether a car is visible in a showroom.
  • show() and hide() are the manufacturer-supported controls for this particular model; they know how to perform the component's intended animation and state changes.
  • A restricted API warning is like seeing a label on an internal service panel: the part may exist and be callable, but it is not a supported control for outside users.

Kotlin's fab.visibility = ... syntax looks like setting a field, but it actually calls a setter method. That method selection is why an IDE warning can appear.

Take Quiz

Syntax and Examples

Kotlin property syntax for a Java-style getter or setter:

view.visibility = View.VISIBLE

is equivalent in principle to:

view.setVisibility(View.VISIBLE)

For an ordinary Android View, use the visibility constants:

import android.view.View

messageText.visibility = View.VISIBLE
loadingIndicator.visibility = View.INVISIBLE
emptyState.visibility = View.GONE

For a Material FloatingActionButton, use its component API when showing or hiding it:

import com.google.android.material.floatingactionbutton.FloatingActionButton

val fab: FloatingActionButton = findViewById(R.id.demoFab)

fab.show()
fab.hide()

show() makes the FAB visible using its Material behavior. hide() hides it using its Material behavior. These methods are clearer than changing the base View property when your goal is specifically to show or hide a FAB.

If you deliberately need ordinary View visibility semantics, such as INVISIBLE or GONE, work with a View-typed reference:

import android.view.View

val fabView: View = demoFab
fabView.visibility = View.GONE

This expresses that you are using the public View API. It should be used intentionally, because GONE and INVISIBLE do not mean exactly the same thing as a FAB's animated hide().

Step by Step Execution

Consider this code:

val fab: FloatingActionButton = findViewById(R.id.demoFab)

fab.hide()
fab.show()

Step by step:

  1. findViewById finds the view created from the layout resource with ID demoFab.
  2. The result is stored as a FloatingActionButton, so FAB-specific methods are available.
  3. fab.hide() requests that the Material component hide itself, normally using its configured hide animation.
  4. fab.show() requests that it become visible again, normally using its configured show animation.

Now compare it with direct View visibility:

val fabView: View = fab
fabView.visibility = View.GONE
  1. The same object is stored in a variable whose declared type is View.
  2. Kotlin translates the assignment into a call to the public View visibility setter.
  3. GONE means the view is hidden and should not occupy layout space.
  4. This does not communicate the higher-level intent to use the FAB's show/hide behavior.

Real World Use Cases

  • Scrolling screens: Hide a FAB while a user scrolls down to provide more content space, then show it when they scroll up. Material-aware behavior is useful here.
  • Form validation: Keep a submit FAB hidden until required fields are valid.
  • Loading states: Use View.GONE for a normal loading indicator when it should not take layout space after loading completes.
  • Permissions and roles: Show an “add” FAB only to users who have permission to create an item.
  • Empty states: Make an action FAB visible when a list has no items, so the user can create the first item.
  • Responsive layouts: Use INVISIBLE when a view should be hidden but its reserved position in a layout must remain stable.
Take Quiz

Real Codebase Usage

In production code, visibility is usually driven by state rather than scattered through click handlers.

For a FAB, a common pattern is a small function that expresses the UI decision:

private fun updateCreateButton(canCreate: Boolean) {
    if (canCreate) {
        demoFab.show()
    } else {
        demoFab.hide()
    }
}

For generic views, teams often use an extension function:

import android.view.View

fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

Be careful not to confuse this generic View.hide() extension with FloatingActionButton.hide(). If the variable is typed as FloatingActionButton, the FAB member function is the more appropriate choice.

For validation, use a guard-like state calculation:

val canSubmit = email.isNotBlank() && password.length >= 8

if (canSubmit) {
    submitFab.show()
} else {
    submitFab.hide()
}

In modern codebases, this decision may be made from observable UI state in a ViewModel, while the Activity or Fragment applies the result to the view.

Common Mistakes

Treating the warning as a reason to use a different VISIBLE constant

This does not solve the underlying issue:

// Do not search for a FloatingActionButton-specific VISIBLE constant.
demoFab.visibility = FloatingActionButton.VISIBLE

VISIBLE is a View visibility value. The warning concerns the setter method selected by lint, not the numeric constant. Use View.VISIBLE for generic views, or show() for a FAB.

Using hide() when the layout must keep its space

hide() is intended for the FAB's component behavior. If your UI requires a hidden view to retain its layout position, use the View.INVISIBLE semantic instead:

val fabView: View = demoFab
fabView.visibility = View.INVISIBLE

Assuming INVISIBLE and GONE are interchangeable

view.visibility = View.INVISIBLE // Hidden, but still takes up space
view.visibility = View.GONE      // Hidden, and does not take up space

Choose based on layout requirements.

Suppressing a warning without understanding it

A suppression can hide useful diagnostics and does not make an internal API stable. Prefer a documented public API such as show() or hide() first.

Forgetting to initialize the view after setting the layout

This ordering is incorrect:

val fab: FloatingActionButton = findViewById(R.id.demoFab)
setContentView(R.layout.activity_main)

Set the content view first, then call findViewById:

setContentView(R.layout.activity_main)
val fab: FloatingActionButton = findViewById(R.id.demoFab)

Comparisons

OptionBest useLayout spaceFAB animation/behavior
fab.show()Show a Material FABComponent-managedYes
fab.hide()Hide a Material FABComponent-managedYes
view.visibility = View.VISIBLEShow a generic view immediatelyTakes spaceNo special FAB behavior
view.visibility = View.INVISIBLEHide but preserve layout spacePreservedNo special FAB behavior
view.visibility = View.GONEHide and remove layout spaceRemovedNo special FAB behavior

show() and hide() are semantic component methods: they say what you want to happen to a FAB. VISIBLE, INVISIBLE, and GONE are low-level layout visibility states inherited from View.

Take Quiz

Cheat Sheet

// Generic Android View visibility
view.visibility = View.VISIBLE
view.visibility = View.INVISIBLE
view.visibility = View.GONE

// Material FloatingActionButton behavior
fab.show()
fab.hide()
  • VISIBLE: draw the view and reserve its layout space.
  • INVISIBLE: do not draw the view, but reserve its layout space.
  • GONE: do not draw the view and remove its layout space.
  • show() / hide(): preferred when the object is a Material FloatingActionButton and you want its intended visibility behavior.
  • Kotlin view.visibility = value calls a setter method behind the scenes.
  • A @RestrictTo warning means lint considers an API internal to a library; do not rely on that API directly.
  • If direct generic View semantics are truly needed, assign the FAB to a View reference before setting visibility.

FAQ

Why does fab.visibility = View.VISIBLE call a method?

Kotlin properties can map to Java getter and setter methods. This assignment is represented as a call to setVisibility(...).

Is View.VISIBLE the correct constant for Android views?

Yes. View.VISIBLE, View.INVISIBLE, and View.GONE are the standard public visibility constants.

Should I use FloatingActionButton.VISIBLE instead?

No. Changing the constant does not address a restricted setter warning. For a FAB, use show() when you want to display it.

What should I use to hide a Material FloatingActionButton?

Use fab.hide() when you want the FAB's normal component behavior. Use View.INVISIBLE or View.GONE only when you specifically need those generic layout semantics.

Is the restricted API warning always a runtime error?

No. It is generally an IDE/lint diagnostic, not necessarily a crash. However, it indicates that your code may depend on an API the library does not promise to support externally.

Can an Android Studio or library preview version cause an incorrect warning?

Yes. Preview versions can contain lint, metadata, or type-resolution issues. Updating Android Studio, the Android Gradle Plugin, and Material dependencies can resolve false-positive diagnostics. Even then, prefer the component's public API.

What is the difference between GONE and hide()?

GONE is a generic View layout state that removes layout space. hide() is a FAB-specific operation that can use the component's behavior and animation.

Take Quiz

Mini Project

Description

Build a small screen that shows a Material FloatingActionButton only when a user has entered text. The project demonstrates using show() and hide() for a FAB rather than changing its visibility through a restricted implementation path.

Goal

Create a text input and a FAB that appears when the input is not blank and hides when it is blank.

Requirements

Use a TextInputEditText or EditText for user input. Use a Material FloatingActionButton in the layout. Hide the FAB when the input is empty or contains only whitespace. Show the FAB when the user enters non-whitespace text. Display a short message when the FAB is clicked.

Take Quiz

Keep learning

Related questions

View more