Convert Figma logo to code with AI

bluelinelabs logoConductor

A small, yet full-featured framework that allows building View-based Android applications

3,897
345
3,897
113

Top Related Projects

2,786

Name UI states, navigate between them, remember where you've been.

5,490

A Model-View-Presenter / Model-View-Intent library for modern Android apps

Mavericks: Android on Autopilot

7,733

Uber's cross-platform mobile architecture framework.

5,683

A sample Android app which showcases advanced usage of Dagger among other open source libraries.

Quick Overview

Conductor is a small, yet full-featured framework for building Android applications. It provides a lightweight alternative to Fragments, offering improved lifecycle management, custom view-based navigation, and easy-to-use transitions between screens.

Pros

  • Simplified lifecycle management compared to Fragments
  • Easy and flexible navigation between screens
  • Smooth and customizable transitions
  • Lightweight and easy to integrate into existing projects

Cons

  • Learning curve for developers accustomed to Fragment-based architecture
  • Limited community resources compared to the standard Android framework
  • May require additional effort to integrate with some third-party libraries designed for Fragments
  • Not officially supported by Google or part of the Android framework

Code Examples

  1. Creating a basic Controller:
class HomeController : Controller() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
        return inflater.inflate(R.layout.controller_home, container, false)
    }
}
  1. Navigating to a new Controller:
router.pushController(RouterTransaction.with(DetailController())
    .pushChangeHandler(HorizontalChangeHandler())
    .popChangeHandler(HorizontalChangeHandler()))
  1. Handling back navigation:
override fun onBackPressed(): Boolean {
    if (router.backstackSize > 1) {
        router.handleBack()
        return true
    }
    return super.onBackPressed()
}
  1. Using ControllerChangeHandler for custom transitions:
class FadeChangeHandler : ControllerChangeHandler() {
    override fun performChange(
        container: ViewGroup,
        from: View?,
        to: View?,
        isPush: Boolean,
        changeListener: ControllerChangeCompletedListener
    ) {
        if (to != null) {
            container.addView(to)
            to.alpha = 0f
            to.animate().alpha(1f).setDuration(300).start()
        }
        if (from != null) {
            from.animate().alpha(0f).setDuration(300).withEndAction {
                container.removeView(from)
                changeListener.onChangeCompleted()
            }.start()
        } else {
            changeListener.onChangeCompleted()
        }
    }
}

Getting Started

  1. Add Conductor to your project's build.gradle:
dependencies {
    implementation 'com.bluelinelabs:conductor:3.1.5'
}
  1. Set up your Activity to use Conductor:
class MainActivity : AppCompatActivity() {
    private lateinit var router: Router

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        router = Conductor.attachRouter(this, findViewById(R.id.controller_container), savedInstanceState)
        if (!router.hasRootController()) {
            router.setRoot(RouterTransaction.with(HomeController()))
        }
    }

    override fun onBackPressed() {
        if (!router.handleBack()) {
            super.onBackPressed()
        }
    }
}

Competitor Comparisons

2,786

Name UI states, navigate between them, remember where you've been.

Pros of Flow

  • Simpler API with a focus on single-Activity applications
  • Lightweight and minimalistic approach to navigation
  • Better integration with Kotlin coroutines

Cons of Flow

  • Less flexibility for complex navigation scenarios
  • Limited support for nested navigation and backstack management
  • Fewer built-in features compared to Conductor

Code Comparison

Flow:

class MyScreen : Screen {
    override fun createView(context: Context) =
        TextView(context).apply { text = "Hello, Flow!" }
}

flow.set(MyScreen())

Conductor:

class MyController : Controller() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
        return TextView(container.context).apply { text = "Hello, Conductor!" }
    }
}

router.setRoot(RouterTransaction.with(MyController()))

Both Flow and Conductor aim to simplify Android navigation, but they take different approaches. Flow focuses on simplicity and single-Activity applications, while Conductor offers more flexibility and features for complex navigation scenarios. Flow's API is generally more concise, but Conductor provides more built-in functionality for managing the backstack and nested navigation. The choice between the two depends on the specific requirements of your project and personal preferences.

5,490

A Model-View-Presenter / Model-View-Intent library for modern Android apps

Pros of Mosby

  • Implements the Model-View-Presenter (MVP) pattern, providing a clear separation of concerns
  • Offers a robust solution for handling view state and preventing memory leaks
  • Includes built-in support for RxJava, making it easier to work with reactive programming

Cons of Mosby

  • Steeper learning curve due to its more complex architecture
  • May introduce more boilerplate code compared to simpler solutions
  • Less flexible for non-MVP architectures or custom implementations

Code Comparison

Mosby (MVP implementation):

public class MyPresenter extends MvpBasePresenter<MyView> {
    public void loadData() {
        // Perform data loading
        getView().showData(data);
    }
}

Conductor (Controller implementation):

public class MyController extends Controller {
    @Override
    protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
        View view = inflater.inflate(R.layout.my_layout, container, false);
        // Set up view and handle logic
        return view;
    }
}

Mosby focuses on MVP architecture, providing a structured approach to separating concerns and managing view state. Conductor, on the other hand, offers a more flexible solution for building Android UIs with a focus on navigation and lifecycle management. The choice between the two depends on the specific needs of the project and the preferred architectural approach.

Mavericks: Android on Autopilot

Pros of Mavericks

  • Built specifically for Kotlin, leveraging its features and syntax
  • Integrates well with Jetpack Compose for modern UI development
  • Provides a robust state management system with built-in error handling

Cons of Mavericks

  • Steeper learning curve, especially for developers new to MVI architecture
  • More opinionated, which may limit flexibility in some scenarios
  • Requires more boilerplate code for simple use cases

Code Comparison

Conductor (Java):

public class MainController extends Controller {
    @Override
    protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
        View view = inflater.inflate(R.layout.controller_main, container, false);
        // Initialize views and set up logic
        return view;
    }
}

Mavericks (Kotlin):

class MainViewModel(state: MyState) : MavericksViewModel<MyState>(state) {
    fun updateData() = setState { copy(data = "New Data") }
}

@Composable
fun MainScreen() {
    val viewModel: MainViewModel = mavericksViewModel()
    // Use viewModel state and actions in Composables
}
7,733

Uber's cross-platform mobile architecture framework.

Pros of RIBs

  • Provides a more comprehensive architecture for large-scale apps
  • Offers better support for deep linking and state restoration
  • Includes built-in dependency injection and scoping

Cons of RIBs

  • Steeper learning curve and more complex implementation
  • May be overkill for smaller projects
  • Less flexibility in UI management compared to Conductor

Code Comparison

Conductor:

class MainController : Controller() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
        return inflater.inflate(R.layout.controller_main, container, false)
    }
}

RIBs:

class MainRouter(
    interactor: MainInteractor,
    component: MainBuilder.Component
) : ViewRouter<MainView, MainInteractor, MainBuilder.Component>(interactor, component)

Both Conductor and RIBs aim to improve Android app architecture, but they take different approaches. Conductor focuses on simplifying navigation and view management, while RIBs provides a more comprehensive architecture for large-scale applications. Conductor is generally easier to adopt and more flexible for UI management, making it suitable for a wider range of projects. RIBs, on the other hand, offers more robust features for complex apps, including built-in dependency injection and better support for deep linking. The choice between the two depends on the project's scale, complexity, and specific requirements.

5,683

A sample Android app which showcases advanced usage of Dagger among other open source libraries.

Pros of u2020

  • Demonstrates advanced usage of Dagger for dependency injection
  • Showcases integration with popular libraries like Retrofit and RxJava
  • Provides a comprehensive example of app architecture and testing practices

Cons of u2020

  • Less focused on navigation and view management compared to Conductor
  • May be more complex for beginners due to its advanced architecture
  • Not actively maintained (last update was several years ago)

Code Comparison

u2020 (Activity setup)

public final class U2020App extends Application {
  @Override public void onCreate() {
    super.onCreate();
    Timber.plant(new Timber.DebugTree());
    ObjectGraph objectGraph = ObjectGraph.create(Modules.list(this));
    objectGraph.inject(this);
  }
}

Conductor (Controller setup)

public class HomeController extends Controller {
    @Override
    protected View onCreateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) {
        View view = inflater.inflate(R.layout.controller_home, container, false);
        return view;
    }
}

The code snippets demonstrate the different focus areas of each library. u2020 emphasizes dependency injection and application-level setup, while Conductor focuses on view management and navigation through its Controller system.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

GitHub Actions Workflow Android Arsenal Javadocs

Conductor

A small, yet full-featured framework that allows building View-based Android applications. Conductor provides a light-weight wrapper around standard Android Views that does just about everything you'd want:

Conductor
:tada:Easy integration
:point_up:Single Activity apps without using Fragments
:recycle:Simple but powerful lifecycle management
:train:Navigation and backstack handling
:twisted_rightwards_arrows:Beautiful transitions between views
:floppy_disk:State persistence
:phone:Callbacks for onActivityResult, onRequestPermissionsResult, etc
:european_post_office:MVP / MVVM / MVI / VIPER / MVC ready

Conductor is architecture-agnostic and does not try to force any design decisions on the developer. We here at BlueLine Labs tend to use either MVP or MVVM, but it would work equally well with standard MVC or whatever else you want to throw at it.

Installation

Conductor 4.0 is coming soon. It is already being used in production with many, many millions of users. It is, however, not guaranteed to be API stable. As such, it is being released as a preview rather than a standard release. Preview in this context is not a commentary on stability. It is considered to be up to the same quality standards as the current 3.x stable release. Changes in Conductor 4 are available in the GitHub releases. In preparation for the release of the next version, there are currently 3 installation options:

Latest Stable 3.x

def conductorVersion = '3.2.0'

implementation "com.bluelinelabs:conductor:$conductorVersion"

// AndroidX Transition change handlers:
implementation "com.bluelinelabs:conductor-androidx-transition:$conductorVersion"

// ViewPager PagerAdapter:
implementation "com.bluelinelabs:conductor-viewpager:$conductorVersion"

// ViewPager2 Adapter:
implementation "com.bluelinelabs:conductor-viewpager2:$conductorVersion"

4.0 Preview

Use 4.0.0-preview-4 as your version number in any of the dependencies above.

SNAPSHOT

Use 4.0.0-SNAPSHOT as your version number in any of the dependencies above and add the url to the snapshot repository:

allprojects {
  repositories {
    maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
  }
}

Components to Know

Conductor Components
ControllerThe Controller is the View wrapper that will give you all of your lifecycle management features. Think of it as a lighter-weight and more predictable Fragment alternative with an easier to manage lifecycle.
RouterA Router implements navigation and backstack handling for Controllers. Router objects are attached to Activity/containing ViewGroup pairs. Routers do not directly render or push Views to the container ViewGroup, but instead defer this responsibility to the ControllerChangeHandler specified in a given transaction.
ControllerChangeHandlerControllerChangeHandlers are responsible for swapping the View for one Controller to the View of another. They can be useful for performing animations and transitions between Controllers. Several default ControllerChangeHandlers are included.
RouterTransactionTransactions are used to define data about adding Controllers. RouterTransactions are used to push a Controller to a Router with specified ControllerChangeHandlers, while ChildControllerTransactions are used to add child Controllers.

Getting Started

Minimal Activity implementation

class MainActivity : AppCompatActivity() {

    private lateinit var router: Router

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)

      setContentView(R.layout.activity_main)

      val container = findViewById<ViewGroup>(R.id.controller_container)

      router = Conductor.attachRouter(this, binding.controllerContainer, savedInstanceState)
        .setPopRootControllerMode(PopRootControllerMode.NEVER)
        .setOnBackPressedDispatcherEnabled(true)

      if (!router.hasRootController()) {
        router.setRoot(RouterTransaction.with(HomeController()))
      }
    }
}

Minimal Controller implementation

class HomeController : Controller() {

  override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup,
    savedViewState: Bundle?
  ): View {
    val view = inflater.inflate(R.layout.controller_home, container, false)
    view.findViewById<TextView>(R.id.tv_title).text = "Hello World"
    return view  
  }
}

Sample Project

Demo app - Shows how to use all basic and most advanced functions of Conductor.

Controller Lifecycle

The lifecycle of a Controller is significantly simpler to understand than that of a Fragment. A lifecycle diagram is shown below:

Controller Lifecycle

Advanced Topics

Retain View Modes

setRetainViewMode can be called on a Controller with one of two values: RELEASE_DETACH, which will release the Controller's view as soon as it is detached from the screen (saves memory), or RETAIN_DETACH, which will ensure that a Controller holds on to its view, even if it's not currently shown on the screen (good for views that are expensive to re-create).

Custom Change Handlers

ControllerChangeHandler can be subclassed in order to perform different functions when changing between two Controllers. Two convenience ControllerChangeHandler subclasses are included to cover most basic needs: AnimatorChangeHandler, which will use an Animator object to transition between two views, and TransitionChangeHandler, which will use Lollipop's Transition framework for transitioning between views.

Child Routers & Controllers

getChildRouter can be called on a Controller in order to get a nested Router into which child Controllers can be pushed. This enables creating advanced layouts, such as Master/Detail.

RxJava Lifecycle

If the AutoDispose dependency has been added, there is a ControllerScopeProvider available that can be used along with the standard AutoDispose library.

Community Projects

The community has provided several helpful modules to make developing apps with Conductor even easier. Here's a collection of helpful libraries:

License

Copyright 2020 BlueLine Labs, Inc.

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.