All Projects → Zhuinden → Simple Stack

Zhuinden / Simple Stack

Licence: apache-2.0
[ACTIVE] Simple Stack, a backstack library / navigation framework for simpler navigation and state management (for fragments, views, or whatevers).

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to Simple Stack

Flowr
FlowR is a wrapper class around the Fragment Manager.
Stars: ✭ 123 (-87.85%)
Mutual labels:  navigation, fragments
Fragnav
An Android library for managing multiple stacks of fragments
Stars: ✭ 1,379 (+36.26%)
Mutual labels:  navigation, fragments
navigator
Annotation processor that eliminates navigation and Bundle boilerplate
Stars: ✭ 13 (-98.72%)
Mutual labels:  fragments, navigation
Flownav
Annotation processor that provides better navigation on android multi-modules projects 🛳.
Stars: ✭ 122 (-87.94%)
Mutual labels:  navigation, fragments
Scene
Android Single Activity Applications framework without Fragment.
Stars: ✭ 1,793 (+77.17%)
Mutual labels:  navigation, fragments
Cicerone
🚦 Cicerone is a lightweight library that makes the navigation in an Android app easy.
Stars: ✭ 2,345 (+131.72%)
Mutual labels:  navigation, fragments
Alligator
Alligator is a modern Android navigation library that will help to organize your navigation code in clean and testable way.
Stars: ✭ 287 (-71.64%)
Mutual labels:  navigation, fragments
Spinmenu
轮盘样式的 Fragment 选择菜单,可转动轮盘切换 Fragment
Stars: ✭ 903 (-10.77%)
Mutual labels:  fragments
Responsive Menu Ant Design
A simple implementation of responsive navigation for Ant Design.
Stars: ✭ 28 (-97.23%)
Mutual labels:  navigation
Django Sitecats
Django reusable application for content categorization.
Stars: ✭ 18 (-98.22%)
Mutual labels:  navigation
Menu
Menu and sidebar management package for Laravel
Stars: ✭ 6 (-99.41%)
Mutual labels:  navigation
A2l
[ICLR 2020] Learning to Move with Affordance Maps 🗺️🤖💨
Stars: ✭ 23 (-97.73%)
Mutual labels:  navigation
Pepper Robot Programming
Pepper Programs : Object Detection Real Time without ROS
Stars: ✭ 29 (-97.13%)
Mutual labels:  navigation
Tagstore
a research software; a fun way of storing files & folders on your local disk drive; tagging
Stars: ✭ 18 (-98.22%)
Mutual labels:  navigation
React Apollo Decorators
Better decorators for Apollo and React
Stars: ✭ 39 (-96.15%)
Mutual labels:  fragments
Fourth robot pkg
4号機(KIT-C4)用リポジトリ
Stars: ✭ 7 (-99.31%)
Mutual labels:  navigation
Tensor field nav
Autonomous mapping based on time-varying tensor fields
Stars: ✭ 41 (-95.95%)
Mutual labels:  navigation
Gonav
A Source Engine navigation mesh file parser written in Go.
Stars: ✭ 37 (-96.34%)
Mutual labels:  navigation
Simple Navigation
A ruby gem for creating navigations (with multiple levels) for your Rails, Sinatra or Padrino applications. Render your navigation as html list, link list or breadcrumbs.
Stars: ✭ 868 (-14.23%)
Mutual labels:  navigation
Lispy
Short and sweet LISP editing
Stars: ✭ 856 (-15.42%)
Mutual labels:  navigation

Simple Stack

Why do I want this?

To make navigation to another screen as simple as backstack.goTo(SomeScreen()), and going back as simple as backstack.goBack().

No more FragmentTransactions in random places. Predictable and customizable navigation in a single location.

What is Simple Stack?

Simple Stack is a backstack library (or technically, a navigation framework) that allows you to represent your navigation state in a list of immutable, parcelable data classes ("keys").

This allows preserving your navigation history across configuration changes and process death - this is handled automatically.

Each screen can be associated with a scope, or a shared scope - to easily share data between screens.

This simplifies navigation and state management within an Activity using fragments, views, or whatever else.

Using Simple Stack

In order to use Simple Stack, you need to add jitpack to your project root build.gradle.kts (or build.gradle):

// build.gradle.kts
allprojects {
    repositories {
        // ...
        maven { setUrl("https://jitpack.io") }
    }
    // ...
}

or

// build.gradle
allprojects {
    repositories {
        // ...
        maven { url "https://jitpack.io" }
    }
    // ...
}

and then, add the dependency to your module's build.gradle.kts (or build.gradle):

// build.gradle.kts
implementation("com.github.Zhuinden:simple-stack:2.6.0")
implementation("com.github.Zhuinden:simple-stack-extensions:2.1.0")

or

// build.gradle
implementation 'com.github.Zhuinden:simple-stack:2.5.0'
implementation 'com.github.Zhuinden:simple-stack-extensions:2.1.0'

How do I use it?

You can check out the tutorials for simple examples.

Fragments

With Fragments, the Activity code typically looks like this:

class MainActivity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
    private lateinit var fragmentStateChanger: DefaultFragmentStateChanger

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

        fragmentStateChanger = DefaultFragmentStateChanger(supportFragmentManager, R.id.container)

        Navigator.configure()
            .setStateChanger(SimpleStateChanger(this))
            .install(this, findViewById(R.id.container), History.of(FirstScreen()))
    }

    override fun onBackPressed() {
        if (!Navigator.onBackPressed(this)) {
            super.onBackPressed()
        }
    }

    override fun onNavigationEvent(stateChange: StateChange) {
        fragmentStateChanger.handleStateChange(stateChange)
    }
}

Where FirstScreen looks like this:

@Parcelize // typically data class
class FirstScreen: DefaultFragmentKey() {
    override fun instantiateFragment(): Fragment = FirstFragment()
}

And FirstFragment looks like this:

class FirstFragment: KeyedFragment(R.layout.first_fragment) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val key: FirstScreen = getKey() // params
    }
}

After which going to the second screen is as simple as backstack.goTo(SecondScreen()).

Scopes

To simplify sharing data/state between screens, a screen key can implement ScopeKey.

The scope is described with a String tag, and services bound to that scope can be configured via ScopedServices.

Services bound to a ServiceBinder get lifecycle callbacks: ScopedServices.Registered, ScopedServices.Activated, or Bundleable.

This lets you easily share a class between screens, while still letting you handle Android's lifecycles seamlessly.

Using the simple-stack-extensions, this can be simplified using the DefaultServiceProvider.

It looks like this:

Navigator.configure()
    .setScopedServices(DefaultServiceProvider())
    /* ... */

And then:

@Parcelize // typically data class
class FirstScreen: DefaultFragmentKey(), DefaultServiceProvider.HasServices {
    override fun instantiateFragment(): Fragment = FirstFragment()

    override fun getScopeTag() = javaClass.name

    override fun bindServices(serviceBinder: ServiceBinder) {
        with(serviceBinder) {
            add(FirstScopedModel())
        }
    }
}

class FirstScopedModel : Bundleable, ScopedServices.Registered { // interfaces are optional
    ...
}

class FirstFragment : KeyedFragment(R.layout.first_fragment) {
    private val firstModel by lazy { lookup<FirstScopedModel>() }

    ...
}

class SecondFragment : KeyedFragment(R.layout.second_fragment) {
    private val firstModel by lazy { lookup<FirstScopedModel>() } // <- available if FirstScreen is in the backstack

    ...
}

And FirstScopedModel is shared between two screens.

Any additional shared scopes on top of screen scopes can be defined using ScopeKey.Child.

What are additional benefits?

Making your navigation state explicit means you're in control of your application.

Instead of hacking around with the right fragment transaction tags, or calling NEW_TASK | CLEAR_TASK and making the screen flicker - you can just say backstack.setHistory(History.of(SomeScreen(), OtherScreen()) and that is now your active navigation history.

Using Backstack to navigate allows you to move navigation responsibilities out of your view layer. No need to run FragmentTransactions directly in a click listener each time you want to move to a different screen. No need to mess around with LiveData<Event<T>> or SingleLiveData to get your "view" to decide what state your app should be in either.

class FirstScopedModel(private val backstack: Backstack) {
    fun doSomething() {
        // ...
        backstack.goTo(SecondScreen())
    }
}

Another additional benefit is that your navigation history can be unit tested.

assertThat(backstack.getHistory()).containsExactly(SomeScreen(), OtherScreen())

And most importantly, navigation (swapping screens) happens in one place, and you are in direct control of what happens in such a scenario. By writing a StateChanger, you can set up "how to display my current navigation state" in any way you want. No more ((MainActivity)getActivity()).setTitleText("blah"); inside Fragment's onStart().

Write once, works in all cases.

override fun onNavigationEvent(stateChange: StateChange) { // using SimpleStateChanger
    val newScreen = stateChange.topNewKey<MyScreen>() // use your new navigation state

    setTitle(newScreen.title);

    ... // set up fragments, set up views, whatever you want
}

Whether you navigate forward or backward, or you rotate the screen, or you come back after low memory condition - it's irrelevant. The StateChanger will always handle the scenario in a predictable way.

More information

For more information, check the wiki page.

License

Copyright 2017-2020 Gabor Varadi

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.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].