material-components / material-components/material-components-android
[FloatingActionButton] Add ability to "flip" a FAB to a new state
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 17.4k
- Forks
- 3.2k
- PR merge metrics
- No merged PRs in 30d
Description
Is your feature request related to a problem? Please describe.
The Material Design 3 guidelines specify that a FAB can change state by fading out and then in with the new state, as shown:
This would be extremely helpful in my app, but there is no implementation of this in MDC,
Describe the solution you'd like
I want a method added to FloatingActionButton with a signature like this:
public void flip(FloatingActionButton.FlipCallback callback)
That would animate out the button, call the given FlipCallback to make any desired changes to the button, and then animate in the button. Moreover, the existing hide and show methods should be able to halt a flip that might be occurring.
This can change if that has too many edge cases (i.e changing FAB types mid-flip), however it must at least support changing the icon and content description.
Describe alternatives you've considered
I have managed to write this to approximate the behavior:
class FlipFloatingActionButton
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.floatingActionButtonStyle
) : FloatingActionButton(context, attrs, defStyleAttr) {
private var pendingConfig: PendingConfig? = null
private var flipping = false
override fun show() {
// Will already show eventually, need to do nothing.
if (flipping) return
// Apply the new configuration possibly set in flipTo. This should occur even if
// a flip was canceled by a hide.
pendingConfig?.run {
setImageResource(iconRes)
contentDescription = context.getString(contentDescriptionRes)
setOnClickListener(clickListener)
}
pendingConfig = null
super.show()
}
override fun hide() {
// Not flipping anymore, disable the flag so that the FAB is not re-shown.
flipping = false
// Don't pass any kind of listener so that future flip operations will not be able
// to show the FAB again.
super.hide()
}
fun flipTo(
@DrawableRes iconRes: Int,
@StringRes contentDescriptionRes: Int,
clickListener: OnClickListener
) {
// Dirty workaround so that we don't do a flip if the given config is already being applied
if (tag == iconRes) return
tag = iconRes
flipping = true
pendingConfig = PendingConfig(iconRes, contentDescriptionRes, clickListener)
// We will re-show the FAB later, assuming that there was not a prior flip operation.
super.hide(FlipVisibilityListener())
}
private data class PendingConfig(
@DrawableRes val iconRes: Int,
@StringRes val contentDescriptionRes: Int,
val clickListener: OnClickListener
)
private inner class FlipVisibilityListener : OnVisibilityChangedListener() {
override fun onHidden(fab: FloatingActionButton) {
if (!flipping) return
flipping = false
show()
}
}
}
But I'm unsure if this will actually work properly given that the state management is very complicated and fragile. I'd imagine that a native solution would be much more reliable, as it would be developed with much more intimate state knowledge.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the FloatingActionButton entry point and read its existing hide and show behavior, including visibility callbacks and state handling. Define the supported flip behavior around changing the icon and content description, and verify that hide or show can interrupt it without leaving the button in an inconsistent state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, java
- Domain
- mobile
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100