A @Parcelize navigation argument stops compiling after a field is added
A detail screen receives its model as a Safe Args navigation argument. It compiled and worked fine until someone added a createdAt field to the model. Now the build fails on the model class with: Type is not directly supported by 'Parcelize'.
The destination is unchanged, and Money is a class you own; LocalDate comes from the JDK and you cannot modify it.
@Parcelize
data class Order(
val id: Long,
val title: String,
val total: Money, // your own class, no annotation on it
val createdAt: LocalDate, // java.time — not yours to change
) : Parcelable
// nav_graph.xml declares: <argument android:name="order" app:argType="...Order" />
Find and fix the cause, without changing what the screen displays.
@Parcelize only writes types it knows how to put in a Parcel, and it knows neither Money nor LocalDate. Money is yours — mark it @Parcelize too. For LocalDate supply a TypeParceler that encodes it, or drop the field and pass the id.
- ✗Blaming Safe Args or the navigation graph rather than the unsupported property types
- ✗Assuming
@Parcelizefalls back toSerializableor to reflection for a type it does not know - ✗Forgetting that a type you do not own still has a fix — a
TypeParceler
- →When is passing the id and re-loading the object the better answer anyway?
- →What limits how large an object you should put into a navigation argument?
What happened
@Parcelize is a code generator, not reflection. It can write a fixed set of types into a Parcel: primitives, String, Parcelable, collections of those, and a few more it knows. When it meets a property whose type it has no writer for, it stops the build — this is a compile error, not a runtime crash.
Two types are unknown here: Money and LocalDate. They need different fixes, because one class is yours and the other is not.
// 1. Money is yours — annotate it as well
@Parcelize
data class Money(val amount: Long, val currency: String) : Parcelable
// 2. LocalDate is not yours — describe how to write and read it
object LocalDateParceler : Parceler<LocalDate> {
override fun create(parcel: Parcel): LocalDate = LocalDate.ofEpochDay(parcel.readLong())
override fun LocalDate.write(parcel: Parcel, flags: Int) = parcel.writeLong(toEpochDay())
}
@Parcelize
@TypeParceler<LocalDate, LocalDateParceler>
data class Order(
val id: Long,
val title: String,
val total: Money,
val createdAt: LocalDate,
) : Parcelable
The third route is not to carry the model through navigation at all: pass id: Long and let the screen load the Order itself. That is often the right answer — a navigation argument survives process death inside a Bundle, and keeping a whole object there is both expensive and brittle.