0% found this document useful (0 votes)
10 views18 pages

MVVM Navigation with Jetpack Compose

Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views18 pages

MVVM Navigation with Jetpack Compose

Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

MVVM + Jetpack Compose Navigation

FOCS_Book

└── app
├── manifests
│ └── [Link]

├── kotlin+java
│ └── [Link].focs_book
│ ├── data
│ │ └── ChapterRepository
│ │
│ ├── model
│ │ └── Chapter
│ │
│ ├── navigation
│ │ └── NavRoutes
│ │
│ └── ui
│ ├── screens
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ └── [Link]
│ │
│ └── theme

│ └── viewmodel
│ ├── Chapter
│ └── [Link]

├── [Link].focs_book (androidTest)
├── [Link].focs_book (test)

├── assets
│ ├── book_content.json (androidTest)
│ └── [Link] (androidTest)

├── res
└── res (generated)

└── Gradle Scripts
├── [Link] (Project: FOCS_Book)
├── [Link] (Module: app)
├── [Link]
├── [Link]
├── [Link]
├── [Link]
├── [Link]
└── [Link]
FOCS_Book

└── app
├── manifests

│ └── [Link]

<?xml version="1.0" encoding="utf-8"?>


<manifest xmlns:android="[Link]
xmlns:tools="[Link]

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.FOCS_Book">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.FOCS_Book">
<intent-filter>
<action android:name="[Link]" />

<category android:name="[Link]"
/>
</intent-filter>
</activity>
</application>

</manifest>


├── kotlin+java
│ └── [Link].focs_book
│ ├── data

│ │ └── ChapterRepository

package [Link].focs_book.data

import [Link]
import [Link].focs_book.[Link]
import [Link]
import [Link]

class ChapterRepository(private val context: Context) {


/**
* Loads chapters from assets. Default filename = "[Link]".
* Returns emptyList() on error so the UI won't crash.
*/
fun loadChapters(filename: String = "[Link]"): List<Chapter> {
val jsonString = try {
[Link](filename).bufferedReader().use
{ [Link]() }
} catch (e: Exception) {
[Link]()
return emptyList()
}

return try {
// specify the target type explicitly
Json { ignoreUnknownKeys = true
}.decodeFromString<List<Chapter>>(jsonString)
} catch (e: Exception) {
[Link]()
emptyList()
}
}
}

│ │
│ ├── model

│ │ └── Chapter

package [Link].focs_book.model

import [Link]

@Serializable
data class Chapter(
val id: Int,
val title: String,
val content: String? = null
)

│ │
│ ├── navigation

│ │ └── NavRoutes
package [Link].focs_book.navigation

sealed class NavRoutes(val route: String) {


object Splash : NavRoutes("splash")
object Home : NavRoutes("home")
object Toc : NavRoutes("toc") // Table of Contents
object ChapterList : NavRoutes("chapter_list") // List of chapters
object BookContent : NavRoutes("book_content") // Reader screen
object Search : NavRoutes("search") // Search screen
object Bookmarks : NavRoutes("bookmarks") // Bookmarks & Notes
object Settings : NavRoutes("settings") // Settings screen
object About : NavRoutes("about") // About screen
object Quiz : NavRoutes("quiz") // Quiz screen

│ │
│ └── ui
│ ├── screens

│ │ ├── [Link]

package [Link].focs_book.[Link]

import [Link].*
import [Link].material3.*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]

@OptIn(ExperimentalMaterial3Api::class) // ✅ allows CenterAlignedTopAppBar


@Composable
fun AboutScreen(navController: NavController) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = { Text("About") },
navigationIcon = {
IconButton(onClick = { [Link]() })
{
Icon(
imageVector = [Link],
contentDescription = "Back"
)
}
}
)
}
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding([Link]),
contentAlignment = [Link]
) {
Column(verticalArrangement = [Link]([Link])) {
Text(
text = "FOCS Book",
style = [Link]
)
Text(
text = "A structured cybersecurity learning app for
students and professionals.",
style = [Link]
)
Text(
text = "Version 1.0.0",
style = [Link]
)
Text(
text = "Developed by Shaikh Junaid Ahmad (2025)",
style = [Link]
)
}
}
}
}

│ │ ├── [Link]

package [Link].focs_book.[Link]

import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]

@Composable
fun BookmarksScreen(navController: NavController) {
val bookmarks = listOf("Bookmark 1", "Bookmark 2", "Bookmark 3")

LazyColumn(
verticalArrangement = [Link]([Link]),
modifier = Modifier
.fillMaxSize()
.padding([Link])
) {
items(bookmarks) { bookmark ->
Card(modifier = [Link]()) {
Box(modifier = [Link]([Link])) {
Text(text = bookmark, style =
[Link])
}
}
}
}
}

│ │ ├── [Link]

package [Link].focs_book.[Link]

import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]

@Composable
fun HomeScreen(navController: NavController) {
Box(
modifier = Modifier
.fillMaxSize()
.padding([Link]),
contentAlignment = [Link]
) {
Column(
verticalArrangement = [Link]([Link]),
horizontalAlignment = [Link],
modifier = [Link]()
) {
HomeMenuCard("📑 Table of Contents") {
[Link]([Link])
}
HomeMenuCard("🔍 Search") {
[Link]([Link])
}
HomeMenuCard("📌 Bookmarks & Notes") {
[Link]([Link])
}
HomeMenuCard("⚙️Settings") {
[Link]([Link])
}
HomeMenuCard("ℹ️About") {
[Link]([Link])
}
}
}
}

@Composable
fun HomeMenuCard(title: String, onClick: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() },
elevation = [Link]([Link])
) {
Box(
modifier = Modifier
.padding([Link])
.fillMaxWidth(),
contentAlignment = [Link]
) {
Text(
text = title,
style = [Link]
)
}
}
}

│ │ ├── [Link]

package [Link].focs_book.[Link]

import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]

@Composable
fun ReaderScreen(navController: NavController, chapterId: Int? = null) {
val context = [Link]
val repo = ChapterRepository(context)
val chapters = [Link]()
val chapter = [Link] { [Link] == chapterId } ?:
[Link]()

Column(
modifier = Modifier
.fillMaxSize()
.padding([Link])
) {
Text(text = chapter?.title ?: "Chapter", style =
[Link])
Spacer(modifier = [Link]([Link]))
Text(
text = chapter?.content ?: "No content available.",
style = [Link]
)
}
}

│ │ ├── [Link]

package [Link].focs_book.[Link]

import [Link].*
import [Link]
import [Link]
import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]

@Composable
fun SearchScreen(navController: NavController) {
var query by remember { mutableStateOf("") }

Column(modifier = Modifier
.fillMaxSize()
.padding([Link])) {
OutlinedTextField(
value = query,
onValueChange = { query = it },
label = { Text("Search") },
modifier = [Link]()
)

Spacer(modifier = [Link]([Link]))

Text(
text = "Search results for \"$query\"",
style = [Link]
)
}
}

│ │ ├── [Link]

package [Link].focs_book.[Link]

import [Link].*
import [Link].material3.*
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]

@Composable
fun SettingsScreen(
navController: NavController,
viewModel: SettingsViewModel
) {
// Collect state from ViewModel
val isDarkMode by [Link]()
val fontSize by [Link]()

Column(
modifier = Modifier
.fillMaxSize()
.padding([Link]),
verticalArrangement = [Link]([Link])
) {
Text("Settings", style = [Link])

// Dark mode toggle


Row(
modifier = [Link](),
horizontalArrangement = [Link]
) {
Text("Dark Mode", style = [Link])
Switch(
checked = isDarkMode,
onCheckedChange = { [Link](it) }
)
}

// Font size slider


Column {
Text("Font Size: ${[Link]()} sp", style =
[Link])
Slider(
value = fontSize,
onValueChange = { [Link](it) },
valueRange = 12f..24f,
steps = 6
)
}

// Reset button
Button(
onClick = { [Link]() },
modifier = [Link]()
) {
Text("Reset to Default")
}
}
}

│ │ ├── [Link]

package [Link].focs_book.[Link]

import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
import [Link]

@Composable
fun SplashScreen(navController: NavController) {
// Navigate to Home after 2 seconds
LaunchedEffect(true) {
delay(2000)
[Link]([Link]) {
popUpTo([Link]) { inclusive = true } // clear
backstack
}
}

// Centered splash content


Box(
modifier = Modifier
.fillMaxSize()
.padding([Link]),
contentAlignment = [Link]
) {
Column(horizontalAlignment = [Link]) {
// Placeholder app title (replace with Image if you add a logo)
Text(
text = "FOCS Book",
style = [Link],
fontWeight = [Link],
fontSize = [Link]
)

Spacer(modifier = [Link]([Link]))

Text(
text = "Foundation of Computer Science",
style = [Link]
)
}
}
}

│ │ └── [Link]

package [Link].focs_book.[Link]

import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
import [Link].focs_book.[Link]
import [Link].focs_book.[Link]

@Composable
fun TocScreen(navController: NavController) {
val context = [Link]
val repository = ChapterRepository(context)
val chapters: List<Chapter> = [Link]() // loads
[Link]
LazyColumn(
verticalArrangement = [Link]([Link]),
modifier = Modifier
.fillMaxSize()
.padding([Link])
) {
items(chapters) { chapter ->
Card(
modifier = Modifier
.fillMaxWidth()
.clickable {
// Navigate to ReaderScreen with chapter ID
[Link]("$
{[Link]}/${[Link]}")
},
elevation = [Link]([Link])
) {
Box(modifier = [Link]([Link])) {
Text(text = [Link], style =
[Link])
}
}
}
}
}

│ │
│ └── theme

│ └── viewmodel

│ ├── [Link]

[Link]
package [Link].focs_book.viewmodel

class ChapterViewModel {
}

│ ├──[Link]
package [Link].focs_book.viewmodel

import [Link]
import [Link]
import [Link]

class SettingsViewModel : ViewModel() {

private val _isDarkMode = MutableStateFlow(false)


val isDarkMode = _isDarkMode.asStateFlow()

private val _fontSize = MutableStateFlow(16f)


val fontSize = _fontSize.asStateFlow()
fun setDarkMode(enabled: Boolean) {
_isDarkMode.value = enabled
// Save to DataStore/SharedPreferences if needed
}

fun setFontSize(size: Float) {


_fontSize.value = size
}

fun resetSettings() {
_isDarkMode.value = false
_fontSize.value = 16f
}
}

│ └── [Link]

package [Link].focs_book

import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
import [Link].focs_book.[Link].*
import [Link].focs_book.[Link].FOCS_BookTheme
import [Link].focs_book.[Link]

class MainActivity : ComponentActivity() {


override fun onCreate(savedInstanceState: Bundle?) {
[Link](savedInstanceState)
setContent {
FOCS_BookTheme {
BookApp()
}
}
}
}

@Composable
fun BookApp() {
val navController = rememberNavController()

NavHost(
navController = navController,
startDestination = [Link]
) {
composable([Link]) { SplashScreen(navController) }
composable([Link]) { HomeScreen(navController) }
composable([Link]) { TocScreen(navController) }
composable(route = "${[Link]}/{chapterId}") {
backStackEntry ->
val chapterId =
[Link]?.getString("chapterId")?.toIntOrNull()
ReaderScreen(navController = navController, chapterId =
chapterId)
}

composable([Link]) {
ReaderScreen(navController = navController) }
composable([Link]) { SearchScreen(navController) }
composable([Link]) {
BookmarksScreen(navController) }

composable([Link]) {
val settingsViewModel: SettingsViewModel = viewModel()
SettingsScreen(navController = navController, viewModel =
settingsViewModel)
}

composable([Link]) { AboutScreen(navController) }
composable([Link]) { QuizScreen(navController) }
}
}

@Composable
fun QuizScreen(navController: [Link]) {
Text(text = "Quiz Screen")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
FOCS_BookTheme {
BookApp()
}
}

├── [Link].focs_book (androidTest)
├── [Link].focs_book (test)

├── assets

│ ├── book_content.json (androidTest)


{
"section": "I Introduction to Cyber Security",
"chapters": [
{
"title": "Defining Cyberspace",
"page": 4,
"content": "Cyberspace refers to..."
},
{
"title": "Architecture of Cyberspace",
"page": 8,
"content": "The architecture of cyberspace consists of..."
}
]
}

│ └── [Link] (androidTest)

[
{ "id": 1, "title": "Unit 1: Introduction to FOCS" },
{ "id": 2, "title": "Unit 2: Algorithms and Complexity" },
{ "id": 3, "title": "Unit 3: Automata Theory" },
{ "id": 4, "title": "Unit 4: Computability" }
]


├── res
└── res (generated)

└── Gradle Scripts


├── [Link] (Project: FOCS_Book)

├── [Link] (Module: app)

plugins {
alias([Link])
alias([Link])
alias([Link])
id("[Link]") version "1.9.23" // use
your Kotlin version

android {
namespace = "[Link].focs_book"
compileSdk = 36

defaultConfig {
applicationId = "[Link].focs_book"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"

testInstrumentationRunner =
"[Link]"
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("[Link]"),
"[Link]"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
}

dependencies {
implementation([Link])
implementation([Link])
implementation([Link])
implementation(platform([Link]))
implementation([Link])
implementation([Link])
implementation([Link])
implementation([Link].material3)
implementation("[Link]:kotlinx-serialization-
json:1.9.0")

// ✅ Add navigation
implementation([Link])

testImplementation([Link])
androidTestImplementation([Link])
androidTestImplementation([Link])
androidTestImplementation(platform([Link]))
androidTestImplementation([Link].junit4)
debugImplementation([Link])
debugImplementation([Link])
}

├── [Link]
├── [Link]
├── [Link]
├── [Link]
├── [Link]

└── [Link]

pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
[Link](RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}

[Link] = "FOCS_Book"
include(":app")

RoadMap for this application


🔹 1. Define App Purpose & Audience • Goal: Provide students, learners, and professionals
easy access to structured cybersecurity content. • Audience: Beginners to intermediate
learners. • Core Features: o Table of contents navigation 📑 o Search within topics 🔍 o
Bookmarks & notes 📌 o Dark mode 🌙 o Offline access 📴
________________________________________
🔹 2. Choose Tech Stack • Language: Kotlin (modern, Google recommended) • UI
Framework: Jetpack Compose (or XML if you prefer classic layouts) • Architecture: MVVM
(Model-View-ViewModel) for scalability • Storage: o Use Room Database or JSON assets to
store book chapters
o SharedPreferences for bookmarks & settings
________________________________________
🔹 3. Design App Structure a) Screens 1. Splash Screen – app logo + book title 2. Home
Screen – shows main sections (I, II, III, IV) 3. Table of Contents Screen – expandable list of
chapters 4. Chapter Reader Screen – display text content with scroll, zoom, and search 5.
Search Screen – search terms across chapters 6. Bookmarks & Notes Screen 7. Settings
Screen – dark mode, font size, reset data ________________________________________ b)
Navigation • Use Navigation Component for easy navigation between screens. • TOC →
Subsections → Chapter → Reader Screen. ________________________________________
🔹 4. Content Management • Store your book content in: o assets JSON/XML files
(lightweight, easy to load offline) o Example: • { • "section": "I Introduction to Cyber
Security", • "chapters": [ • { • "title": "Defining Cyberspace", • "page": 4, • "content":
"Cyberspace refers to..." • }, • { • "title": "Architecture of Cyberspace", • "page": 4, •
"content": "The architecture of cyberspace consists of..." • } • ] • } • This allows dynamic
rendering without hardcoding. ________________________________________
🔹 5. UI/UX Features • Expandable TOC list (Accordion style) • Reader-friendly UI:
adjustable font, themes • Highlighting & bookmarking (save last-read position) • Case Study
Section: formatted nicely with examples • Search bar: full-text search inside chapters
________________________________________ 🔹 6. Security Features (since it’s a
Cybersecurity book ⚡) • Protect content from easy copy-paste (optional). • Provide
Quiz/MCQ module later for learning engagement. • Offline mode (no data leak via internet).
________________________________________
🔹 7. Development Phases Phase 1 (MVP – 3 weeks) • Setup project, splash screen, navigation
• Implement Table of Contents + Reader screen • Load chapters from JSON Phase 2
(Enhancements – 3 weeks) • Add bookmarks, notes, and search • Add dark mode + font size
customization • Optimize for offline access Phase 3 (Advanced – optional) • Add quizzes /
practice tests • Integrate voice reading (Text-to-Speech) • Cloud sync (Firebase) for multi-
device ________________________________________
🔹 8. Deployment • Test thoroughly on emulators + your real phone (Infinix). • Generate
Signed APK or App Bundle. • Publish on Google Play Store (if public) or distribute APK
privately. ________________________________________
🔹 9. Tools Needed • Android Studio 2025.1.3.7 (already installed 🎉) • Gradle (comes with
Studio) • JSON editor (for managing book content) • GitHub (optional for version control)
________________________________________ ✅ This roadmap will give you a
professional book reader app, but tailored for cybersecurity education. 👉 Next step: I can
design a sample folder structure + UI wireframe for your project.

You might also like