https://kotlinlang.org logo
#compose
Title
# compose
l

loloof64

12/15/2021, 12:09 PM
Reading at navigation documentation (https://developer.android.com/jetpack/compose/navigation#nav-with-args), I've seen that we can navigate with string argument. But is there a way to pass custom argument ? Or must I serialize/deserialize between my custom
class
and
String
? Or is even this last very bad practice ?
l

Leo Zam

12/15/2021, 1:20 PM
We just convert object to json and wrap to base64, i guess it’s only way
😳 1
l

loloof64

12/15/2021, 1:32 PM
Thank you 🙂 I am also thinking about handling the data directly in the main activity, where I define the
@Composable
: so to do state hoisting. I think i can handle that way also.
c

Colton Idle

12/15/2021, 3:01 PM
It's a bad practice to pass anything but strings. You should think of it like a web address. You want to give an id in the web address, not the full object serialized into a string. From the docs:
Caution: Passing complex data structures over arguments is considered an anti-pattern. Each destination should be responsible for loading UI data based on the minimum necessary information, such as item IDs. This simplifies process recreation and avoids potential data inconsistencies.
tldr. Just use identifiers. Bonus being you can retrieve stuff after process death. if you really want to "share" an object, just put the object in some shared scope by the two things that need it.
☝️ 1
👍🏾 1
i

Ian Lake

12/15/2021, 3:40 PM
Yep, identifiers make sense. Arguments shouldn't be the source of truth for data
🤩 1
👍🏾 1
That being said, Navigation supports passing custom types if your identifier is complicated (you absolutely shouldn't be base64ing anything) : https://developer.android.com/guide/navigation/navigation-kotlin-dsl#custom-types
2
l

loloof64

12/16/2021, 8:54 AM
@Colton Idle Thank you. I was not aware of this fact. In fact I need to pass a list of PGNGame, which is a custom type itself.
@Ian Lake Thank you. So I'll need to review Jetpack Compose concepts : as I'm still having some issue understanding the fact that argument should not be the source of truth for data. Also my PGNGame custom data is quite a rather complex data : hold a mutable map from String to String and a list of another custom data.
@Colton Idle I will also investigate into the sharing solution, from the top
Composable
in main activity
As my shared data is very very complex (with a recursive data), I went into sharing it with a view model. Not sure if this is fine. Also I'm afraid this could create Memory Leak
Is there a better way to share complex recursive data among NavHost components ?
c

Colton Idle

12/16/2021, 12:15 PM
I think its fine. yeah. lol.
👍🏾 1
you just gotta make sure you can get that data back if it goes away (process death)
l

loloof64

12/16/2021, 3:29 PM
No other way => so I'm gonna for it
👍 1
I'm sure I can always recover data : as in fact it is made from a Chess Pgn file format (either from assets, or from local files). So all I have to do is to parse the file again, and it's quite fast (using an ANTLR4 grammar). So yes, data can be easily recovered.
Finally came to make my custom data
@Parcelize
and to use this feature
Copy code
val games = extractGames(fileData = it, context = context)
     navController.currentBackStackEntry?.savedStateHandle?.set(
       NavHostParcelizeArgs.gamesList,
       games
     )
navController.navigate(NavHostRoutes.gamePage)
and
Copy code
val gamesList =
        navController.previousBackStackEntry?.savedStateHandle?.get<List<PGNGame>>(
            NavHostParcelizeArgs.gamesList
        )
and
Copy code
@Composable
fun MainContent(stockfishLib: StockfishLib) {
    val navController = rememberNavController()
    NavHost(navController, startDestination = NavHostRoutes.gamesListPage) {
        composable(NavHostRoutes.gamesListPage) {
            GamesListPage(
                navController = navController,
            )
        }
        composable(NavHostRoutes.gamePage) {
                GamePage(
                    navController = navController,
                    stockfishLib = stockfishLib,
                )
        }
    }
}
This way should be far cleaner.
i

Ian Lake

12/16/2021, 11:55 PM
No, you really, really don't want to be abusing savedStateHandle for that. This is way, way worse than your initial code of having a
rememberSaveable
above the NavHost layer acting as a "repository" that both destinations use as their source of truth
👍🏾 1
I'd suggest reading the Guide to App Architecture, which talks specifically about how to get data onto your screens: https://developer.android.com/jetpack/guide
👍 1
👍🏾 1
l

loloof64

12/23/2021, 10:12 AM
Hi eveyone ! I've started to design my app with Jetpack Architecture components. But then I'm stuck about how to use my UseCase so that all screens can make a call to it. More in the following posts
This is the use case.
This is the data source.
This is the repository.
But how to "share" the use case to all screens ? I mean, where should I instantiate it ? In the
@composable
which defines the
NavHost
? And how to pass it to all screens ? In short, where should I create the single instance of
GamesFromFileExtractorUseCase
? Otherwise, should the
GamesFromFileExtractorUseCase
be a Singleton / object class ?
This is the current state of my main activity.
j

Javier

12/23/2021, 10:54 AM
I wouldn't use a
object
+
lateinit var
if I could use a
class
+
@Singleton
+
val
👍🏾 1
l

loloof64

12/23/2021, 10:55 AM
Thank you. Could you give a simple snippet as an example ? Because I don't see yet how to apply your suggestion
Oups. Didn't realize that
@Singleton
is also a Dagger annotation. Thank you very much 🙂
Meanwhile, it seems that using
@Singleton
does not make it a Singleton : https://stackoverflow.com/a/44237507/662618
So maybe I should create it inside Application class instead : https://stackoverflow.com/a/57118576/662618
j

Javier

12/23/2021, 12:57 PM
depending on how you configure
@Singleton
, you should get the same instance always which is your use case
👍🏾 1
l

loloof64

12/23/2021, 2:29 PM
Thank you. So I'll try this way. Also the SO link I gave above was posted on 2019.
Here I'm calling
Copy code
(context.applicationContext as MyApplication).gamesFromFileExtractorUseCase.extractGame()
but I'm wondering whether it is better or worse than setting it inside NavHost state.
3 Views