dart-parent-2.0.1 (Aug 4, 2016)
Jan 10, 2014
Jan 10, 2018
Prateek Srivastava (f2prateek)
Prateek Srivastava (f2prateek)
Anton Malinskiy (Malinskiy)
孙善明 (yaming116)
John Ericksen (johncarl81)
Iain Connor (iainconnor)
Stéphane Nicolas (stephanenicolas)
Michael Basil (intrications)
Alex Facciorusso (alexfacciorusso)
Stuart Kent (stkent)
Stephen Vinouze (StephenVinouze)
Source code
APK file

Show card


**** Version 3 of Dart & Henson is gonna be released soon in January 2018. If you are looking for the README page of Dart & Henson 2, please visit this wiki page.****

**** This page is under construction until version 3 is released. ****


Extra "binding" library & intent builders for Android. Dart & Henson (DH) uses annotation processing to bind intent's extras to pojo fields, and to generate intent builders via a fluent API.

Description of DH 3

Dart and Henson is a an Android Library that structures the navigation layer of your apps. It helps to create intents and consume them in a structured way. We believe it's the best way to organize your navigation layer, and make it less error-prone and easier to maintain.

It is composed of 2 components: Dart and Henson. Both of them use annotated classes that describe the parameters (extras of the intent) of a target activity. DH3 is not easy to setup manually, and we strongly encourage to use the gradle henson-plugin that we provide to use it. See the samples for more details.

Navigation models

A navigation model class is a simple pojo with annotated non private fields. The fields describe an extra passed to the target of an intent:

public class MyActivityNavigationModel {
  //a simple requested field, it's name is used as the extra key
  public String extra1;
  //a named field using an annotation
  public @BindExtra(MY_CONSTANT_NAME) String extra2;
  //an optional field
  public @Nullable MyParcelableOrSerializable extra3;

Note that in DH3, navigation models:

  • are mandatory, it's not possible to annotate activities directly.
  • must follow a naming convention: they should have the same fully qualified name as the activity or service they describe the navigation of, plus the suffix: NavigationModel. (e.g.: com.foo.wooper.app.MyActivityNavigationModel).
  • must be placed in the navigation source set: in src/navigation/main/java.


The historical first component of the library is used to map intents to Pojos (navigation models). Typically, a target activity will define a navigation model class, a pojo with annotated fields and will map the intents it receives to an instance of its model:

public void onCreate(Bundle savedInstanceState) {
  Dart.bind(this, myNavigationModel)

An activity (or a service) can map the extras of the intent it receives, or a bundle like savedInstanceState.


The second component of the library is used to create intents. Based on the navigation model, henson will create an intent builder for the described class (remember the name of the activity / service can be dedudced from the FQN of the model). It creates also some useful wrapper around them, see below.

Generally speaking, Intent Builders generated by Henson are not used directly, but via the HensonNavigator class that is generated for each module.

The HensonNavigator Class

The HensonNavigator is generated on a usage basis: it wraps all the intent builder that a module can use.

Intent intent = HensonNavigator.gotoMyActivity(context)
 .extra3(myObj) //optional

The intent builders used by a module are detected automatically during the build, based on the dependencies a module uses, and the HensonNavigator is generated accordingly.

What's new in DH3 ?


  • DH3 fully supports modularization. It was the main motivation for the version 3, and it requested quite a few changes. We don't have a migration guide yet.
  • navigation models have changed and are mandatory, they need a single annotation: @DartModel.
  • navigation models must follow a naming convention
  • they must be placed in a different source set in: src/navigation/main/java
  • DH3 offers a gradle plugin. DH3 uses a lot of annotation processing internally, configurations, source sets, artifacts, custom tasks. Do not set it up manually unless you know gradle well.
  • annotations have changed, the new model is simpler.
  • DH3 classes have been repackaged. Though, unfortunately, we couldn't rename the groupId of the library, and gradle doesn't let you use DH2 and DH3 simultaneously.


If ProGuard is enabled be sure to add these rules to your configuration:

-dontwarn dart.internal.**
-keep class **__ExtraBinder { *; }
-keepclasseswithmembernames class * {
    @dart.* <fields>;
-keep class **Henson { *; }
-keep class **__IntentBuilder { *; }
-keep class **HensonNavigator { *; }

#if you use it
#see Parceler's github page
#for specific proguard instructions



implementation 'com.f2prateek.dart:dart:(insert latest version)'
annotationProcessor 'com.f2prateek.dart:dart-processor:(insert latest version)'

Henson :

implementation 'com.f2prateek.dart:henson:(insert latest version)'
annotationProcessor 'com.f2prateek.dart:henson-processor:(insert latest version)'


For all Kotlin enthusiasts, you may wonder how to use this library to configure your intents. This is perfectly compatible, with a bit of understanding of how Kotlin works, especially when it comes to annotation processing.

Assuming that your project is already configured with Kotlin, update your build.gradle file :

apply plugin: 'kotlin-kapt'

dependencies {
  implementation 'com.f2prateek.dart:henson:(insert latest version)'
  kapt 'com.f2prateek.dart:henson-processor:(insert latest version)'

Now you can use @InjectExtra annotation to generate either non-null or nullables properties :

class ExampleActivity : Activity() {
  lateinit var title: String

  var titleDefaultValue: String = "Default Title"

  var titleNullable: String? = null

  override fun onCreate(savedInstanceState: Bundle?) {
      // TODO Use "injected" extras...

Note that you still need to use the Java @Nullable annotation otherwise Henson won't interpret your property as nullable and will generate a builder with a mandatory field (even though you declared your property as nullable with the "?" Kotlin marker). Finally, you have to add the @JvmField annotation or your compiler will complain about not having a backing field.

You may need to add an argument to your build.gradle file if your activities and fragments are located in different packages as mentioned above. The Kotlin syntax with kapt is :

kapt {
    arguments {
        arg("dart.henson.package", "your.package.name")

Finally, if you are using Parceler that comes built-in with this library, the syntax does not change from Java, except when dealing with data classes. Because Parceler requires a default constructor with no argument, here is how you need to declare your data class :

data class ParcelExample @ParcelConstructor constructor(
        val id: Int,
        val name: String,

Talks & Slides


Copyright 2013 Jake Wharton
Copyright 2014 Prateek Srivastava (@f2prateek)

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


Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and
limitations under the License.