# Overview

## What is it?

A Kotlin multiplatform **state management and navigation library** for [Compose](https://developer.android.com/jetpack/compose), based on a fork of [Voyager](https://voyager.adriel.cafe/) by Adriel Cafe, and a port of [flutter\_bloc ](https://pub.dev/packages/flutter_bloc)by Felix Angelov.

## Show me some code

```kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //standard setContent to define UI for an activity with Compose
        setContent {
            //use RootNavigator to initialize compose_bloc navigator for activity
            RootNavigator(MainScreen(userName="Albert Einstein"))
        }
    }
}

// A Screen is what the name suggests, and is the basic UI entity to which you can
// navigate to with a Navigator. Entities associated with a Screen like a ScreenModel
// or Bloc are automatically disposed when a Screen is disposed, for example when we
// receive an "onBackPressed" event in a Screen.
// Note that a Screen must be Serializable, so that its fields, that you can think as 
// it "arguments" can be saved and restored when an Activity is paused and then restarted
// or when screen is rotated.
class MainScreen(val userName:String): Screen {
    @Composable
    override fun Content() {
        // the current navigator, in this case it is the root navigator.
        // It is possible to define nested navigators
        val navigator= LocalNavigator.currentOrThrow
        Compose_blocTheme {
            Scaffold(
                topBar = { TopAppBar{ Text("Counter test app") } },
                backgroundColor = MaterialTheme.colors.background,
                    Column {
                        Text("Hello $userName")
                        // when the user click the button we navigate to a new screen
                        Button(onClick={ navigator.push(CounterScreen())})
                             { Text("click to open Counter Screen") }
                    }
                }
            )
        }
    }
}

// The idea behind the Bloc architecture, very similar to Redux, is that we
// don't modify directly the state of the application (the state associated to the Bloc)
// but instead we send events ("actions" in Redux) that are processed
// by the Bloc registered event handlers 

// Here is the definition of the events CounterBloc can handle, all events
// must inherit to some base event type that the bloc is supposed to handle
interface CounterEvent
class AdditionEvent(val value:Int):CounterEvent
class SubtractionEvent(val value:Int):CounterEvent

// this is the state of the bloc. It should be an immutable object, that we don't
// update in-place. instead we create a new instance with the modified values.
// a data class is perfect for this purpose
data class CounterState(val counter:Int=0)

// here we define the Bloc itself. The bloc wrap together
// - some application state (CounterState)
// - associated event handlers to handle changes to the application state
// - a kotlin Flow with the current value of the bloc state, that we can transform to
//   compose MutableState and listen to, for  automatically updating the UI 
//   on state changes
class CounterBloc(cscope: CoroutineScope, startCounter:Int=0):
 Bloc<CounterEvent, CounterState>(
     // every bloc has an associated coroutineScope that is automatically cancelled
     // when the bloc is disposed.
     cscope,   CounterState(startCounter),false) 
 {
    // in the bloc constructor we define the bloc event handlers
    init {
        // each event handler define a function that is called when
        // an event of the specified type is received. the function
        // has two arguments
        // - the received event
        // - an "emit" method to call to emit the updated state according to
        //   the received event. 
        // Note that unlike Redux reducers, event handler are not necessarily pure
        // function without side-effects. On the contrary there can be event handlers
        // whose only purpose are their side effects, and that do not emit a new state
        // at all. Use the coroutine scope associated to the bloc to run the side effects
        on<AdditionEvent> { event, emit ->
            val s=state
            emit(s.copy(counter =s.counter+event.value ))
        }
        on<SubtractionEvent> { event, emit ->
            val s=state
            emit(s.copy(counter =s.counter-event.value ))
        }
    }
}

class CounterScreen: Screen {
    @Composable
    override fun Content() {
          Column(modifier=Modifier.fillMaxWidth(), 
                 horizontalAlignment = Alignment.CenterHorizontally) {
            // out of the BlocProvider composable subtree the bloc is not available
            val bnull= rememberProvidedBloc<CounterBloc>()
            Log.e(LOGTAG,"obtained bnull counter bloc: $bnull")  //this must be null
            // BlocProvider makes available the specified bloc (CounterBloc)
            // to the associated composable subtree
            BlocProvider({cscope-> CounterBloc(cscope,1)} ) {
                //rememberProvidedBlocOf is similar to dependency injection:
                //  it retrieves the specified bloc type as defined by the closest
                //  enclosing BlocProvider
                val b= rememberProvidedBloc<CounterBloc>()?:return@BlocProvider
                // define some callbacks to wrap sending events to the bloc so
                // that the actual UI does need to know anything about the bloc
                val onIncrement = { b.add(AdditionEvent(1)) }
                val onDecrement = { b.add(SubtractionEvent(1) }
                //BlocBuilder search for the specified bloc type as defined by 
                // the closest enclosing blocProvider and subscribes to its states
                // updates, as a Composable mutableState  that when changes trigger
                // recomposition
                BlocBuilder(b) { counterState->
                    //this is the actual ui composable
                    CounterControls(
                        "Counter display updated always",
                        counterState.counter,
                        onDecrement, onIncrement)
                }
            }
            // out of the BlocProvider composable subtree the bloc is not available
            val bnull2:CounterBloc = rememberProvidedBloc()
            Log.e(LOGTAG,"obtained bnull2 counter bloc: $bnull2") //this must be null

        }
    }

}

@Composable
fun CounterControls(
    explanatoryText:String,
    counterValue: Int,
    onDecrement: () -> Unit,
    onIncrement: () -> Unit
) {
    Text(explanatoryText)
    Text("Counter value: ${counterValue}")
    Row {
        Button(
            onClick = onDecrement,
            modifier = Modifier.padding(horizontal = 8.dp)
        ) { Text(text = "-") }
        Button(
            onClick = onIncrement,
            modifier = Modifier.padding(horizontal = 8.dp)
        ) { Text(text = "+") }
    }
}

```

### Learn more about Navigator

{% content-ref url="/pages/V3qSdIuVWu98hoeceE8w" %}
[Navigator Overview](/compose-bloc/navigator-documentation/navigator-overview.md)
{% endcontent-ref %}

### Learn more about Bloc

{% content-ref url="/pages/XIjcrUfzicEpQHLqABB9" %}
[Bloc and Cubit Overview](/compose-bloc/bloc-documentation/bloc-and-cubit-overview.md)
{% endcontent-ref %}

### Integration of Blocs with Navigator and Compose

{% content-ref url="/pages/54RkdIxepy2i4Tglpx4E" %}
[Blocs and Compose Overview](/compose-bloc/bloc-documentation/blocs-and-compose-overview.md)
{% endcontent-ref %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://beyondeye.gitbook.io/compose-bloc/overview.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
