{"id":13610743,"url":"https://github.com/massivemadness/Fragula","last_synced_at":"2025-04-13T01:33:01.659Z","repository":{"id":40538401,"uuid":"466849855","full_name":"massivemadness/Fragula","owner":"massivemadness","description":"🧛 Fragula is a swipe-to-dismiss extension for navigation component library for Android","archived":false,"fork":false,"pushed_at":"2023-12-24T08:38:24.000Z","size":55670,"stargazers_count":323,"open_issues_count":7,"forks_count":19,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-01T19:55:37.020Z","etag":null,"topics":["android","backstack","compose","compose-navigation","fragment","gesture","jetpack-compose","kotlin","library","navigation","navigation-component","navigator","swipe-to-dismiss","swipeable","swipeback","transitions","type-safety"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/massivemadness.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-03-06T20:35:21.000Z","updated_at":"2024-07-13T09:44:53.000Z","dependencies_parsed_at":"2022-07-13T15:59:26.116Z","dependency_job_id":"60d7eb47-39f1-4a06-b290-d90d7c52b117","html_url":"https://github.com/massivemadness/Fragula","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massivemadness%2FFragula","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massivemadness%2FFragula/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massivemadness%2FFragula/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/massivemadness%2FFragula/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/massivemadness","download_url":"https://codeload.github.com/massivemadness/Fragula/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223558323,"owners_count":17165106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","backstack","compose","compose-navigation","fragment","gesture","jetpack-compose","kotlin","library","navigation","navigation-component","navigator","swipe-to-dismiss","swipeable","swipeback","transitions","type-safety"],"created_at":"2024-08-01T19:01:47.572Z","updated_at":"2024-11-07T17:30:37.435Z","avatar_url":"https://github.com/massivemadness.png","language":"Kotlin","readme":"# Fragula 2\n\n**Fragula** is a swipe-to-dismiss extension for [navigation component](https://developer.android.com/guide/navigation/navigation-getting-started) library for Android.  \nIt is an adaptation of an earlier version created by **@shikleev** and now maintained in this repository.\n\n![Android CI](https://github.com/massivemadness/Fragula/workflows/Android%20CI/badge.svg) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Fragula-red.svg?style=flat)](https://android-arsenal.com/details/1/8405)\n\n|                                                             Dark Theme                                                              |                                                                Light Theme                                                                |\n|:-----------------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------:|\n| \u003cimg src=\"https://raw.githubusercontent.com/massivemadness/Fragula/master/.github/images/showcase.gif\" align=\"center\" width=\"70%\"/\u003e | \u003cimg src=\"https://raw.githubusercontent.com/massivemadness/Fragula/master/.github/images/showcase_light.gif\" align=\"center\" width=\"70%\"/\u003e |\n\n---\n\n# Table of Contents\n\n## Fragments\n\n1. [Gradle Dependency](#gradle-dependency)\n2. [The Basics](#the-basics)\n3. [More Options](#more-options)\n   1. [Navigate with arguments](#navigate-with-arguments)\n   2. [Multiple BackStacks](#multiple-backstacks)\n4. [Swipe Direction](#swipe-direction)\n5. [Swipe Transitions](#swipe-transitions)\n6. [Theming](#theming)\n\n## Jetpack Compose\n1. [Gradle Dependency](#gradle-dependency-1)\n2. [The Basics](#the-basics-1)\n3. [More Options](#more-options-1)\n   1. [Navigate with arguments](#navigate-with-arguments-1)\n   2. [Multiple BackStacks](#multiple-backstacks)\n4. [Customization](#customization)\n\n---\n\n# Fragments\n\nThe `fragula-core` module provides everything you need to get started with the library. \nIt contains all core and normal-use functionality.  \n\n[![MavenCentral](https://img.shields.io/maven-central/v/com.fragula2/fragula-core?label=Download\u0026color=blue)](https://repo1.maven.org/maven2/com/fragula2/fragula-core/)\n\n\u003cimg src=\"https://raw.githubusercontent.com/massivemadness/Fragula/develop/.github/images/carbon.png\" width=\"700\" /\u003e\n\n## Gradle Dependency\n\nAdd this to your module’s `build.gradle` file:\n\n```gradle\ndependencies {\n  ...\n  implementation 'com.fragula2:fragula-core:2.10.1'\n}\n```\n\nThe `fragula-core` module **does not** provide support for jetpack compose, you need to add the\n`fragula-compose` dependency in your project.\n\n---\n\n## The Basics\n\n**First,** you need to replace `NavHostFragment` with `FragulaNavHostFragment` in your layout:\n\n```xml\n\u003c!-- activity_main.xml --\u003e\n\u003candroidx.fragment.app.FragmentContainerView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:name=\"com.fragula2.FragulaNavHostFragment\" \n    android:id=\"@+id/nav_host\"\n    app:navGraph=\"@navigation/nav_graph\"\n    app:defaultNavHost=\"true\" /\u003e\n```\n\n**Second,** you need to replace your `\u003cfragment\u003e` destinations in graph with `\u003cswipeable\u003e` as shown below:\n\n```xml\n\u003c!-- nav_graph.xml --\u003e\n\u003cnavigation xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/nav_graph\"\n    app:startDestination=\"@id/detailFragment\"\u003e\n\n    \u003cswipeable\n        android:id=\"@+id/detailFragment\"\n        android:name=\"com.example.fragula.DetailFragment\"\n        android:label=\"DetailFragment\"\n        tools:layout=\"@layout/fragment_detail\" /\u003e\n\n    ...\n    \n\u003c/navigation\u003e\n```\n\n**Finally**, you need to set opaque background to your fragment’s root layout \nto avoid any issues with swipe animation.\n\n```xml\n\u003c!-- fragment_detail.xml --\u003e\n\u003candroidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"?android:colorBackground\"\n    android:layoutDirection=\"local\"\u003e\n    \n    ...\n    \n\u003c/androidx.constraintlayout.widget.ConstraintLayout\u003e\n```\n\nThat's it! No need to worry about gestures, animations and switching the navigation framework \nyou already use in your project.\n\n---\n\n## More Options\n\n### Navigate with arguments\n\nIn general, you should work with Fragula as if you would work with normal fragments. You should \nstrongly prefer passing only the minimal amount of data between destinations, as the total space\nfor all saved states is limited on Android.\n\n**First**, add an argument to the destination:\n\n```xml\n\u003cswipeable \n    android:id=\"@+id/detailFragment\"\n    android:name=\"com.example.fragula.DetailFragment\"\u003e\n     \u003cargument\n         android:name=\"itemId\"\n         app:argType=\"string\" /\u003e\n \u003c/swipeable\u003e\n```\n\n**Second**, create a Bundle object and pass it to the destination using `navigate()` as shown below: \n\n```kotlin\nval bundle = bundleOf(\"itemId\" to \"123\")\nfindNavController().navigate(R.id.detailFragment, bundle)\n```\n\n**Finally**, in your receiving destination’s code, use the `getArguments()` method to retrieve the\nBundle and use its contents:\n\n```kotlin\nval textView = view.findViewById\u003cTextView\u003e(R.id.textViewItemId)\ntextView.text = arguments?.getString(\"itemId\")\n```\n\nIt's strongly recommended to use [Safe Args](https://developer.android.com/jetpack/androidx/releases/navigation#safe_args)\nplugin for navigating and passing data, because it ensures type-safety.\n\n### Multiple BackStacks\n\nCurrently multiple backstacks is **not supported**, which means you can’t safely use extensions such \nas `setupWithNavController(...)` without losing your current backstack. \n\nThis issue affects both `BottomNavigationView` and `NavigationView` widgets.\n\n---\n\n## Swipe Direction\n\nIf you want to change the direction of swipe gesture, you can do that by setting\n`app:swipeDirection=\"...\"` manually in your navigation container. This example below sets up\nvertical swipe direction.\n\n```xml\n\u003c!-- activity_main.xml --\u003e\n\u003candroidx.fragment.app.FragmentContainerView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:name=\"com.fragula2.FragulaNavHostFragment\" \n    android:id=\"@+id/nav_host\" \n    app:swipeDirection=\"top_to_bottom\"\n    app:navGraph=\"@navigation/nav_graph\"\n    app:defaultNavHost=\"true\" /\u003e\n```\n\nYou can use either `left_to_right` (default) or `right_to_left` for horizontal direction.\nFor vertical direction you can use only `top_to_bottom`, the `bottom_to_top` is not supported due \nto internal ViewPager2 restrictions.\n\n\u003e **Note**\n\u003e If you having an issues with nested scrollable views, this appears to be a \n[scroll issue](https://developer.android.com/training/animation/vp2-migration#nested-scrollables) \nin ViewPager2. Please follow Google’s example to solve this.\n\n---\n\n## Swipe Transitions\n\nYou may want to know when the scrolling offset changes to make smooth transitions inside your \nfragment view. To start listening scroll events you need to retrieve `SwipeController` and set \n`OnSwipeListener` as shown below:\n\n```kotlin\nclass DetailFragment : Fragment(R.layout.fragment_detail) {\n   \n    private lateinit var swipeController: SwipeController\n    private lateinit var swipeListener: OnSwipeListener\n    \n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        // ...\n        swipeController = findSwipeController()\n        swipeListener = OnSwipeListener { position, positionOffset, positionOffsetPixels -\u003e\n            // TODO animate views using `positionOffset` or `positionOffsetPixels`.\n            //  the `position` points to the position of the fragment in backstack\n        }\n        swipeController.addOnSwipeListener(swipeListener)\n    }\n   \n    override fun onDestroyView() {\n        super.onDestroyView()\n        swipeController.removeOnSwipeListener(swipeListener)\n    }\n}\n```\n\n\u003e **Note**\n\u003e Currently shared element transitions between destinations are not supported in any form.\n\u003e Remember that you must remove the listener when the fragment view is destroyed.\n\n---\n\n## Theming\n\nIn most of the cases there is no need to change any values, but if you wish to override these, \nthere are attributes provided:\n\n```xml\n\u003cstyle name=\"AppTheme\" parent=\"Theme.MaterialComponents.Light.NoActionBar\"\u003e\n    \u003citem name=\"colorPrimary\"\u003e...\u003c/item\u003e\n    \u003citem name=\"colorPrimaryDark\"\u003e...\u003c/item\u003e\n    \u003citem name=\"colorAccent\"\u003e...\u003c/item\u003e\n\n    \u003c!--\n        This overrides the color used for the dimming when fragment is being dragged.\n        The default value is #000000 for both light and dark themes.\n    --\u003e\n    \u003citem name=\"fgl_scrim_color\"\u003e#000000\u003c/item\u003e\n\n    \u003c!--\n        This overrides the amount of dimming when fragment is being dragged.\n        Think of it as a `fgl_scrim_color` alpha multiplier.\n    --\u003e\n    \u003citem name=\"fgl_scrim_amount\"\u003e0.15\u003c/item\u003e\n\n    \u003c!--\n        This overrides the parallax multiplier when fragment is being dragged.\n        It determines how much the underneath fragment will be shifted\n        relative to the visible fragment (that is being dragged).\n    --\u003e\n    \u003citem name=\"fgl_parallax_factor\"\u003e1.3\u003c/item\u003e\n   \n    \u003c!--\n        This overrides the duration of swipe animation using `navController.navigate(...)` \n        and `navController.popBackStack()` methods.\n    --\u003e\n    \u003citem name=\"fgl_anim_duration\"\u003e500\u003c/item\u003e\n   \n    \u003c!--\n        This overrides the elevation applied to the fragment view.\n        Note that it doesn't support `android:outlineAmbientShadowColor`\n        and `android:outlineSpotShadowColor` attributes in your theme.\n    --\u003e\n    \u003citem name=\"fgl_elevation\"\u003e3dp\u003c/item\u003e\n   \n\u003c/style\u003e\n```\n\n---\n\n# Jetpack Compose β\n\nThe `fragula-compose` module provides support for jetpack compose.\nIt may not contain all the features described earlier. If you want to make a feature request, \nconsider creating an issue on GitHub.  \n\n[![MavenCentral](https://img.shields.io/maven-central/v/com.fragula2/fragula-compose?label=Download\u0026color=red)](https://repo1.maven.org/maven2/com/fragula2/fragula-compose/)\n\n\u003cimg src=\"https://raw.githubusercontent.com/massivemadness/Fragula/develop/.github/images/carbon_compose.png\" width=\"700\" /\u003e\n\n## Gradle Dependency\n\nAdd this to your module’s `build.gradle` file:\n\n```gradle\ndependencies {\n  ...\n  implementation 'com.fragula2:fragula-compose:2.10.1'\n}\n```\n\nThe `fragula-compose` module **does not** provide support for fragments, you need to add the\n`fragula-core` dependency in your project.\n\n---\n\n## The Basics\n\n**First,** you need to replace `NavHost(...)` with `FragulaNavHost(...)` in your main composable:\n\n```kotlin\n// MainActivity.kt\nsetContent {\n    AppTheme {\n        Surface(\n           modifier = Modifier.fillMaxSize(), \n           color = MaterialTheme.colors.background,\n        ) {\n            val navController = rememberFragulaNavController()\n            FragulaNavHost(navController, startDestination = \"list\") {\n                // ...\n            }\n        }\n    }\n}\n```\n\n**Second,** you need to replace your `composable(...)` destinations with `swipeable(...)` as shown below:\n\n```kotlin\nval navController = rememberFragulaNavController()\nFragulaNavHost(navController, startDestination = \"list\") {\n    swipeable(\"list\") {\n        ListScreen(navController)\n    }\n    swipeable(\"details\") {\n        DetailsScreen(navController)\n    }\n}\n```\n\n**Finally**, you need to set opaque background to your composables to avoid any issues with swipe animation.\n\n```kotlin\n@Composable\nfun DetailsScreen(navController: NavController) {\n    Box(modifier = Modifier.fillMaxSize()\n       .background(Color.White)\n    ) {\n        // TODO content\n    }\n}\n```\n\n---\n\n## More Options\n\n### Navigate with arguments\n\nFragula also supports passing arguments between composable destinations the same way as in the\nandroidx navigation library. Create a deeplink and specify the argument type, then you can extract \n`NavArguments` from the `NavBackStackEntry` that is available in the lambda of the `swipeable()` \nfunction.\n\n```kotlin\nNavHost(startDestination = \"profile/{userId}\") {\n    // ...\n    swipeable(\"profile/{userId}\", arguments = listOf(\n        navArgument(\"userId\") { type = NavType.StringType }\n    )) { backStackEntry -\u003e\n        ProfileScreen(navController, backStackEntry.arguments?.getString(\"userId\"))\n    }\n}\n```\n\nTo pass the argument to the destination, simply call `navController.navigate(\"profile/user1234\")`.\nFor more information read the article [Navigating with Compose](https://developer.android.com/jetpack/compose/navigation)\non official android developers website.\n\n### Multiple BackStacks\n\nAs already have been mentioned, Fragula **doesn't support** multiple backstacks both in XML and Compose.\nIf you really need this feature in your app, consider creating a nested `NavHost` for bottom tabs only.\n\n---\n\n## Customization\n\nIf you'd like to discover more customization features, here is parameters list.\n\n```kotlin\n@Composable\nfun FragulaNavHost(\n    navController: NavHostController,\n    startDestination: String,\n    modifier: Modifier = Modifier,\n    route: String? = null,\n    onPageScrolled: (Int, Float, Int) -\u003e Unit, // Scroll listener (position, offset, offsetPixels)\n    swipeDirection: SwipeDirection = SwipeDirection.LEFT_TO_RIGHT, // Scroll direction\n    scrollable: Boolean = true, // Controls user's scrolling\n    scrimColor: Color = ScrimColor, // Color used for the dimming\n    scrimAmount: Float = 0.15f, // Percentage of dimming (depends on drag offset)\n    elevationAmount: Dp = 3.dp, // Elevation applied on the composable\n    parallaxFactor: Float = 1.3f, // Parallax multiplier (depends on drag offset)\n    animDurationMs: Int = 500, // Duration of swipe animation\n    builder: NavGraphBuilder.() -\u003e Unit\n) {\n    // ...\n}\n```\n","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmassivemadness%2FFragula","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmassivemadness%2FFragula","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmassivemadness%2FFragula/lists"}