Question
Android FloatingActionButton Visibility and Restricted API Warnings in Kotlin
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, andView.GONEare valid public constants.- The warning is about the particular
setVisibility()method lint believes Kotlin is calling. FloatingActionButtonprovides publicshow()andhide()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.visibilityis like the normal dashboard control for whether a car is visible in a showroom.show()andhide()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.
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:
findViewByIdfinds the view created from the layout resource with IDdemoFab.- The result is stored as a
FloatingActionButton, so FAB-specific methods are available. fab.hide()requests that the Material component hide itself, normally using its configured hide animation.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
- The same object is stored in a variable whose declared type is
View. - Kotlin translates the assignment into a call to the public
Viewvisibility setter. GONEmeans the view is hidden and should not occupy layout space.- 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.GONEfor 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
INVISIBLEwhen a view should be hidden but its reserved position in a layout must remain stable.
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
| Option | Best use | Layout space | FAB animation/behavior |
|---|---|---|---|
fab.show() | Show a Material FAB | Component-managed | Yes |
fab.hide() | Hide a Material FAB | Component-managed | Yes |
view.visibility = View.VISIBLE | Show a generic view immediately | Takes space | No special FAB behavior |
view.visibility = View.INVISIBLE | Hide but preserve layout space | Preserved | No special FAB behavior |
view.visibility = View.GONE | Hide and remove layout space | Removed | No 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.
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 MaterialFloatingActionButtonand you want its intended visibility behavior.- Kotlin
view.visibility = valuecalls a setter method behind the scenes. - A
@RestrictTowarning means lint considers an API internal to a library; do not rely on that API directly. - If direct generic
Viewsemantics are truly needed, assign the FAB to aViewreference before settingvisibility.
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.
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.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.