Skip to content

Instantly share code, notes, and snippets.

@chrisbanes
Last active March 20, 2022 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisbanes/bd9aa241dbab971c23433aaff8104d38 to your computer and use it in GitHub Desktop.
Save chrisbanes/bd9aa241dbab971c23433aaff8104d38 to your computer and use it in GitHub Desktop.
FragmentInsetsComposeView
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.chrisbanes.accompanist.insets
import android.content.Context
import android.util.AttributeSet
import android.view.ViewGroup
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
/**
* A huge giant hack to help with https://github.com/chrisbanes/accompanist/issues/155.
*
* At the moment Compose will layout on the first [onMeasure] call. Now for fragments, they usually
* will not receive [WindowInsets] (via [dispatchApplyWindowInsets] until the
* second (or third) [onMeasure]. This creates the flicker.
*
* This class can be used in place of [ComposeView]. It ignores any [onMeasure] calls
* until we've received some insets. This is dangerous though, as a view may never receive any
* [WindowInsets] for valid reasons. To try and combat that, there's a limit to how many
* [onMeasure] calls can be ignored.
*/
class FragmentInsetsComposeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private var measureCalls = 0
private var applyInsetsRequested = false
@Suppress("DEPRECATION")
override fun requestFitSystemWindows() {
applyInsetsRequested = true
super.requestFitSystemWindows()
}
override fun dispatchApplyWindowInsets(insets: WindowInsets?): WindowInsets {
return super.dispatchApplyWindowInsets(insets).also {
applyInsetsRequested = false
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
if (measureCalls < MAXIMUM_ONMEASURE_IGNORED && applyInsetsRequested) {
setMeasuredDimension(
MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec)
)
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
measureCalls++
}
val composeView: ComposeView = ComposeView(context)
init {
addView(
composeView,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
/**
* Set the Jetpack Compose UI content for this view.
*
* @see [ComposeView.setContent].
*/
fun setContent(content: @Composable () -> Unit) = composeView.setContent(content)
companion object {
private const val MAXIMUM_ONMEASURE_IGNORED = 5
}
}
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.Providers
import androidx.fragment.app.Fragment
import dev.chrisbanes.accompanist.insets.FragmentInsetsComposeView
import dev.chrisbanes.accompanist.insets.LocalWindowInsets
import dev.chrisbanes.accompanist.insets.ViewWindowInsetObserver
class MyFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = FragmentInsetsComposeView(requireContext()).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
val windowInsets = ViewWindowInsetObserver(this).start(consumeWindowInsets = false)
setContent {
Providers(
LocalWindowInsets provides windowInsets
) {
// content
}
}
}
}
@southerton81
Copy link

Providers has been renamed to CompositionLocalProvider
https://developer.android.com/jetpack/androidx/releases/compose-runtime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment