I am building an android app where I add products in the database in the Detailfragment and show them in the main fragment: This is the Product class:
@Parcelize
@Entity(tableName = "product_table")
data class Product (
@ColumnInfo(name = "name")
var name:String = "product",
):Parcelable{@PrimaryKey(autoGenerate = true)
var id:Long = 0L}
This is the ProductViewModel Iam using
class ProductViewModel(val database: ProductDatabaseDao) : ViewModel() {
val products = database.getAllProducts()
val productsString = products.value?.get(0)?.name
private var _navigateToProductsList = MutableLiveData<Boolean>()
val navigateToProductsList : LiveData<Boolean>
get() = _navigateToProductsList
private suspend fun insert(product: Product) {
database.insert(product)
}
fun onAddProduct(){
viewModelScope.launch {
val product = Product()
product.name = "toy"
insert(product)
}
_navigateToProductsList.value = true
}
fun doneNavigation(){
_navigateToProductsList.value = false
}
}
This is the ProductDetailFragment:
class ProductDetailFragment : Fragment() {
private val productViewModel: ProductViewModel by activityViewModels {
ProductViewModelFactory(
getDatabase()
)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val binding = DataBindingUtil.inflate<FragmentProductDetailBinding>(
inflater,
R.layout.fragment_product_detail,
container,
false
)
binding.lifecycleOwner = this
binding.productViewModel = productViewModel
productViewModel.navigateToProductsList.observe(viewLifecycleOwner, Observer {
if (it == true) {
this.findNavController()
.navigate(ProductDetailFragmentDirections.actionProductDetailFragmentToProductsListFragment())
// Reset state to make sure we only navigate once, even if the device
// has a configuration change
productViewModel.doneNavigation()
}
})
return binding.root
}
fun getDatabase(): ProductDatabaseDao {
val application = requireNotNull(this.activity).application
val dataSource = ProductDatabase.getInstance(application).productDatabaseDao
return dataSource
}
}
and this is product_detail_fragment
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
<data>
<variable
name="productViewModel"
type="com.github.mariemmezghani.inventory.viewModel.ProductViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/product_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:background="@drawable/round_button"
android:text="@string/save_button"
android:textColor="@color/white"
android:onClick="@{()->productViewModel.onAddProduct()}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/product_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Here I am calling the productViewmodel.onAddProduct() in the save_Button
And this is the products_list_fragment
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<!-- Data to make available to the XML via data binding. In this case,
the whole ViewModel, so that we can access the LiveData,
click handlers, and state variables. -->
<data>
<variable
name="viewModel"
type="com.github.mariemmezghani.inventory.viewModel.ProductViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ProductsListFragment">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:clickable="true"
app:backgroundTint="@color/color_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@android:drawable/ic_input_add" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:text="@{viewModel.productsString}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Before implementing the recyclerView to display the products added by the user, I wanted to test that the products are added correctly to the database, so I just added a product and gave it a name "toy" and tried to display it in the TextView of this product_list_fragment.
I can not figure out why this did not work and nothing is displayed in the screen when I press the save_Button
Thank you for your help
I found the solution to my question.
The problem is that products.value?
( type LiveData<List>) is returning null from Room. I managed to fix the problem by using Transformations.map.
I changed this:
val productsString = products.value?.get(0)?.name
to this:
val productsString = Transformations.map(products){products -> products.get(0).name}