I'm building a project using ViewModel
, Compose and Hilt. I can't switch to BookListScreen
because I am getting the error in the title. Points that I think are important:
onItemClick = { navController.navigate(Screen.BookListScreen.route) }
in MainScreen.composable(route = Screen.BookListScreen.route) { BookListScreen(navController) }
in MainActivity
.
- And
@AndroidEntryPoint
annotation. I've added this toMainActivity
but do I need to add it elsewhere?
Main Activity:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LOTRTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Screen.MainScreen.route
) {
composable(
route = Screen.MainScreen.route
) {
MainScreen(navController)
}
composable(
route = Screen.BookListScreen.route
) {
BookListScreen(navController)
}
}
}
}
}
}
}
Main Screen:
@Composable
fun MainScreen(navController: NavController) {
val configuration = LocalConfiguration.current
when (configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
SectionsScreenRotate(navController)
}
else -> {
SectionsScreen(navController)
}
}
}
@Composable
fun SectionsScreen(navController: NavController) {
Column(
modifier = Modifier.padding(10.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(10.dp)
) {
SectionItem(
section = Section("Characters", R.drawable.charachtersdraw),
onItemClick = {}
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.padding(10.dp)
) {
SectionItem(
section = Section("Movies", R.drawable.moviesdraw),
onItemClick = {}
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.padding(10.dp)
) {
SectionItem(
section = Section("Books", R.drawable.booksdraw),
onItemClick = { navController.navigate(Screen.BookListScreen.route) }
)
}
}
}
@Composable
fun SectionsScreenRotate(navController: NavController) {
Row(
modifier = Modifier.padding(10.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(10.dp)
) {
SectionItem(
section = Section("Characters", R.drawable.charachtersdraw),
onItemClick = {}
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.padding(10.dp)
) {
SectionItem(
section = Section("Movies", R.drawable.moviesdraw),
onItemClick = { navController.navigate(Screen.BookListScreen.route)}
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.padding(10.dp)
) {
SectionItem(
section = Section("Books", R.drawable.booksdraw),
onItemClick = {}
)
}
}
}
@Composable
fun SectionItem(section: Section, onItemClick: () -> Unit) {
Card(
modifier = Modifier
.fillMaxSize()
.clickable { onItemClick() },
shape = RoundedCornerShape(15.dp),
elevation = 5.dp,
) {
Box(modifier = Modifier.fillMaxSize()) {
Image(
painter = painterResource(id = section.imageId),
contentDescription = section.title,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
contentAlignment = Alignment.BottomCenter
) {
Text(
section.title, style = TextStyle(
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
)
)
}
}
}
}
BookListScreen:
@Composable
fun BookListScreen(
navController: NavController,
bookListViewModel: BookListViewModel = viewModel()
) {
val state = bookListViewModel.state.value
Box(modifier = Modifier.fillMaxSize()) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(state.books) { book ->
BookListItem(
book = book
)
}
}
if (state.error.isNotBlank()) {
Text(
text = state.error,
color = MaterialTheme.colors.error,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.align(Alignment.Center)
)
}
if (state.isLoading) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
}
ViewModel:
@HiltViewModel
class BookListViewModel @Inject constructor(
private val bookRepository: BookRepository
) : ViewModel(){
val state = mutableStateOf(BookListState())
init {
getBooks()
}
private fun getBooks() {
bookRepository.getBooks().onEach { result ->
when(result) {
is Resource.Success -> {
state.value = BookListState(books = result.data ?: emptyList())
}
is Resource.Error -> {
state.value = BookListState(error = result.message ?: "Beklenmedik bir hata oluştu")
}
is Resource.Loading -> {
state.value = BookListState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
}
AppModule:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun getLotrApi() : LotrApi {
return Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(LotrApi::class.java)
}
@Provides
@Singleton
fun getBookRepository(api: LotrApi) : BookRepository {
return BookRepository(api)
}
}
BookRepository:
class BookRepository @Inject constructor(
private val api : LotrApi
){
fun getBooks() : Flow<Resource<List<Book>>> = flow {
try {
emit(Resource.Loading<List<Book>>())
val books = api.getBookList().BookListDtoToBookList()
emit(Resource.Success<List<Book>>(books))
}
catch (e: HttpException) {
emit(Resource.Error<List<Book>>(e.localizedMessage ?: "Beklenmedik hata"))
}
catch(e: IOException) {
emit(Resource.Error<List<Book>>(e.localizedMessage ?: "Lütfen internet bağlantınızı kontrol edin"))
}
}
}
Screen:
sealed class Screen(val route : String){
object BookListScreen : Screen("book_list")
object BookChaptersScreen : Screen("book_chapters")
object MainScreen : Screen("main_screen")
}
CodePudding user response:
While using Hilt along with Navigation Library, you need to use hiltViewModel()
instead of viewModel()
to inject ViewModels. Check out documentation for more info.