Model-driven navigation for Jetpack Compose

Overview

Appyx

Build Maven Central GitHub license

Project page

https://bumble-tech.github.io/appyx

License

Copyright 2021 Bumble.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Consider introducing a Github merge queue

    Consider introducing a Github merge queue

    Currently all branches are merged into main without requiring being up to date with main.

    This could mean we merge an old branch that has a breaking change, and the main branch needs to be fixed.

    Also, potentially if we introduce stricter static code analysis, potentially this older branch will increase our tech debt

    We should consider using a merge queue to avoid this problem: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue

    Otherwise we could also enforce that all PRs need to be up to date with main, but I believe that will be far more annoying.

    build & CI 
    opened by LachlanMcKee 6
  • [DRAFT] Gesture navigation API + swipe implementation

    [DRAFT] Gesture navigation API + swipe implementation

    Description

    Basic gesture API + Swipe implementation. Implemented as a Modifier which is applied to container composable.

    If we agree on the API more gestures to come:

    • draggable
    • multi touching
    • ..

    Fixes https://github.com/bumble-tech/appyx/issues/97

    https://user-images.githubusercontent.com/5773436/186649066-32f5b004-6ce2-4587-bca2-84000399fff7.mp4

    Check list

    • [ ] I have updated CHANGELOG.md if required.
    • [ ] I have updated documentation if required.
    enhancement 
    opened by KovalevAndrey 5
  • Backstack: triggering navigation mid-transition discards previous transition

    Backstack: triggering navigation mid-transition discards previous transition

    The issue seems to be happening when doing consecutive operations (push or pop), best explained with video:

    https://user-images.githubusercontent.com/20369236/177795492-c806aeaa-2046-467a-ad29-7ff85852a7c6.mp4

    Full sample code:

    class RootActivity : NodeActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        WindowCompat.setDecorFitsSystemWindows(window, false)
    
        setContent {
          MaterialTheme {
            NodeHost(
              integrationPoint = integrationPoint,
              factory = { buildContext ->
                RootNode(
                  buildContext = buildContext,
                  backstack = BackStack(
                    initialElement = RootNode.Routing.Child("Root"),
                    savedStateMap = buildContext.savedStateMap,
                  )
                )
              },
            )
          }
        }
      }
    }
    
    class RootNode(
      buildContext: BuildContext,
      private val backstack: BackStack<Routing>,
    ) : ParentNode<RootNode.Routing>(
      routingSource = backstack,
      buildContext = buildContext,
    ) {
      sealed class Routing : Parcelable {
        @Parcelize
        data class Child(val id: String) : Routing()
      }
    
      @Composable
      override fun View(modifier: Modifier) {
        Surface(
          modifier = modifier.fillMaxSize(),
          color = Color.White
        ) {
          Children(
            routingSource = backstack,
            transitionHandler = rememberBackstackSlider(
              transitionSpec = { tween(2000, easing = LinearEasing) }
            ),
          )
        }
      }
    
      override fun resolve(routing: Routing, buildContext: BuildContext): Node {
        return ChildNode(
          name = when (routing) {
            is Routing.Child -> routing.id
          },
          buildContext = buildContext,
          pushNew = { backstack.push(Routing.Child(UUID.randomUUID().toString())) },
        )
      }
    }
    
    class ChildNode(
      private val name: String,
      private val pushNew: () -> Unit,
      buildContext: BuildContext
    ) : Node(buildContext = buildContext) {
    
      private val colors = listOf(
        manatee,
        sizzling_red,
        atomic_tangerine,
        silver_sand,
        md_pink_500,
        md_indigo_500,
        md_blue_500,
        md_light_blue_500,
        md_cyan_500,
        md_teal_500,
        md_light_green_500,
        md_lime_500,
        md_amber_500,
        md_grey_500,
        md_blue_grey_500
      )
    
      private val colorIndex =
        buildContext.savedStateMap?.get(KEY_COLOR_INDEX) as? Int ?: Random.nextInt(colors.size)
      private val color = colors[colorIndex]
    
      override fun onSaveInstanceState(state: MutableSavedStateMap) {
        super.onSaveInstanceState(state)
        state[KEY_COLOR_INDEX] = colorIndex
      }
    
      @Composable
      override fun View(modifier: Modifier) {
        Box(
          modifier = Modifier
            .fillMaxSize()
            .background(
              color = color,
              shape = RoundedCornerShape(6.dp)
            )
        ) {
          Column(
            modifier = Modifier.padding(24.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp),
          ) {
            Text("Child ($name).")
            Row {
              // Local UI state should be saved too (both in backstack and onSaveInstanceState)
              var counter by rememberSaveable { mutableStateOf(0) }
              Text(text = "Counter $counter", modifier = Modifier.align(CenterVertically))
              Spacer(modifier = Modifier.width(16.dp))
              Button(onClick = { counter++ }, content = { Text("Increment") })
            }
            Row {
              Button(onClick = { navigateUp() }, content = { Text("Go up") })
            }
            Row {
              Button(onClick = pushNew, content = { Text("Push new") })
            }
          }
        }
      }
    
      companion object {
        private const val KEY_COLOR_INDEX = "ColorIndex"
      }
    }
    
    bug 
    opened by steelahhh 4
  • Introduced dependency diff github action

    Introduced dependency diff github action

    Description

    Adds the ability to see dependency changes within PRs.

    You can see the diff in action within this PR (https://github.com/LachlanMcKee/appyx/pull/2) - It needs to be raised in my fork as this needs to be the base branch.

    Check list

    • [x] I have updated CHANGELOG.md if required.
    • [x] I have updated documentation if required.
    opened by LachlanMcKee 3
  • Fix IntegrationPoint memory leak created by ActivityIntegrationPoint

    Fix IntegrationPoint memory leak created by ActivityIntegrationPoint

    Description

    ActivityIntegrationPoint leaking Activity instances since ActivityIntegrationPoint holds a strong reference to the Activity. WeakHashMap is unable to clear the entry since the same activity instance is being used as key and also in the value object

    ┬───
    │ GC Root: Thread object
    │
    ├─ android.net.ConnectivityThread instance
    │    Leaking: NO (PathClassLoader↓ is not leaking)
    │    Thread name: 'ConnectivityThread'
    │    ↓ Thread.contextClassLoader
    ├─ dalvik.system.PathClassLoader instance
    │    Leaking: NO (ActivityIntegrationPoint↓ is not leaking and A ClassLoader is
    │    never leaking)
    │    ↓ ClassLoader.runtimeInternalObjects
    ├─ java.lang.Object[] array
    │    Leaking: NO (ActivityIntegrationPoint↓ is not leaking)
    │    ↓ Object[884]
    ├─ com.bumble.appyx.core.integrationpoint.ActivityIntegrationPoint class
    │    Leaking: NO (a class is never leaking)
    │    ↓ static ActivityIntegrationPoint.integrationPoints
    │                                      ~~~~~~~~~~~~~~~~~
    ├─ java.util.WeakHashMap instance
    │    Leaking: UNKNOWN
    │    Retaining 169.6 kB in 3954 objects
    │    ↓ WeakHashMap.table
    │                  ~~~~~
    ├─ java.util.WeakHashMap$Entry[] array
    │    Leaking: UNKNOWN
    │    Retaining 169.5 kB in 3951 objects
    │    ↓ WeakHashMap$Entry[0]
    │                       ~~~
    ├─ java.util.WeakHashMap$Entry instance
    │    Leaking: UNKNOWN
    │    Retaining 83.2 kB in 1926 objects
    │    referent instance of co.zuper.android.ui.host.HostActivity with mDestroyed
    │    = true
    │    ↓ WeakHashMap$Entry.value
    │                        ~~~~~
    ├─ com.bumble.appyx.core.integrationpoint.ActivityIntegrationPoint instance
    │    Leaking: UNKNOWN
    │    Retaining 83.2 kB in 1925 objects
    │    activity instance of co.zuper.android.ui.host.HostActivity with mDestroyed
    │    = true
    │    ↓ ActivityIntegrationPoint.activity
    │                               ~~~~~~~~
    ╰→ co.zuper.android.ui.host.HostActivity instance
    ​     Leaking: YES (ObjectWatcher was watching this because co.zuper.android.ui.
    ​     host.HostActivity received Activity#onDestroy() callback and
    ​     Activity#mDestroyed is true)
    ​     Retaining 82.9 kB in 1916 objects
    ​     key = 005ec729-daaa-43df-a6d2-2f97825f9d7a
    ​     watchDurationMillis = 18279
    ​     retainedDurationMillis = 13277
    ​     mApplication instance of co.zuper.android.ZuperApp
    ​     mBase instance of androidx.appcompat.view.ContextThemeWrapper
    
    METADATA
    
    Build.VERSION.SDK_INT: 33
    Build.MANUFACTURER: Google
    LeakCanary version: 2.9.1
    App process name: co.zuper.android.dev
    Class count: 30112
    Instance count: 243224
    Primitive array count: 143091
    Object array count: 38493
    Thread count: 31
    Heap total bytes: 31331321
    Bitmap count: 28
    Bitmap total bytes: 900521
    Large bitmap count: 0
    Large bitmap total bytes: 0
    Stats: LruCache[maxSize=3000,hits=117858,misses=248001,hitRate=32%]
    RandomAccess[bytes=12538085,reads=248001,travel=81765454152,range=37319569,size=
    46458866]
    Analysis duration: 4738 ms
    

    Check list

    • [x] I have updated CHANGELOG.md if required.
    • [x] I have updated documentation if required.
    bug 
    opened by devv911 3
  • Issue #34 Add unit and instrumentation tests to github action

    Issue #34 Add unit and instrumentation tests to github action

    Description

    Ensure that unit tests and instrumentation tests are run as part of the PR

    Fixes #34

    Check list

    • [x] I do not need to update CHANGELOG.md.
    • [x] I do not need to update the documentation.
    enhancement 
    opened by LachlanMcKee 3
  • Add unit test support

    Add unit test support

    Description

    This pull request adds a unit testing API. New helpers: InteractorTestHelper NodeTestHelper ParentNodeTestHelper

    To make testing easier you can use: FeatureStub NodeViewStub instead of relying on mocks.

    Check list

    • [x] I have updated CHANGELOG.md if required.
    • [x] I have updated documentation if required.
    opened by vladcipariu91 3
  • Use default stiffness for spring()

    Use default stiffness for spring()

    Description

    Remove overridden spring specs due to compose interrupt transition behaviour. ...

    Check list

    • [x] CHANGELOG.md not updated
    • [x] Documentation not updated
    opened by zsoltk 3
  • Remove fragment integration point

    Remove fragment integration point

    Description

    Fixes https://github.com/bumble-tech/appyx/issues/146

    Check list

    • [x] I have updated CHANGELOG.md if required.
    • [x] I have updated documentation if required.
    cleanup 
    opened by KovalevAndrey 2
  • Drop com.bumble.appyx.core.minimal.reactive

    Drop com.bumble.appyx.core.minimal.reactive

    We already have attachWorkflow and waitForChildAttached coroutine powered public API, so I think we can migrate copy-pasted from RIBs PermissionRequestBoundary to Flows.

    Or we should commit to minimal.reactive and change attachWorkflow, waitForChildAttached implementations.

    @zsoltk @KovalevAndrey @LachlanMcKee

    question 
    opened by CherryPerry 2
  • Remove InteractorTestHelper.kt

    Remove InteractorTestHelper.kt

    Description

    We're discouraging the use of Interactor tests in favor of using Node tests.

    The reasoning here is that unit tests are about units, not classes. And in the case of a node-based architecture, the unit is the node. Also by writing node tests you are also covering the integration of different node parts.

    Check list

    • [X] I have updated CHANGELOG.md if required.
    • [X] I have updated documentation if required.
    opened by mapm14 2
  • Verify API stability after release

    Verify API stability after release

    For all published modules:

    • Use org.jetbrains.kotlinx:binary-compatibility-validator and add it as part of CI workflow
    • Use explicitApi() mode for Kotlin Gradle plugin
    enhancement 
    opened by CherryPerry 0
  • Wait until root node is created before performing integration point operations

    Wait until root node is created before performing integration point operations

    Description

    Fixes https://github.com/bumble-tech/appyx/issues/174

    Check list

    • [ ] I have updated CHANGELOG.md if required.
    • [ ] I have updated documentation if required.
    bug 
    opened by KovalevAndrey 0
  • [Draft] Use AndroidX saved state

    [Draft] Use AndroidX saved state

    Description

    Instead of using custom activity result engine, use AndroidX provided.

    Other option: use permissions and activity results only from view via Compose provided methods.

    Check list

    • [ ] I have updated CHANGELOG.md if required.
    • [ ] I have updated documentation if required.
    opened by CherryPerry 0
  • IntegrationPointExample does not work with

    IntegrationPointExample does not work with "do not keep activities"

    Steps:

    1. Do not keep activities
    2. Open IntegrationPointExample
    3. Launch activity for result
    4. Return random result code
    5. Node has not received it

    Issue:

    1. The node is created and subscribed to ActivityStarter
    2. When the next activity is launched the node is destroyed
    3. onActivityResult is invoked and events are pushed
    4. Compose rendering is triggered and the node is recreated (Activity.setContent { })
    5. The node is created and subscribed to ActivityStarter

    Discovered after converting error log into exception in https://github.com/bumble-tech/appyx/pull/173

    bug 
    opened by CherryPerry 0
  • Implement SavedStateRegistryOwner per Node

    Implement SavedStateRegistryOwner per Node

    And provide as LocalComposition.

    See https://github.com/badoo/RIBs/pull/374

    By using SavedStateRegistry per node we are scoping instance saving to a particular place instead of relying on Activity. JetPack navigation does the same.

    enhancement 
    opened by CherryPerry 0
Releases(1.0-alpha09)
  • 1.0-alpha09(Sep 22, 2022)

    • #151 - Breaking change: Renamed Routing to NavTarget. All related namings are affected (RoutingElement, RoutingKey, etc.)
    • #158 - Breaking change: Renamed TransitionState to State in all NavModel impls. Renamed STASHED_IN_BACK_STACK to STASHED.
    • #146 - Breaking change: Removed FragmentIntegrationPoint. Clients should use ActivityIntegrationPoint.getIntegrationPoint(context: Context) to get integration point from Fragment
    • #160 - Breaking change: Renamed navmodel-addons to navmodel-samples and stopped publishing the binary. If you feel we should add any of the samples to the main codebase, please let us know!
    • #138 - Fixed: androidx.appcompat:appcompat from is exposed via api within com.bumble.appyx:core. This prevents potential compilation bugs.
    • #143 - Fixed: Correctly exposed transitive dependencies that are part of the libraries ABI
    • #162 - Fixed: NodeTestHelper's moveTo function can now move to Lifecycle.State.DESTROYED. The node itself has safeguards to prevent moving from destroyed state, and moving to destroyed is a valid test case.
    • #145 - Updated: SpotlightSlider now uses offset modifier with lambda
    • #159 - Added: NodeHost now takes modifier parameter to decorate the view of a root node
    • #162 - Added: disposeOnDestroyPlugin extension has been added to interop-rx2. This will allow Rx2 code to be easily disposed when the node it belongs to is destroyed.
    • #161 - Added: Operation helpers
    Source code(tar.gz)
    Source code(zip)
  • 1.0-alpha08(Sep 12, 2022)

    • #140 - Breaking change: Added testing-ui-activity module to avoid needing to add testing-ui as a debug implementation as part of instrumentation testing. See the linked issue for more details
    • #139 - Fixed: IntegrationPoint memory leak created by ActivityIntegrationPoint
    Source code(tar.gz)
    Source code(zip)
  • 1.0-alpha07(Sep 9, 2022)

    • #122 - Breaking change: ChildEntry.ChildMode is removed, now nodes are always created when a nav model changes (previously default behaviour)
    • #99Breaking change: Removed IntegrationPointAppyxProvider and made ActivityIntegrationPoint's constructor private. Use ActivityIntegrationPoint.createIntegrationPoint. This uses a weak reference to keep track of the integration points, and will not introduce memory leaks.
    • #122 - Added: New ChildEntry.KeepMode that allows to destroy nodes that are currently not visible on the screen
    • #132 - Added: New NodeComponentActivity to extend when wanting to work with ComponentActivity as your base activity, eg when migrating from a project built from the Jetpack Compose template
    • #119 - Fixed: Lifecycle observers are invoked in incorrect order (child before parent)
    • #62 - Fixed: Node is marked with stable annotation making some of the composable functions skippable
    • #129 - Updated: Removed sealed interface from operations to allow client to define their own
    • #133 - Updated: NodeView interface and ParentNode marked as stable improving amount of skippable composables
    Source code(tar.gz)
    Source code(zip)
  • 1.0-alpha06(Aug 26, 2022)

    • #96Breaking change: Removed InteractorTestHelper. Please use Node tests instead of Interactor tests.
    • #99Breaking change: Modified package of NodeConnector and Connectable
    • #99Added: Source.rx2() to convert Source to io.reactivex.Observable
    • #107Fixed: Back press handlers are not properly registered on lifecycle events
    Source code(tar.gz)
    Source code(zip)
  • 1.0-alpha05(Aug 19, 2022)

    • https://github.com/bumble-tech/appyx/issues/83 – Breaking change: RoutingSource renamed to NavModel. All subclasses, fields, package names, etc., any mentions of the word follow suit.
    • https://github.com/bumble-tech/appyx/pull/91 – Fixed: Spotlight next and previous operations crash fix
    Source code(tar.gz)
    Source code(zip)
  • 1.0-alpha04(Aug 18, 2022)

    • https://github.com/bumble-tech/appyx/pull/39 – Added: Workflows implementation to support deeplinks
    • https://github.com/bumble-tech/appyx/pull/32 – Added: BackPressHandler plugin that allows to control back press behaviour via androidx.activity.OnBackPressedCallback
    • https://github.com/bumble-tech/appyx/issues/59 – Added: interface for ParentNodeView<>
    • https://github.com/bumble-tech/appyx/issues/69 – Added: Jetpack Compose Navigation code sample
    • https://github.com/bumble-tech/appyx/issues/81 – Added: Support integration point for multiple roots
    • https://github.com/bumble-tech/appyx/pull/65 – Added: InteropBuilderStub and InteropSimpleBuilderStub testing util classes
    • https://github.com/bumble-tech/appyx/issues/47 – Updated: The customisations module is now pure Java/Kotlin.
    • https://github.com/bumble-tech/appyx/issues/85 – Updated: Improved InteropView error messaging when Activity does not implement IntegrationPointAppyxProvider
    • https://github.com/bumble-tech/appyx/issues/88 – Updated: Moved TestUpNavigationHandler to testing-unit-common
    Source code(tar.gz)
    Source code(zip)
  • 1.0-alpha03(Aug 2, 2022)

  • 1.0-alpha02(Jul 19, 2022)

    • https://github.com/bumble-tech/appyx/pull/19 – Fixed: Do not allow setting Node.integrationPoint on non-root nodes.
    • https://github.com/bumble-tech/appyx/pull/21 – Fixed: Integration point attached twice crash when using live literals
    • https://github.com/bumble-tech/appyx/issues/14 – Fixed: Transition interruptions bug
    • https://github.com/bumble-tech/appyx/pull/23 – Added: Unit test support
    • https://github.com/bumble-tech/appyx/issues/26 – Added: Publish snapshot versions
    • https://github.com/bumble-tech/appyx/pull/9 – Migrated app-tree-utils into this repository
    Source code(tar.gz)
    Source code(zip)
  • 1.0-alpha01(Jul 4, 2022)

Compose-navigation - Set of utils to help with integrating Jetpack Compose and Jetpack's Navigation

Jetpack Compose Navigation Set of utils to help with integrating Jetpack Compose

Adam Kobus 5 Apr 5, 2022
A library that enables Safe Navigation for you Composable destinations when using Jetpack Compose Navigation

A library that enables Safe Navigation for you Composable destinations when using Jetpack Compose Navigation

Roman Levinzon 56 Aug 26, 2022
An MVI project setup using Jetpack compose. This would be a good place to start learning Model View Intent (MVI) architecture for Android.

Compose-MVI An MVI project setup using Jetpack compose. This would be a good place to start learning Model View Intent (MVI) architecture for Android.

null 6 Jul 28, 2022
Navigation-Compose - A sample to showcase Kotlin, MVVM, Hilt, Coroutines, StateFlow, Jetpack compose

Navigation-Compose A sample to showcase Kotlin, MVVM, Hilt, Coroutines, StateFlo

Mohammadali Rezaei 6 Jul 13, 2022
A Simple Blog App using Jetpack Compose, Flow, Navigation Compose, Room and Firebase

BlogCompose A Simple Blog App using Jetpack Compose, Flow, Navigation Compose, Room and Firebase Instructions Download your Firebase configuration fil

null 3 Aug 16, 2022
This repos one of the ways hows how to use Jetpack Compose Navigation along with Dagger 2

Dagger 2 and Jetpack Compose Integration This repository is about a way how to use Dagger 2 for projects which using Jetpack Compose. Here is an artic

Alexey Glukharev 9 Oct 3, 2022
Create Bottom Navigation Bar with Jetpack Compose

BottomNavigationBarComposeExample Create Bottom Navigation Bar with Jetpack Compose https://johncodeos.com/how-to-create-bottom-navigation-bar-with-je

JohnCodeos.com 30 Sep 7, 2022
Small Android project demonstrating some navigation components for Jetpack Compose.

Small Android project demonstrating some navigation components for Jetpack Compose. Created this for presenting about this topic for a GDG meetup.

Parshav 3 Sep 15, 2021
Small code generating library for safe Jetpack Compose navigation with no boilerplate.

Compose Destinations A KSP library to use alongside compose navigation. It reduces boilerplate code and is less error-prone since passing arguments be

Rafael Costa 1.6k Oct 1, 2022
Kotlin, MVVM, Navigation Component, Hilt, Jetpack Compose, Retrofit2

What is this project? This course will replace my old java mvvm introduction: https://codingwithmitch.com/courses/rest-api-mvvm-retrofit2/. Watch the

Mitch Tabian 430 Sep 29, 2022
[Tutorial] D-pad navigation in Jetpack Compose

dpad-compose D-pad navigation in Jetpack Compose The problem While Android is mostly used on touch devices, the operating system can also be used with

Walter Berggren 25 Sep 26, 2022
Android Sample Kotlin+ MVI + Jetpack compose + Coroutines + Retrofit + Hilt + Room + Navigation component

MVIComposeSample Android Sample app to show user latest movies implementing MVI + Clean Architecture using kotlin & Jetpack compose following solid an

Ahmed Atwa 9 Jul 28, 2022
Android App made by Jetpack Compose Components with Kotlin, MVVM Pattern, Multi Module, Navigation, Hilt, Coroutines, Retrofit and cached data by Room

Android App made by Jetpack Compose Components with Kotlin, MVVM Pattern, Multi Module, Navigation, Hilt, Coroutines, Retrofit and cached data by Room

Yogi Dewansyah 13 Aug 31, 2022
Android App made by Jetpack Compose Components with Kotlin, MVVM Pattern, Multi Module, Navigation, Hilt, Coroutines, Retrofit and cached data by Room

Mobile Banking Android App made by Jetpack Compose Components with Kotlin, MVVM Pattern, Multi Module, Navigation, Hilt, Coroutines, Retrofit and cach

Yogi Dewansyah 13 Aug 31, 2022
Jetpack Compose Boids | Flocking Insect 🐜. bird or Fish simulation using Jetpack Compose Desktop 🚀, using Canvas API 🎨

?? ?? ?? Compose flocking Ants(boids) ?? ?? ?? Jetpack compose Boids | Flocking Insect. bird or Fish simulation using Jetpack Compose Desktop ?? , usi

Chetan Gupta 38 Sep 25, 2022
A collection of animations, compositions, UIs using Jetpack Compose. You can say Jetpack Compose cookbook or play-ground if you want!

Why Not Compose! A collection of animations, compositions, UIs using Jetpack Compose. You can say Jetpack Compose cookbook or play-ground if you want!

Md. Mahmudul Hasan Shohag 159 Sep 26, 2022
Learn Jetpack Compose for Android by Examples. Learn how to use Jetpack Compose for Android App Development. Android’s modern toolkit for building native UI.

Learn Jetpack Compose for Android by Examples. Learn how to use Jetpack Compose for Android App Development. Android’s modern toolkit for building native UI.

MindOrks 372 Sep 20, 2022
This is a sample app(For beginners - App #2) built using Jetpack Compose. It demonstrates the concept of State Hoisting in Jetpack Compose.

JetBMICalculator This is a sample app(For beginners - App #2) built using Jetpack Compose. It demonstrates the concept of State Hoisting in Jetpack Co

BHAVNA THACKER 2 Feb 9, 2022
Jetpack-Compose-Demo - Instagram Profile UI using Jetpack Compose

Jetpack-Compose-Demo Instagram Profile UI using Jetpack Compose

omar 1 Aug 11, 2022