Jetpack Compose : ViewTreeLifecycleOwner not found - android

I'm getting this error while using Compose in my fragment which works fine in case of XML
ViewTreeLifecycleOwner not found from androidx.fragment.app.FragmentContainerView
I'm using a single activity approach without using Jetpack Navigation component
Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nav)
supportFragmentManager.commit {
setReorderingAllowed(true)
add<InboxFragment>(R.id.nav_fragmentContainerView_appNav)
}
}
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/nav_fragmentContainerView_appNav"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Fragment:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
Text(text = "HELLO FRIEND!")
}
}
}
Dependencies:
def fragment_version = "1.3.3"
implementation("androidx.fragment:fragment-ktx:$fragment_version")
def compose_version = "1.0.0-beta06"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.activity:activity-compose:1.3.0-alpha07"
classpath "com.android.tools.build:gradle:7.0.0-alpha15"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30"

Since you are using an AppCompatActivity, only the appcompat 1.3 versions populate the ViewTreeLifecycleOwner.
Add:
implementation 'androidx.appcompat:appcompat:1.3.0'

None of these worked and there seems to be an open issue with BottomSheetDialog.
https://issuetracker.google.com/issues/261078350

Related

Incorrect work of the fragmentManager in conjunction with the Jetpack Compose

I added jetpack compose into my old project and I have noticed one problem with fragmentManager work.
In short, I have the following transactions:
MainActivity -> SettingsFragment -> BackgroundSettingsFragment.
When we go to BackgroundSettingsFragment and we press a back button, we return to SettingsFragment, but its content is empty. I debbuged SettingsFragment, after returning here, everything looks fine, but layout field is just empty.
I added breakpoints to different lifecycle events (onCreate, onCreateView, onViewCreated). Everything works great, I see that resources also is fine.
Without jetpack compose dependencies, there is no problem.
In details, I have the following code.
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion "$rootProject.kotlinVersion"
}
// Dependencies
// --- Jetpack Compose ---
implementation "androidx.compose.ui:ui:$compose_version"
// Integration with activities
implementation "androidx.activity:activity-compose:1.5.1"
// Integration with ViewModels
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
// Tooling support (Previews, etc.)
implementation "androidx.compose.ui:ui-tooling:$compose_version"
// Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
implementation 'androidx.compose.foundation:foundation:1.2.0'
// Material Design
implementation "androidx.compose.material:material:$compose_version"
// Material design icons
implementation "androidx.compose.material:material-icons-core:$compose_version"
implementation "androidx.compose.material:material-icons-extended:$compose_version"
// Integration with observables
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.runtime:runtime-rxjava2:$compose_version"
// Navigation
implementation "androidx.navigation:navigation-compose:2.5.1"
implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
MainActivity:
class MainActivity : RxAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layout)
redirectToSettingsFragment()
}
fun redirectToSettingsFragment() {
replaceFragmentSafely(SettingsFragment(), R.id.contentFrame)
}
}
SettingsFragment:
class SettingsFragment : RxFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupView()
}
fun setupView() {
label.setText(R.string.background_settings)
value.setText(R.string.color)
}
fun setupToolbar() {
(activity as MainActivity).toolbar.setOnBackButtonClickListener {
activity?.onBackPressed()
}
}
fun redirectToSecondFragment() {
replaceFragmentSafely(BackgroundSettingsFragment(), R.id.contentFrame, allowBackStack = true)
}
}
BackgroundSettingsFragment:
class BackgroundSettingsFragment : RxFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
}
fun setupToolbar() {
(activity as MainActivity).toolbar.setOnBackButtonClickListener {
activity?.onBackPressed()
}
}
}
fun AppCompatActivity.replaceFragmentSafely(fragment: Fragment,
#IdRes containerViewId: Int,
allowBackStack: Boolean = false) {
val fm = supportFragmentManager
.beginTransaction()
.replace(containerViewId, fragment, fragment.javaClass.toString())
if (allowBackStack) fm.addToBackStack(null)
try {
if (!supportFragmentManager.isStateSaved) {
fm.commit()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Here is the result.
What SettingsFragment looks like before we go to the BackgroundSettingsFragment:
What SettingsFragment looks like after we returned from the BackgroundSettingsFragment:
Please help, maybe someone already faced such a problem.

How to fix 'Cannot create an instance of class....'?

Here is my build.gradle
dependencies {
//viewmodel
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
//noinspection GradleDependency
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation 'androidx.activity:activity-ktx:1.4.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
}
viewModel
class AlarmViewModel(application: Application) : AndroidViewModel(application) {
private var repository : AlarmRepository = AlarmRepository(application)
var list : LiveData<List<Alarm>> = repository.list
}
Fragment
class AlarmListFragment : Fragment() {
private lateinit var viewModel: AlarmViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAlarmListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(activity!!.application)).get(AlarmViewModel::class.java)
}
}
}
Searching on google and trying to fix but it doesn't work
plz help me :<
I need ur helpe!!!

How to debug two RecyclerViews that suddenly stopped working?

I was working on an Android project that features two fragments on the main activity with one RecyclerView in each of them. It was looking good and functioning as intended, then somewhere along the way something broke and now neither is populating with anything. I'm not sure how to even begin to debug this, as everything looks correct to me. I will just focus on one of the RecyclerViews for the code below. The other one is imported from another project, so it has even less reason to break as I didn't code it! If you need any more information let me know as I am learning as I go here.
I have tried cleaning the project, rebuilding, restarting Android Studio in case it was a weird cache issue. I have combed over the code but nothing jumps out at me and Android Studio is not raising any errors. The project builds and loads but the Recycler Views are devoid of content.
build.gradle
dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation project(':crunchycalendar')
testImplementation 'junit:junit:4.13.2'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
debugImplementation 'androidx.fragment:fragment-testing:1.4.1'}
CardsFragment.kt
class CardsFragment : Fragment(R.layout.fragment_cards) {
var sampleDidits : Array<Didit> = arrayOf(
Didit("Walk 15 minutes around the block with your dog", "Grab the earbuds and get outside", R.drawable.footsteps),
Didit("Meditate 20 minutes", "This flavor text is too long man chill out", R.drawable.meditation),
Didit("Take your meds", "Withdrawal ain't fun", R.drawable.pill_bottle)
)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val binding = FragmentCardsBinding.inflate(layoutInflater, container, false)
// Add the following lines to create RecyclerView
val recyclerView = binding.cardsRecyclerView
recyclerView.setHasFixedSize(true);
val snapHelper: SnapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)
val layoutManager = LinearLayoutManager(this.context)
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
recyclerView.layoutManager = layoutManager;
recyclerView.adapter = CardViewAdapter(sampleDidits);
return binding.root
}
}
fragment_cards.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/cf_constraint"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/cardsRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
CardViewAdapter.kt
class CardViewAdapter(private val didit_tasks: Array<Didit>) :
RecyclerView.Adapter<CardViewAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cv = LayoutInflater.from(parent.context)
.inflate(R.layout.didit_card, parent, false) as CardView
return ViewHolder(cv)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// This is where data is added to the card view
val cardView = holder.cardView
val mainText = cardView.findViewById<View>(R.id.maintext) as TextView
val streakText = cardView.findViewById<View>(R.id.streak_text) as TextView
val flavorText = cardView.findViewById<View>(R.id.flavorText) as TextView
val diditIcon = cardView.findViewById<View>(R.id.didit_icon) as ImageView
val drawable = ContextCompat.getDrawable(cardView.context, didit_tasks[position].icon)
val streakString = didit_tasks[position].currentStreakString + " Day Streak"
diditIcon.setImageDrawable(drawable)
mainText.text = didit_tasks[position].mainText
flavorText.text = didit_tasks[position].flavorText
streakText.text = streakString
}
override fun getItemCount(): Int {
return didit_tasks.size
}
class ViewHolder(val cardView: CardView) : RecyclerView.ViewHolder(
cardView
)
Update:
It appears to be an issue not with my Recycler Views but with my Fragments. None of the fragments are loading up and I'm not sure why.
The issue seemed to be within my MainActivity.kt file. Under the onCreate function I had both setContentView(R.layout.activity_main)
AND
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
Removing the former line solved the issue so all fragments loaded properly

TextField breaks composing when used with Jetpack navigation?

I’ve been trying to put a simple app together, using Compose 1.0.0-beta09, Kotlin 1.5.10, and Jetpack Navigation 2.3.4.
The app has one activity and two fragments.
First (main) fragment /screen (clicking in the button takes me to the second fragment/screen ): Screen One screenshot
Second fragment:/screen: Screen Two screenshot
Problem: After interacting with (putting the cursor in) the TextField on the first screen and subsequently clicking on the button, the second screen loads but is empty (the onCreateView of the SecondFragment is called but the setContent doesn’t work / the screen doesn’t get recomposed?).
If I don’t interact with the TextField, the problem doesn’t happen.
I’ve tested on emulators with API levels 28 & 30, compose 1.0.0-beta0709, Kotlin 1.4.32 & 1.5.10 with similar results.
Empty Screen Two
Main classes:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AndroidViewBinding(ContentMainBinding::inflate)
}
}
}
class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = ComposeView(inflater.context).apply {
setContent {
FirstScreen( onButtonClick = {
findNavController().navigate(R.id.nav_second_fragment)
})
}
}
}
#Composable
fun FirstScreen(onButtonClick: () -> Unit) {
Column {
Text("Screen One", color = Color.Blue, fontSize = 30.sp)
Button(
onClick = {
onButtonClick() },
content = {
Text(text = "go to Screen Two", color = Color.White)
})
TextField(
value = "",
onValueChange = {},
label = { Text(stringResource(R.string.label_enter_value)) },
)
}
}
class SecondFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = ComposeView(inflater.context).apply {
setContent {
Column {
Text("Screen Two", color = Color.Blue, fontSize = 30.sp)
}
}
}
}
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdk 30
defaultConfig {
applicationId "com.example.composewithnavigation"
minSdk 28
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
compose true
viewBinding true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.5.10'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.ui:ui-viewbinding:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.3.0-beta01'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
}
As far as I know, fragments are discouraged in Compose for navigation. You are supposed to be creating multiple screens, like the FirstScreen Composable here. Anyway, I think the reason for the setContent not being called is that it is called from within the other setContent from your first fragment. Inside the setContent, you make the navigation call, which redirects it to the second fragment, which calls a setContent yet again. Because the second fragment will already be displayed inside the setContent of the FirstScreen Composable, your are actually nesting it, which might not be supported by compose , (I mean, makes sense).
That is why we recommend ditching the fragments and using Composables instead, which do not require explicitly calling setContent

findNavController is missing and not working

i was doing this https://youtu.be/GOpeBbfyb6s?t=1405 with navigation arch but i cant type it.findNavController it is showing red
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lgnbtn.setOnClickListener {
val nameBundle = Bundle()
nameBundle.putString("name",idfield.text.toString())
it.findNavController().navigate(R.id.mainFragment, nameBundle)
}
}
As per the Declaring Navigation dependencies, you must use the -ktx versions of the dependencies to use Kotlin extensions, such as the findNavController() extension for View.
Therefore, replace any dependencies on navigation-fragment with navigation-fragment-ktx and similarly for navigation-ui with navigation-ui-ktx.

Categories

Resources