Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

(Taken from Zhuinden/ExampleGithubClient)

Flowless MVP Example: "Example Github Client"

In reality, login just waits 3 seconds, and the only repository you download is "square" repository.

This is a sample to show data-domain-presentation layering, subscoping via Dagger2, and MVP architecture; in a single-Activity setup provided by flowless.

Some tests are included, although they aren't complete.

Data layer:

  • data layer is reactive, currently in-memory and doesn't actually persist itself anywhere. It should be replaced with SqlBrite + RxJava, or Realm

  • data is held by the DataSource, it is manipulated via the "Repository"

Domain layer:

  • the Service that communicates with the Github API, provided via Retrofit

  • threading is provided by Bolts-Android

  • the manipulation of data is done in the interactors

Presentation layer:

  • contains the presenters which store state and tell the view what to do when an event occurs

  • also contains the views that delegate all callbacks to their presenters


  • contains anything else that is not domain specific

  • also contains TransitionDispatcher, which is what the application uses to determine what to do on an application state change

The application is driven by the keys.

For example,

public abstract class LoginKey
        implements Parcelable {
    public static LoginKey create() {
        return new AutoValue_LoginKey();

The dispatcher creates the view and the scoped component, the view injects itself, and attaches itself to the presenter.

public class RepositoriesView
        extends RelativeLayout
        implements FlowLifecycles.ViewLifecycleListener, RepositoriesPresenter.ViewContract {

    // constructors
    public void init() {
        if(!isInEditMode()) {
            RepositoriesComponent repositoriesComponent = DaggerService.getComponent(getContext());

    RepositoriesPresenter repositoriesPresenter;
    public void onViewRestored() {

    public void onViewDestroyed(boolean removedByFlow) {

The presenter provides a view contract based on which it can call the callbacks inside the view, to bring it "up-to-date" or to navigate.

public class RepositoriesPresenter
        extends BasePresenter<RepositoriesPresenter.ViewContract> {
    public RepositoriesPresenter() {
    public interface ViewContract
            extends Presenter.ViewContract {
        void updateRepositories(List<Repository> repositories);

        void openRepository(String url);

    protected void initializeView(ViewContract view) {
        if(repositories == null || repositories.isEmpty()) {
        } else {

The interactors download the data via the service, and save it to the model. The presenters are subscribed to changes inside the model.

public class GetRepositoriesInteractorImpl
        implements GetRepositoriesInteractor {
    GithubService githubService;

    RepositoryRepository repositoryRepository;

    public GetRepositoriesInteractorImpl() {

    public Task<List<Repository>> getRepositories(final String user, int page) {
        return githubService.getRepositories(user, page).continueWith(task -> {
            if(task.isFaulted()) {
                throw task.getError();
            return repositoryRepository.saveOrUpdate(task.getResult());
You can’t perform that action at this time.