{"id":17193588,"url":"https://github.com/shaishavgandhi/navigator","last_synced_at":"2025-04-13T20:11:37.410Z","repository":{"id":57727067,"uuid":"132220243","full_name":"ShaishavGandhi/navigator","owner":"ShaishavGandhi","description":"Annotation processor that eliminates navigation and Bundle boilerplate","archived":false,"fork":false,"pushed_at":"2019-07-21T18:05:43.000Z","size":7347,"stargazers_count":13,"open_issues_count":6,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T10:46:52.539Z","etag":null,"topics":["activity","android","annotation-processor","boilerplate","bundle","fragments","java","navigation"],"latest_commit_sha":null,"homepage":"","language":"Java","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/ShaishavGandhi.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}},"created_at":"2018-05-05T06:10:27.000Z","updated_at":"2020-07-05T14:35:20.000Z","dependencies_parsed_at":"2022-09-15T01:33:18.513Z","dependency_job_id":null,"html_url":"https://github.com/ShaishavGandhi/navigator","commit_stats":null,"previous_names":["shaishavgandhi05/navigator"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaishavGandhi%2Fnavigator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaishavGandhi%2Fnavigator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaishavGandhi%2Fnavigator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShaishavGandhi%2Fnavigator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ShaishavGandhi","download_url":"https://codeload.github.com/ShaishavGandhi/navigator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248774980,"owners_count":21159534,"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":["activity","android","annotation-processor","boilerplate","bundle","fragments","java","navigation"],"created_at":"2024-10-15T01:44:31.584Z","updated_at":"2025-04-13T20:11:37.383Z","avatar_url":"https://github.com/ShaishavGandhi.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Navigator\n\n\n![CircleCI branch](https://img.shields.io/circleci/project/github/ShaishavGandhi/navigator/master.svg)\n\n\u003cimg src=\"assets/navigator-logo.png\" width=\"275px\"/\u003e\n\n\n\nUtility library that generates activity navigation boilerplate for you, along with all it's bindings. \n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.shaishavgandhi.navigator/navigator.svg)](https://mvnrepository.com/artifact/com.shaishavgandhi.navigator/navigator)\n\n```groovy\ndependencies {\n  implementation 'com.shaishavgandhi.navigator:navigator:x.y.z'\n  annotationProcessor 'com.shaishavgandhi.navigator:navigator-compiler:x.y.z'\n  \n  // Or if using Kotlin\n  kapt 'com.shaishavgandhi.navigator:navigator-compiler:x.y.z'\n}\n```\nSnapshots of the development version are available in [Sonatype's snapshots repository.](https://oss.sonatype.org/content/repositories/snapshots/)\n\n## Use Case\n\nNavigating to another activity requires a lot of boilerplate code in both activites. \n\nSource activity:\n```java\npublic final class MainActivity extends Activity {\n  \n  protected void openDetailActivity() {\n    Intent intent = new Intent(context, DetailActivity.class);\n    intent.putParcelableExtra(\"user\", user);\n    intent.putString(\"source\", source);\n    intent.putString(\"title\", title);\n    intent.putString(\"subtitle\", subtitle);\n    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK || Intent.FLAG_ACTIVITY_CLEAR_TASK);\n    startActivity(intent);\n  }\n\n}\n```\n\nThe destination activity is even more complicated:\n\n```java\n\npublic final class DetailActivity extends Activity {\n  \n  String title;\n  String source;\n  String subtitle;\n \n  @Override protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    parseIntent();\n  }\n  \n  private void parseIntent() {\n    if (getIntent() != null \u0026\u0026 getIntent().getExtras() != null) {\n      Bundle bundle = getIntent().getExtras();\n      if (bundle.containsKey(\"title\")) {\n        title = bundle.getString(\"title\");\n      }\n      \n      // So on for every attribute\n    \n    }\n  }\n}\n\n```\n\nThere are lots of things that can go wrong with this. \n1. There is no type safety.\n2. There is no implicit contract between any two activities which state what is required and what is optional.\n3. Everything is nullable and that's not good. \n\n## Usage\n\nNavigator provides a simple builder API, that provides an implicit contract between the source and destination activites, as well as removes the binding boilerplate for you.\n\nYou only need to annotate your fields with `@Extra` in your destination activity and Navigator will generate a builder API for you to start that activity. \n\nBy default, Navigator will treat all fields with `@Extra` as necessary and required for the destination activity to start. Fields that are not _required_ by the activity but might be expected from some places can be annotated with `@Nullable` from android support-annotations. \n\nFields annotated with `@Extra` must be public or package-private. If the fields are private, then you must provide a setter for them that will be used by Navigator to bind the data.\n\nUsing the same example:\n\n```java\n\npublic final class DetailActivity extends Activity {\n  \n  @Extra String title; // Annotate with @Extra to tell Navigator that this is required when opening activity\n  @Extra @Nullable String source; // @Nullable tells Navigator that this is an optional extra\n  @Extra String subtitle;\n \n  @Override protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    DetailActivityBinder.bind(this); // Automatically bind extras\n  }\n}\n```\n\n```java\npublic final class MainActivity extends Activity {\n  \n  protected void openDetailActivity() {\n    DetailActivityBuilder.builder(title, subtitle) // Required extras by ActivityC go in static factory method\n      .setSource(source) // optional extras\n      .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK || Intent.FLAG_ACTIVITY_CLEAR_TASK)\n      .start(this);\n  }\n\n}\n```\n\nThe sample example would work in Kotlin as well. \n\n## Fragments\n\nThe same examples mentioned above work for fragments as well. However, **Navigator is not interested in being a navigation library for fragments**. A whole different library can be written about that. \n\nNavigator does support binding of arguments passed to the fragment as well as constructing the arguments required for a fragment in an API that is very similar to Activities. \n\n#### Get arguments\n\n```java\nBundle arguments = DetailFragmentBuilder.builder(userList)\n                .setPoints(points)\n                .getBundle();\n\nMyFragment fragment = new MyFragment();\nfragment.setArguments(arguments);\n```\n\n#### Bind arguments\n\n```java\nclass DetailFragment extends Fragment {\n\n    @Extra User user;\n    @Extra Point points;\n\n    @Override public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        DetailFragmentBinder.bind(this);\n    }\n\n}\n```\n\n#### Alternative To SafeArgs\n\nIf you've used the Jetpack Navigation library from Google, you might have used SafeArgs to safely pass data between two fragments. Navigator seems to have a better approach than SafeArgs since you can bind all your Fragment variables in one go instead of actually getting them individually like in SafeArgs. You can also use handy Kotlin extensions that make the API better. More on that in the next section.\n\n## Kotlin\n\nNavigator has first class support for Kotlin and it's language features. If you use `kapt` as your annotation processor, \nNavigator will generate handy Kotlin extensions for you which simplify the API.\n\n\n#### Bind arguments\n\n```kotlin\n\nclass DetailActivity : Activity() {\n  \n  @Extra lateinit var title: String \n  @Extra var source: String? = null // null type indicates that it is optional\n  @Extra lateinit var subtitle: String\n \n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    bind() // Simply call bind extension on DetailActivity\n  }\n}\n```\n\nUsing kapt will also simplify your API when using it in a Java class. \n\n```java\npublic class DetailActivity extends Activity {\n    \n    @Extra String message;\n    \n    @Override protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        DetailActivityNavigator.bind(this); // Generated kotlin extension with a nicer API\n    }\n    \n}\n```\n\n#### Creating Builder\n\nYou can also use a handy Kotlin extension to get rid of the static factory builders. If you're preparing `Bundle` for DetailFragment, you can do:\n```kotlin\nclass MainFragment: Fragment() {\n  \n  fun showDetail(post: Post, authors: List\u003cAuthor\u003e?) {\n    val bundle = detailFragmentBuilder(post)\n                   .setAuthors(authors)\n                   \n    val fragment = DetailFragment()\n    fragment.setArguments(bundle)\n    replaceFragment(fragment)\n  }\n\n}\n```\n\n## Advanced Usage\n\nNavigator exposes most ways to start an activity. \n\n#### Start Activity For Result\n\n```kotlin\nDetailActivityBuilder.builder(users, source)\n               .setPoints(points)\n               .startForResult(activity, requestCode)\n```\n\n#### Start Activity With Transition Bundle\n```kotlin\nDetailActivityBuilder.builder(users, source)\n               .setPoints(points)\n               .startWithExtras(activity, transitionBundle)\n```\n\n#### Supply your own keys\n\nIt is easy to transition to Navigator in a large codebase. For example, you can mark a variable \nas `@Extra` in an existing class, which already has logic to parse out the Bundle. But you cannot\n possibly change the key to that particular variable in every place from which it's called. With \n Navigator, you can easily specify your own custom key which will be used to execute the binding \n of all the extras. Example:\n \n ```java\n public final class MyActivity extends Activity {\n    \n    @Extra(key = FragmentExtras.CUSTOM_KEY) // Custom key that other classes use when invoking MyActivity\n    String extra;\n    \n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        MyActivityNavigator.bind(this);\n    }\n }\n ```\n\n#### Bundle\n\nIn cases where you just want to use the type-safety and implicit contract of Navigator, you can easily use the builder to get the bundle created by Navigator\n\n```java\nBundle bundle = DetailActivityBuilder.builder(users, source)\n               .setPoints(points)\n               .getBundle();\n```\n\n## Add Ons\n\nIf you're using Kotlin, [BundleX](https://github.com/shaishavgandhi05/BundleX) is a useful add-on to Navigator. [BundleX](https://github.com/shaishavgandhi05/BundleX) generates extensions on the `Bundle` using the same `@Extra` annotation. \n\n```kotlin\nclass MyActivity: AppcompatActivity {\n  \n  @Extra lateinit var message: String\n  \n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_main)\n        \n    val bundle = intent.extras\n    message = bundle.getMessage(defaultValue = \"hello world\") // Generated extension\n  }\n  \n  fun startActivity(message: String) {\n    val bundle = Bundle()\n    bundle.putMessage(message) // Use generated setter \n    // Start activity with bundle\n  }\n    \n}\n```\n\nSimply add to your build.gradle\n```groovy\ndependencies {\n  kapt 'com.shaishavgandhi:bundlex-compiler:x.y.z'\n}\n```\n\n\n\n## Thanks\n\n* [Baran Pirincal](https://github.com/baranpirincal) for the excellent logo.\n* [Jake Wharton](https://github.com/JakeWharton) and [ButterKnife](https://github.com/JakeWharton/butterknife) for the annotation processing references. \n\n## License\n    \n    Copyright 2018 Shaishav Gandhi.\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaishavgandhi%2Fnavigator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshaishavgandhi%2Fnavigator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaishavgandhi%2Fnavigator/lists"}