bluelinelabs / bluelinelabs/Conductor

[Question] Replacing one controller with another upon configuration change

Open
#636 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
3.9k
Forks
338
PR merge metrics
No merged PRs in 30d

Description

I have a controller in which I want to insert another controller, conditionally. If the phone is in portrait layout I want to add Portrait controller, if in landscape or if an additional setting is turned on then Landscape controller. I'm doing it the following way:

```Kotlin
// onControllerCreated is basically onCreateView()
override fun onControllerCreated(savedViewState: Bundle?) {
super.onControllerCreated(savedViewState)

// AndroidUtils.isSplitMode() is basically "orientation==LANDSCAPE" but it is also manually configurable
val controller = if (AndroidUtils.isSplitMode(currentContext())) {
SplitHomeController()
} else {
HomeController()
}

val transaction = RouterTransaction.with(controller)
.tag(controller.getControllerTag().tag)

getChildRouter(contentContainer).setRoot(transaction)
}
```

I'm using the same view (contentContainer) for both controllers and also using RetainViewMode.RETAIN_DETACH for all controllers.

The issue I'm having with this approach is that upon config change the old HomeController is still in the stack, it's getting rebound and then is destroyed (after `getChildRouter(contentContainer).setRoot(transaction)`) which is expected. I'm getting the following log:

```
// Normal app start (the phone is in landscape orientation)
2020-12-04 14:46:19.995 D/BaseController: MainController onCreateView()
2020-12-04 14:46:20.005 D/BaseController: SplitHomeController onCreateView()
2020-12-04 14:46:20.216 D/BaseController: HomeController onCreateView()
2020-12-04 14:46:20.436 D/BaseController: SplitBrowseController onCreateView()
2020-12-04 14:46:20.556 D/BaseController: CatalogController onCreateView()
2020-12-04 14:46:20.624 D/BaseController: SplitThreadController onCreateView()
2020-12-04 14:46:20.679 D/BaseController: MainController onAttach()
2020-12-04 14:46:20.680 D/BaseController: SplitHomeController onAttach()
2020-12-04 14:46:20.680 D/BaseController: HomeController onAttach()
2020-12-04 14:46:20.680 D/BaseController: SplitBrowseController onAttach()
2020-12-04 14:46:20.680 D/BaseController: CatalogController onAttach()
2020-12-04 14:46:20.681 D/BaseController: SplitThreadController onAttach()

// The app start with the phone in portrait orientation and then is rotated (logs are taken when rotation occurs)
2020-12-04 14:46:35.528 D/BaseController: MainController onDetach()
2020-12-04 14:46:35.528 D/BaseController: HomeController onDetach()
2020-12-04 14:46:35.528 D/BaseController: SlideBrowseController onDetach()
2020-12-04 14:46:35.528 D/BaseController: CatalogController onDetach()
2020-12-04 14:46:35.528 D/BaseController: ThreadController onDetach()
2020-12-04 14:46:35.531 D/BaseController: MainController onDestroyView()
2020-12-04 14:46:35.531 D/BaseController: HomeController onDestroyView()
2020-12-04 14:46:35.532 D/BaseController: SlideBrowseController onDestroyView()
2020-12-04 14:46:35.532 D/BaseController: CatalogController onDestroyView()
2020-12-04 14:46:35.532 D/BaseController: ThreadController onDestroyView()
2020-12-04 14:46:35.574 D/BaseController: MainController onCreateView()
2020-12-04 14:46:35.614 D/BaseController: HomeController onCreateView()
2020-12-04 14:46:35.620 D/BaseController: SlideBrowseController onCreateView()
2020-12-04 14:46:35.626 D/BaseController: CatalogController onCreateView()
2020-12-04 14:46:35.632 D/BaseController: ThreadController onCreateView()
2020-12-04 14:46:35.670 D/BaseController: MainController onAttach()
2020-12-04 14:46:36.029 D/BaseController: SplitHomeController onCreateView()
2020-12-04 14:46:36.051 D/BaseController: HomeController onCreateView()
2020-12-04 14:46:36.055 D/BaseController: SplitBrowseController onCreateView()
2020-12-04 14:46:36.057 D/BaseController: CatalogController onCreateView()
2020-12-04 14:46:36.065 D/BaseController: SplitThreadController onCreateView()
2020-12-04 14:46:36.068 D/BaseController: HomeController onDestroyView()
2020-12-04 14:46:36.069 D/BaseController: SlideBrowseController onDestroyView()
2020-12-04 14:46:36.069 D/BaseController: CatalogController onDestroyView()
2020-12-04 14:46:36.069 D/BaseController: ThreadController onDestroyView()
2020-12-04 14:46:36.072 D/BaseController: SplitHomeController onAttach()
2020-12-04 14:46:36.072 D/BaseController: HomeController onAttach()
2020-12-04 14:46:36.072 D/BaseController: SplitBrowseController onAttach()
2020-12-04 14:46:36.072 D/BaseController: CatalogController onAttach()
2020-12-04 14:46:36.073 D/BaseController: SplitThreadController onAttach()
```

While everything seems to be working I wonder if there is an official way to handle this situation (without old controller getting recteated)? Maybe I can somehow remove this controller during the call `Conductor.attachRouter(this, rootContainer, savedInstanceState)` before setting the root controller onto the main router?

I saw the MasterDetailController demo and that it uses two containers when the phone is in landscape orientation and two child routers but I wonder whether it's possible to only have one container.

Contributor guide

Open the contributing guide

Research direction

Start with Conductor.attachRouter(this, rootContainer, savedInstanceState), the child router's setRoot(transaction), RetainViewMode.RETAIN_DETACH, and the MasterDetailController demo. Trace restoration and configuration-change behavior for a single container, then determine whether replacing the child controller without recreation is supported. Done means documenting the supported approach or confirming that the observed lifecycle is expected.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.