Skip to main content

Overview

The article details feature displays comprehensive information about a selected news article, including title, authors, publication date, summary, and a full-size image. Users can read the full article by opening it in a web browser.

Architecture

The detail screen follows MVVM architecture:
  • ArticleDetailFragment: UI layer for displaying article details
  • ArticleDetailsViewModel: Manages article data and loading state
  • GetArticleByIdUseCase: Fetches article details by ID
  • ArticleDetail: Domain model with complete article information

Fragment Implementation

The ArticleDetailFragment receives article information through Safe Args navigation.

Receiving Navigation Arguments

ArticleDetailFragment.kt:32-43
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    arguments?.let {
        articleId = it.getLong("articleId", -1L)
        titleToolbar = it.getString("newsSite")
    }

    if (articleId == -1L && titleToolbar == null) {
        return
    }
}
The fragment receives articleId and newsSite from the navigation arguments, with validation to ensure valid data.

View Setup

ArticleDetailFragment.kt:53-60
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (requireActivity() as AppCompatActivity).supportActionBar?.title = titleToolbar

    setupToolbar()
    fetchArticle()
    observeArticle()
}

Data Fetching

ArticleDetailFragment.kt:68-70
private fun fetchArticle() {
    viewModel.fetchArticleById(articleId, reload = true)
}

Observing Article Data

ArticleDetailFragment.kt:73-77
private fun observeArticle() {
    viewModel.article.observe(viewLifecycleOwner) { resource ->
        handleResource(resource)
    }
}

State Management

The fragment handles three states using the Resource sealed class.
ArticleDetailFragment.kt:80-86
private fun handleResource(resource: Resource<ArticleDetail>) {
    when (resource) {
        is Resource.Error -> showError()
        is Resource.Loading -> showLoading()
        is Resource.Success -> loadData(resource.data)
    }
}

UI Data Binding

When article data is successfully loaded, it’s displayed in the UI.
ArticleDetailFragment.kt:89-115
private fun loadData(article: ArticleDetail?) {
    article?.let {
        with(binding.contentCard) {
            tvArticleTitle.text = it.title
            tvDate.text = getString(R.string.published_at, it.publishedAt.toFormattedDate())
            tvAuthors.text = getString(
                R.string.authors,
                if (it.authors.isEmpty()) {
                    "No author"
                } else {
                    it.authors.joinToString(", ") { author -> author.name }
                }
            )
            articleContent.text = it.summary



            btnContinueReading.setOnClickListener {
                openWebPage(article.url)
            }
        }

        binding.headerImage.toNetworkGlide(requireContext(), it.imageUrl)
    }

    hideLoading()
}
The UI displays:
  • Article title
  • Publication date (formatted)
  • Authors (comma-separated, or “No author”)
  • Article summary
  • Header image loaded from network
  • Button to continue reading in browser

ViewModel Implementation

The ArticleDetailsViewModel manages the article detail state.

Properties

ArticleDetailsViewModel.kt:18-21
private var hasLoaded = false

private val _article = MutableLiveData<Resource<ArticleDetail>>()
val article: LiveData<Resource<ArticleDetail>> = _article

Fetching Article by ID

ArticleDetailsViewModel.kt:28-36
fun fetchArticleById(id: Long, reload: Boolean = false) {
    if (hasLoaded && !reload) return
    _article.value = Resource.Loading()

    viewModelScope.launch {
        _article.value = articleById.getArticleById(id)
        hasLoaded = true
    }
}
The hasLoaded flag prevents unnecessary re-fetching when the screen is recreated (e.g., on configuration changes), unless explicitly reloaded.

Domain Model

The ArticleDetail model contains comprehensive article information.
ArticleDetail.kt:3-13
data class ArticleDetail(
    val id: Long,
    val title: String,
    val authors: List<Author>,
    val url: String,
    val newsSite: String,
    val imageUrl: String,
    val summary: String,
    val publishedAt: String,
    val updatedAt: String
)
The “Continue Reading” button opens the full article in a web browser.
ArticleDetailFragment.kt:147-154
private fun openWebPage(url: String) {
    val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
    if (intent.resolveActivity(requireContext().packageManager) != null) {
        startActivity(intent)
    } else {
        showToast("No se encontró un navegador web")
    }
}
The app checks if a browser is available before attempting to open the URL. If no browser is found, it shows a toast message.

Error Handling

If loading fails, the app displays an error screen with a retry button.
ArticleDetailFragment.kt:135-144
private fun showError() {
    binding.showState(showError = true)
    binding.contentInformation.apply {
        ivInformation.toResourceGlide(requireContext(), R.drawable.error)

        contentError.btnRetry.setOnClickListener {
            fetchArticle()
        }
    }
}

Loading State

During data fetching, a loading indicator is displayed.
ArticleDetailFragment.kt:123-132
private fun showLoading() {
    binding.showState(showLoading = true)

    binding.contentLoading.apply {
        ivLoading.toResourceGlide(
            requireContext(),
            R.drawable.astronaut_with_space_shuttle_loader
        )
    }
}

Use Case Implementation

The feature uses a use case to fetch article details from the repository.
viewModelScope.launch {
    _article.value = articleById.getArticleById(id)
    hasLoaded = true
}
The detail screen is accessed from the article list via Safe Args.
ArticlesFragment.kt:168-175
private fun navigateArticlesToArticleDetail(article: Article) {
    findNavController().navigate(
        ArticlesFragmentDirections.actionArticlesFragmentToArticleDetailFragment(
            articleId = article.id,
            newsSite = article.newsSite
        )
    )
}

Article List

Browse all space flight news articles

Offline Mode

Access article details without internet

Build docs developers (and LLMs) love