PPB - Pertemuan 14 - News App

 5025231245 | Rafie Zaidan Umara | Pertemuan 14 | PPB C


CRUD Registrasi Mahasiswa Room Database

News App merupakan aplikasi Android yang dirancang untuk menampilkan berita terkini secara real-time dengan memanfaatkan layanan API dari NewsAPI. Aplikasi ini menerapkan arsitektur MVVM (Model-View-ViewModel) serta menggunakan Retrofit untuk mengambil data dari internet dan Jetpack Compose untuk membangun antarmuka pengguna modern. Pengguna dapat melihat daftar berita, melakukan pencarian berita, membaca detail berita, serta mengakses sumber berita secara langsung melalui browser.

  


1. MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
NewsAppTheme {
val viewModel: NewsViewModel = viewModel()
val selectedArticle by viewModel.selectedArticle.collectAsState()

if (selectedArticle == null) {
HomeScreen(
viewModel = viewModel,
onArticleClick = { article ->
viewModel.selectArticle(article)
}
)
} else {
DetailScreen(
article = selectedArticle!!,
onBackClick = {
viewModel.selectArticle(null) // Reset selection to go back
}
)
}
}
}
}
}
Kode pada MainActivity berfungsi sebagai titik awal aplikasi ketika dijalankan. Pada bagian ini tema aplikasi diterapkan menggunakan NewsAppTheme, kemudian dibuat objek NewsViewModel untuk mengelola data berita. Activity juga bertugas mengatur tampilan yang muncul, yaitu menampilkan halaman Home ketika belum ada berita yang dipilih dan menampilkan halaman Detail ketika pengguna memilih salah satu berita.

2. Article.kt
data class Article(
@SerializedName("source") val source: Source?,
@SerializedName("author") val author: String?,
@SerializedName("title") val title: String?,
@SerializedName("description") val description: String?,
@SerializedName("url") val url: String,
@SerializedName("urlToImage") val urlToImage: String?,
@SerializedName("publishedAt") val publishedAt: String?,
@SerializedName("content") val content: String?
)
File Article.kt digunakan untuk mendefinisikan struktur data berita yang diterima dari API. Setiap berita memiliki informasi seperti judul, penulis, gambar, tanggal publikasi, deskripsi, dan isi berita. Data class ini memudahkan proses konversi data JSON dari API menjadi objek Kotlin yang dapat digunakan oleh aplikasi.

3. NewsResponse.kt
data class NewsResponse(
@SerializedName("status") val status: String?,
@SerializedName("totalResults") val totalResults: Int?,
@SerializedName("articles") val articles: List<Article>?
)
Class NewsResponse digunakan sebagai wadah utama data yang diterima dari NewsAPI. Objek ini berisi status permintaan, jumlah berita yang ditemukan, dan daftar berita yang kemudian akan ditampilkan pada halaman utama aplikasi.

4. ApiService.kt
interface ApiService {
@GET("v2/top-headlines")
suspend fun getTopHeadlines(
@Query("country") country: String = "us",
@Query("q") query: String? = null,
@Query("apiKey") apiKey: String
): NewsResponse
}
File ApiService.kt digunakan untuk mendefinisikan endpoint API yang akan diakses menggunakan Retrofit. Pada proyek ini digunakan endpoint top-headlines dari NewsAPI untuk mengambil berita terbaru berdasarkan negara atau kata kunci pencarian yang dimasukkan pengguna.

5. RetroClient.kt
object RetrofitClient {
private const val BASE_URL = "https://newsapi.org/"

// REPLACE WITH YOUR ACTUAL NEWSAPI KEY
const val API_KEY = "YOUR_API_KEY"
val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
File RetrofitClient.kt berfungsi untuk menginisialisasi Retrofit yang digunakan sebagai penghubung antara aplikasi dengan API berita. Seluruh konfigurasi seperti base URL dan converter diletakkan pada file ini sehingga dapat digunakan kembali di berbagai bagian aplikasi.

6. NewsRepository.kt
class NewsRepository(private val apiService: ApiService = RetrofitClient.apiService) {
suspend fun getTopHeadlines(query: String? = null): List<Article> {
return withContext(Dispatchers.IO) {
val response = apiService.getTopHeadlines(
country = "us",
query = query,
apiKey = RetrofitClient.API_KEY
)
response.articles ?: emptyList()
}
}
}
Repository berfungsi sebagai lapisan penghubung antara ViewModel dan sumber data. Dengan adanya repository, proses pengambilan data berita menjadi lebih terstruktur karena seluruh akses API dipusatkan pada satu tempat sehingga lebih mudah dikelola dan dikembangkan.

7. NewsUiState.kt
sealed interface NewsUiState {
data object Loading : NewsUiState
data class Success(val articles: List<Article>) : NewsUiState
data class Error(val message: String) : NewsUiState
data object Empty : NewsUiState
}
File NewsUiState.kt digunakan untuk mengatur kondisi tampilan aplikasi. State yang tersedia meliputi Loading saat data sedang dimuat, Success ketika data berhasil diperoleh, Error ketika terjadi kesalahan, dan Empty ketika tidak ditemukan berita yang sesuai.

8. NewsViewModel.kt
class NewsViewModel(private val repository: NewsRepository = NewsRepository()) : ViewModel() {
private val _uiState = MutableStateFlow<NewsUiState>(NewsUiState.Loading)
val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
private val _searchQuery = MutableStateFlow("")
val searchQuery: StateFlow<String> = _searchQuery.asStateFlow()
private val _isRefreshing = MutableStateFlow(false)
val isRefreshing: StateFlow<Boolean> = _isRefreshing.asStateFlow()
private val _errorEvent = MutableSharedFlow<String>()
val errorEvent: SharedFlow<String> = _errorEvent.asSharedFlow()
private var allArticles = listOf<Article>()

// Store selected article for detail view
private val _selectedArticle = MutableStateFlow<Article?>(null)
val selectedArticle: StateFlow<Article?> = _selectedArticle.asStateFlow()

init {
fetchNews()
}

fun fetchNews(isRefresh: Boolean = false) {
viewModelScope.launch {
if (isRefresh) {
_isRefreshing.value = true
} else {
_uiState.value = NewsUiState.Loading
}
try {
// Fetch news from repository
val articles = repository.getTopHeadlines()
allArticles = articles
filterArticles()
} catch (e: Exception) {
val errorMessage = e.localizedMessage ?: "Failed to load news"
if (isRefresh) {
_errorEvent.emit(errorMessage)
} else {
_uiState.value = NewsUiState.Error(errorMessage)
}
} finally {
_isRefreshing.value = false
}
}
}

fun onSearchQueryChanged(query: String) {
_searchQuery.value = query
filterArticles()
}

private fun filterArticles() {
val query = _searchQuery.value.trim()
if (allArticles.isEmpty() && _uiState.value !is NewsUiState.Loading) {
_uiState.value = NewsUiState.Empty
return
}
val filtered = if (query.isEmpty()) {
allArticles
} else {
allArticles.filter {
it.title?.contains(query, ignoreCase = true) == true ||
it.description?.contains(query, ignoreCase = true) == true ||
it.author?.contains(query, ignoreCase = true) == true
}
}
if (filtered.isEmpty() && allArticles.isNotEmpty()) {
_uiState.value = NewsUiState.Empty
} else if (filtered.isNotEmpty()) {
_uiState.value = NewsUiState.Success(filtered)
} else {
_uiState.value = NewsUiState.Empty
}
}

fun selectArticle(article: Article) {
_selectedArticle.value = article
}
}
File NewsViewModel.kt merupakan pusat logika aplikasi yang bertugas mengelola data berita. ViewModel mengambil data dari repository kemudian mengubah state tampilan sesuai kondisi yang terjadi. Selain itu ViewModel juga menangani fitur pencarian berita, refresh data, dan pemilihan berita yang akan ditampilkan pada halaman detail.

9. NewsCard.kt
fun NewsCard(
article: Article,
onClick: () -> Unit,
modifier: Modifier = Modifier
)
Komponen NewsCard digunakan untuk menampilkan ringkasan berita pada halaman utama. Setiap card berisi gambar berita, judul berita, nama sumber, dan tanggal publikasi. Ketika card ditekan, pengguna akan diarahkan menuju halaman detail berita.

10. HomeScreen.kt
fun HomeScreen(
viewModel: NewsViewModel,
onArticleClick: (Article) -> Unit,
modifier: Modifier = Modifier12.
)
Halaman Home merupakan halaman utama aplikasi yang menampilkan daftar berita terkini. Pada halaman ini tersedia fitur pencarian berita, indikator loading, pesan error, fitur refresh, serta daftar berita yang ditampilkan menggunakan LazyColumn. Halaman ini menjadi pusat interaksi utama pengguna dengan aplikasi.

11. DetailScreen.kt
@Composable
fun DetailScreen(
article: Article,
onBackClick: () -> Unit,
modifier: Modifier = Modifier
)
Halaman Detail digunakan untuk menampilkan informasi berita secara lengkap. Pengguna dapat melihat gambar berukuran besar, judul berita, nama penulis, deskripsi, isi berita, serta tombol untuk membuka artikel asli melalui browser. Halaman ini memberikan pengalaman membaca yang lebih nyaman dibandingkan tampilan ringkasan pada halaman utama.

12. AppNavGraph.kt
@Composable
fun AppNavGraph(
viewModel: NewsViewModel,
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController()
)
File AppNavGraph.kt digunakan untuk mengatur navigasi antar halaman dalam aplikasi. Melalui Navigation Compose, aplikasi dapat berpindah dari halaman Home ke halaman Detail dan kembali lagi tanpa perlu membuat Activity baru sehingga performa aplikasi menjadi lebih baik dan struktur kode lebih rapi.

13. Kesimpulan
Berdasarkan hasil implementasi yang telah dilakukan, aplikasi News App berhasil dikembangkan menggunakan Kotlin, Jetpack Compose, Retrofit, dan arsitektur MVVM. Aplikasi mampu mengambil data berita secara real-time dari NewsAPI dan menampilkannya dalam antarmuka yang modern serta responsif. Selain itu, penerapan ViewModel, Repository, dan StateFlow membuat pengelolaan data menjadi lebih terstruktur dan mudah dipelihara. Melalui proyek ini, dapat dipahami penerapan konsep konsumsi REST API, manajemen state, navigasi antar halaman, serta pengembangan aplikasi Android modern menggunakan Jetpack Compose.

Comments

Popular posts from this blog

Tugas Eksplorasi Perkembangan Teknologi Rafie Zaidan Umara 5025231245

PPB - Pertemuan 10 dan 11 - Marketplace Siswa

Tugas 4 PBO - Clock Display