My app crashes when I hit the button to update a field in the firestore
with spinner value.
The collection name is orders
The document name is dynamic, model.id
is used to get it.
The field name is order_status
New value that needs to be updated for the field order_status
is to be taken from the spinner.
The code works fine and the field get updated when I hardcode the value, but it crashes when I want to use the selected value of spinner
Following is the error in Logcat
kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at com.trad.ui.adapters.OrderStatusListAdapter.access$getBinding$p(OrderStatusListAdapter.kt:32)
at com.trad.ui.adapters.OrderStatusListAdapter$onBindViewHolder$1.onClick(OrderStatusListAdapter.kt:84)
at android.view.View.performClick(View.java:8160)
at android.widget.TextView.performClick(TextView.java:16222)
at android.view.View.performClickInternal(View.java:8137)
at android.view.View.access$3700(View.java:888)
at android.view.View$PerformClick.run(View.java:30236)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8512)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
OrdersByStatusFragment.kt
class OrdersByStatusFragment : BaseFragment() {
private lateinit var binding: OrderStatusLayoutBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_orders_by_status, container, false)
}
override fun onResume() {
super.onResume()
getOrderStatusList()
}
private fun getOrderStatusList() {
showProgressDialog(resources.getString(R.string.please_wait))
FirestoreClass().getOrderStatusList(this@OrdersByStatusFragment)
}
fun successOrderStatusList(orderStatusList: ArrayList<OrderStatus>) {
hideProgressDialog()
if (orderStatusList.size > 0) {
rv_order_by_status.visibility = View.VISIBLE
tv_no_orders_by_status_found.visibility = View.GONE
rv_order_by_status.layoutManager = LinearLayoutManager(activity)
rv_order_by_status.setHasFixedSize(true)
val orderStatusListAdapter =
OrderStatusListAdapter(requireActivity(), orderStatusList)
rv_order_by_status.adapter = orderStatusListAdapter
} else {
rv_order_by_status.visibility = View.GONE
tv_no_orders_by_status_found.visibility = View.VISIBLE
}
}
fun successNewOrderStatus() {
Toast.makeText(requireContext(), "Success", Toast.LENGTH_SHORT).show()
}
}
OrderStatusListAdapter.kt
open class OrderStatusListAdapter(
private val context: Context,
private var list: ArrayList<OrderStatus>,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private lateinit var binding: OrderStatusLayoutBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MyViewHolder(OrderStatusLayoutBinding.inflate(LayoutInflater.from(parent.context), parent,false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val model = list[position]
if (holder is MyViewHolder) {
GlideLoader(context).loadProductPicture(
model.image,
holder.itemView.iv_order_status_item_image
)
val dateFormat = "dd MMM yyyy HH:mm"
val formatter = SimpleDateFormat(dateFormat, Locale.getDefault())
val calendar: Calendar = Calendar.getInstance()
calendar.timeInMillis = model.order_datetime
val orderDateTime = formatter.format(calendar.time)
holder.itemView.tv_order_status_order_date.text = orderDateTime
holder.itemView.tv_order_status_item_name.text = model.items[0].title
holder.itemView.tv_order_status_item_price.text = "$${model.total_amount}"
holder.itemView.tv_order_status.text = model.order_status
holder.itemView.tv_order_status_order_id.text = model.id
holder.itemView.btn_order_status_change_status.setOnClickListener {
binding.spnOrderChangeStatus.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
Toast.makeText(
context,
"Selected value is ${
parent?.getItemAtPosition(position).toString()
}",
Toast.LENGTH_SHORT
).show()
val newstat = parent?.getItemAtPosition(position).toString()
FirestoreClass().updateOrderStatus(model.id, newstat)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
}
holder.itemView.ib_order_status_delete_product.visibility = View.GONE
holder.itemView.setOnClickListener {
val intent = Intent(context, SoldProductDetailsActivity::class.java)
intent.putExtra(Constants.EXTRA_SOLD_PRODUCT_DETAILS, model)
context.startActivity(intent)
}
}
}
override fun getItemCount(): Int {
return list.size
}
inner class MyViewHolder(private val binding : OrderStatusLayoutBinding) : RecyclerView.ViewHolder(binding.root)
}
FirestoreClass.kt
fun updateOrderStatus(
orderID: String,
newOrderStatus: String
) {
mFireStore.collection(Constants.ORDERS)
.document(orderID)
.update("order_status", newOrderStatus)
.addOnSuccessListener {
}
.addOnFailureListener {
}
}
EDIT
Following is the new error.
2021-06-09 01:24:17.550 2253-2253/com.tradaxis E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.tradaxis, PID: 2253
java.lang.NullPointerException: rv_order_by_status must not be null
at com.tradaxis.ui.fragments.OrdersByStatusFragment.successOrderStatusList(OrdersByStatusFragment.kt:76)
at com.tradaxis.firestore.FirestoreClass$getOrderStatusList$1.onSuccess(FirestoreClass.kt:935)
at com.tradaxis.firestore.FirestoreClass$getOrderStatusList$1.onSuccess(FirestoreClass.kt:22)
at com.google.android.gms.tasks.zzn.run(Unknown Source:4)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8512)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
rv_order_by_status.visibility = View.VISIBLE
in 'fun successOrderStatusList(orderStatusList: ArrayList)' is the code at OrdersByStatusFragment.kt:76
fragment.successOrderStatusList(list)
in FirestoreClass.kt is the code at FirestoreClass.kt:935
class FirestoreClass {
is the code at FirestoreClass.kt:22
NOTE: There was no such error for these lines of code before making these changes.
In your OrderStatusFragment OnCreateView you aren't setting the binding. You need to do something like this
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Instead of ResultProfileBinding you'd use your OrderStatusLayoutFragment. This is unrelated to firebase, but it should fix the crash you are currently having.
Read more: https://developer.android.com/topic/libraries/view-binding
Don't forget the ondestroy, to avoid memory leaks.