Skip to content
Projects

miaowmiaow/fragmject has 1,467 GitHub stars and 249 forks. GitHot measured +1 stars in 24 hours and +1 in 7 days. Last checked 2026-11-10 04:09 UTC. Last push 2026-10-28.

How these counts are measured

fragmject is a learning project prepared for Kotlin and Jetpack Compose. | fragmject 是一个为初学者准备的上手项目。 通过对 Kotlin 和 Compose 的系统运用,实现的一个功能完备符合主流市场标准 App。 fragmject 没有复杂的业务和多余的封装, 完全依照 Android Developer 官方的写法。 代码简单,内容全面,快速上手,对理解其他项目设计思想和封装技巧也很有帮助。

Measured star growth

in 24h
+1
in 7 days
+1
in 15 days
Update delayed
in 1 month
Update delayed

Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.

README.EN.md

Original

Locales & Contributors

  • de_en English by DeepSeek-V4-Pro

Preface

At first glance learning Kotlin was quite difficult, tutorials and videos focused on knowledge points but lacked practical project examples. To overcome this hurdle I created fragmject which provides an example app that demonstrates core features of both Kotlin and Jetpack Compose while keeping things simple enough for beginners to grasp quickly. Special thanks goes out to Wan Android who provide their open API for use with this project!

Introduction

Fragmject is designed as an easy starting point for those new to programming with Kotlin and Jetpack Compose. By leveraging these technologies we are able to create a fully featured production ready application that adheres strictly to best practices outlined by Google themselves. Fragmject avoids complex business logic or unnecessary layers of abstraction; it simply implements everything according to how you would do so in real life projects following official guidelines provided by Android Developers website. Code is concise yet comprehensive making it straightforward even for someone without any prior experience. It will help you understand design patterns employed elsewhere alongside various techniques used for dependency injection and view binding.

Tech Stack

  • Language: Kotlin 2.4.x + Compose
  • Architecture: MVVM / MVI hybrid, multi-module (core + feature + app)
  • Navigation: Navigation 3 (NavBackStack + NavDisplay)
  • UI: Material 3 + WindowSizeClass adaptive layout
  • DI: Hilt
  • Database: Room 3
  • Network: Retrofit + OkHttp
  • Build: Gradle Kotlin DSL + Version Catalog + Convention Plugins

By working through this project you'll gain valuable insights into several areas such as:

  • Kotlin + Compose declarative UI
  • Navigation 3 (type-safe navigation + List-Detail side-by-side)
  • WindowSizeClass adaptive layout for tablets / foldables
  • MVVM, MVI
  • Custom views built from scratch including image pickers, editors, datepickers etc...
  • Advanced topics like bytecode manipulation via ASM library

Development Environment

  • JDK 21 or newer is required. The convention plugins in build-logic target JVM 21, so Gradle must run on JDK 21+. Older JDKs fail at configuration time with requires at least JVM runtime version 21. In Android Studio, choose Android Studio default JDK under Settings > Build Tools > Gradle (the bundled JBR qualifies); on the command line, point JAVA_HOME at JDK 21+. The project does not configure toolchain auto-download and does not pin an exact version.
  • Signing: keystore.properties / keystore.key in the repository are test-only keys and are committed on purpose. Use a separate private keystore for real releases.
  • CI (.github/workflows/ci.yml) runs the module dependency guard, the Compose stability check and all unit tests (including ArchitectureTest) on pull requests and the main branch.

In order for you to run this project normally, please use the latest preview version of Android Studio. You can download it at the following address: Download Android Studio | Android Developer

Pre knowledge

Before diving into this project, here are some basic concepts you might want to familiarize yourself with:

Beginner's Advice

If you prefer a codebase with less abstraction and more straightforward code to get started quickly, we recommend switching to the v1.4.0 tag, which keeps a simpler implementation without the complex architectural layering and wrappers, making it easier to understand and learn.

Screenshot display

1.jpg 2.jpg 3.jpg

Project Directory Structure

├── app                                             app shell module
|  └── src
|     └── main
|     |   ├── assets                                assets (HTML/JS/JSON mock data)
|     |   └── java                                  source code
|     |      ├── MainActivity.kt                    single Activity
|     |      ├── FragmjectApplication.kt            Application (Hilt entry)
|     |      └── AppNavGraph.kt                     navigation graph (Navigation 3 + WindowSizeClass)
|     |
|     ├── build.gradle.kts                          module build config
|     ├── dictionary                                custom obfuscation dictionary
|     └── proguard-rules.pro                        code obfuscation config
| 
├── core                                            core layer (foundation, no business dependencies)
|  ├── android-platform                             platform & process-level utilities (AppScope / BaseContentProvider / File* / CacheUtils / UriPathUtils)
|  ├── data-contract                                data port contracts (remote/local DataSource + HTTP protocol models)
|  ├── data-repository                              data implementation (RepositoryImpl / PagingSource / Hilt bindings)
|  ├── database                                     database (Room 3)
|  ├── designsystem                                 design system (AppTheme / WindowSizeClass / colors / spacing, no components)
|  ├── domain                                       domain layer (Repository interfaces + UseCase + DomainResult)
|  ├── image-editor                                 image editing engine (canvas / crop / encode / save)
|  ├── model                                        data models
|  ├── navigation-contract                          navigation contracts (semantic Navigator, cross-feature decoupling)
|  ├── navigation-runtime                           navigation capability (Navigation 3 type-safe routes)
|  ├── network                                      network layer (Retrofit + OkHttp)
|  ├── qrscanner                                    scanning capability (CameraX / ML Kit)
|  ├── ui                                           UI component library (FeedCard / PagingSwipeRefreshBox etc.)
|  └── webview                                      WebView capability (pooling / caching / CookieStore)
| 
├── feature                                         feature modules (each contains api / impl submodules)
|  ├── article                                      article module (WebView detail / download)
|  |  ├── api                                       API layer (NavKey definitions)
|  |  └── impl                                      implementation layer (Screen / ViewModel)
|  ├── auth                                         login / signup
|  ├── authenticator                                OTP (account management / Keystore-encrypted secret)
|  ├── collection                                   favorites / shares
|  ├── demo                                         component demos (calendar / wheel picker / scanning / nested scroll etc.)
|  ├── home                                         home (Home / Nav / Project / System / My)
|  ├── picture                                      picture module (picker / preview / editor)
|  ├── search                                       search
|  └── user                                         user (profile / points / ranking / settings / history)
| 
├── build-logic                                     build logic (Gradle Convention Plugins)
|  └── convention
|     └── src/main/kotlin
|        ├── FragmjectAndroidApplicationPlugin      application convention plugin
|        ├── FragmjectAndroidComposePlugin          compose convention plugin
|        ├── FragmjectKotlinParcelizePlugin         Kotlin Parcelize capability plugin
|        ├── FragmjectKotlinSerializationPlugin     Kotlin Serialization capability plugin
|        ├── FragmjectAndroidHiltPlugin             Hilt convention plugin
|        ├── FragmjectAndroidLibraryPlugin          library convention plugin
|        ├── FragmjectAndroidDependencyGuardPlugin  dependency guard plugin
|        └── FragmjectAndroidRoomPlugin             Room convention plugin
|
├── gradle
|  └── libs.versions.toml                           version catalog
|
├── build.gradle.kts                                project build config
├── config.properties                               project config
├── gradle.properties                               gradle config
└── settings.gradle.kts                             module dependency config

Adaptive Layout (WindowSizeClass)

The project implements full adaptive layout for tablets and foldables using material3-window-size-class.

Layout Strategy

Window Size Width Navigation Component Detail Display
Compact < 600dp NavigationBar (bottom bar) full-screen push
Medium 600–840dp NavigationDrawerItem + Surface (left nav) full-screen push
Expanded ≥ 840dp PermanentNavigationDrawer (persistent rail) side-by-side panel

List-Detail

In Expanded mode (tablet landscape / desktop), tapping articles, user profiles, settings, etc. renders in the right-side panel instead of full-screen navigation:

graph LR
    A["List (50%)"] --> B["DetailPane (50%)"]
    B --> C["WebScreen"]
    B --> D["UserScreen"]
    B --> E["SettingScreen"]
    B --> F["..."]

Key Files

Usage

val windowSizeClass = LocalWindowSizeClass.current
when (windowSizeClass.widthSizeClass) {
    WindowWidthSizeClass.Compact -> CompactLayout()
    WindowWidthSizeClass.Medium  -> MediumLayout()
    WindowWidthSizeClass.Expanded -> ExpandedLayout()
}

Jetpack Compose

Less code

Using Compose instead of the traditional Android View system enables us to accomplish more with less code, leading to fewer lines of code needing testing and debugging, and lower likelihood of bugs occurring. This makes maintenance easier for reviewers and maintainers alike, reducing the amount of code that needs to be read, understood, approved, and maintained.

Composereflects a simpler concept in itslayout system,whereallcodeiswritten in one language and located within thesamefile,eliminating theneedfor back-and-forth switching between Kotlin andXML.

Intuitive

User's Question: Compose uses declarative APIs which mean that you only have to define the interface without worrying about how to implement each step.

By leveraging Compose, you can create smaller statelesscomponents that aren't attached to any particular activity or fragment.

In Compose, state is explicit and passed down tocomposable elements allowing them to maintain their integrity while being isolated from other parts of the app. When the statechanges, the UI updates automatically.

Mutually compatible

Compose is compatible with all your existing code: you can call Compose code from View or call View from Compose. Most commonly used libraries, such as Navigation, ViewModel, and Kotlin coroutine, are suitable for Compose, so you can start adopting them anytime, anywhere.

SharedFlowBus

Quick access

// send message
SharedFlowBus.with(objectKey: Class<T>).tryEmit(value: T)

// send sticky message
SharedFlowBus.withSticky(objectKey: Class<T>).tryEmit(value: T)

// subscribe message
SharedFlowBus.on(objectKey: Class<T>).observe(owner){ it ->
    println(it)
}

// subscribe sticky message
SharedFlowBus.onSticky(objectKey: Class<T>).observe(owner){ it ->
    println(it)
}

Picture editor(feature/picture)

Screenshot display

5.gif 6.gif 7.gif

Quick access

PictureEditorDialog.newInstance()
    .setBitmapPath(path)
    .setEditorFinishCallback(object : EditorFinishCallback {
        override fun onFinish(path: String) {
            val bitmap = BitmapFactory.decodeFile(path, BitmapFactory.Options())
        }
    })
    .show(childFragmentManager)

If you feel that PictureEditorDialog cannot meet the requirements, you can also customize the style through PictureEditorView.

Custom usage

<com.example.miaow.picture.editor.PictureEditorView
    android:id="@+id/pic_editor"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
picEditor.setBitmapPath(path)
picEditor.setMode(PictureEditorView.Mode.STICKER)
picEditor.setGraffitiColor(Color.parseColor("#ffffff"))
picEditor.setSticker(StickerAttrs(bitmap))
picEditor.graffitiUndo()
picEditor.mosaicUndo()
picEditor.saveBitmap()

That's all for PictureEditorView. For specific usage, please refer to PictureEditorDialogs.

Picture cropping

<com.example.miaow.picture.editor.PictureClipView
    android:id="@+id/clip"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
clip.setBitmapResource(bitmap)
clip.rotate()
clip.reset()
clip.saveBitmap()

That's all for PictureClipView. For specific usage, please refer to PictureClipDialogs.

Picture selection

if (context is AppCompatActivity) {
    PictureSelectorDialog.newInstance()
        ...
        .show(context.supportFragmentManager)
}

Calendar

8.gif

Source location

└── feature
    └── demo
       └── impl
          └── ui
             └── calendar
                └── CalendarScreen.kt

Quick access

val calendarState = rememberCalendarState()
calendarState.addSchedule(text)

Calendar(
    state = calendarState,
    modifier = Modifier.padding(vertical = 15.dp),
    onSelectedDateChange = { y, m, d ->
        println("CalendarScreen: $y - $m - $d")
    }
)

Main open-source libraries

Gitee

About me

Thanks

Thank you to all outstanding open source projects^_^

If you like it, I hope to give it to Star or Fork^_^

Thank you~~

Explore related projects