Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add anchor urls for headers #4149

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions docs/src/content/docs/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Using the bloc library allows us to separate our application into three layers:

We're going to start at the lowest level layer (farthest from the user interface) and work our way up to the presentation layer.

## Data Layer
## [Data Layer](#data-layer)

The data layer's responsibility is to retrieve/manipulate data from one or more sources.

Expand All @@ -35,7 +35,7 @@ The data layer can be split into two parts:

This layer is the lowest level of the application and interacts with databases, network requests, and other asynchronous data sources.

### Data Provider
### [Data Provider](#data-provider)

The data provider's responsibility is to provide raw data. The data provider should be generic and versatile.

Expand All @@ -44,23 +44,23 @@ We might have a `createData`, `readData`, `updateData`, and `deleteData` method

<DataProviderSnippet />

### Repository
### [Repository](#repository)

The repository layer is a wrapper around one or more data providers with which the Bloc Layer communicates.

<RepositorySnippet />

As you can see, our repository layer can interact with multiple data providers and perform transformations on the data before handing the result to the business logic layer.

## Business Logic Layer
## [Business Logic Layer](#business-logic-layer)

The business logic layer's responsibility is to respond to input from the presentation layer with new states. This layer can depend on one or more repositories to retrieve data needed to build up the application state.

Think of the business logic layer as the bridge between the user interface (presentation layer) and the data layer. The business logic layer is notified of events/actions from the presentation layer and then communicates with repository in order to build a new state for the presentation layer to consume.

<BusinessLogicComponentSnippet />

### Bloc-to-Bloc Communication
### [Bloc-to-Bloc Communication](#bloc-to-bloc-communication)

Because blocs expose streams, it may be tempting to make a bloc which listens to another bloc. You should **not** do this. There are better alternatives than resorting to the code below:

Expand All @@ -76,7 +76,7 @@ A bloc should only receive information through events and from injected reposito

If you're in a situation where a bloc needs to respond to another bloc, you have two other options. You can push the problem up a layer (into the presentation layer), or down a layer (into the domain layer).

#### Connecting Blocs through Presentation
#### [Connecting Blocs through Presentation](#connecting-blocs-through-presentation)

You can use a `BlocListener` to listen to one bloc and add an event to another bloc whenever the first bloc changes.

Expand All @@ -86,7 +86,7 @@ The code above prevents `SecondBloc` from needing to know about `FirstBloc`, enc

In some situations, you may not want to couple two blocs in the presentation layer. Instead, it can often make sense for two blocs to share the same source of data and update whenever the data changes.

#### Connecting Blocs through Domain
#### [Connecting Blocs through Domain](#connecting-blocs-through-domain)

Two blocs can listen to a stream from a repository and update their states independent of each other whenever the repository data changes. Using reactive repositories to keep state synchronized is common in large-scale enterprise applications.

Expand All @@ -100,7 +100,7 @@ The same repository can be injected into each bloc that needs to react to new ap

For more about using streams with Bloc, see [How to use Bloc with streams and concurrency](https://verygood.ventures/blog/how-to-use-bloc-with-streams-and-concurrency).

## Presentation Layer
## [Presentation Layer](#presentation-layer)

The presentation layer's responsibility is to figure out how to render itself based on one or more bloc states. In addition, it should handle user input and application lifecycle events.

Expand Down
48 changes: 24 additions & 24 deletions docs/src/content/docs/bloc-concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ There are several core concepts that are critical to understanding how to use th

In the upcoming sections, we're going to discuss each of them in detail as well as work through how they would apply to a counter app.

## Streams
## [Streams](#streams)

:::note
Check out the official [Dart Documentation](https://dart.dev/tutorials/language/streams) for more information about `Streams`.
Expand Down Expand Up @@ -88,7 +88,7 @@ We can put it all together like so:

Now that we have a basic understanding of how `Streams` work in Dart we're ready to learn about the core component of the bloc package: a `Cubit`.

## Cubit
## [Cubit](#cubit)

A `Cubit` is a class which extends `BlocBase` and can be extended to manage any type of state.

Expand All @@ -102,7 +102,7 @@ States are the output of a `Cubit` and represent a part of your application's st
For more information about the origins of `Cubit` checkout [the following issue](https://github.com/felangel/cubit/issues/69).
:::

### Creating a Cubit
### [Creating a Cubit](#creating-a-cubit)

We can create a `CounterCubit` like:

Expand All @@ -118,7 +118,7 @@ This would allow us to instantiate `CounterCubit` instances with different initi

<CounterCubitInstantiationSnippet />

### Cubit State Changes
### [Cubit State Changes](#cubit-state-changes)

Each `Cubit` has the ability to output a new state via `emit`.

Expand All @@ -130,7 +130,7 @@ In the above snippet, the `CounterCubit` is exposing a public method called `inc
The `emit` method is protected, meaning it should only be used inside of a `Cubit`.
:::

### Using a Cubit
### [Using a Cubit](#using-a-cubit)

We can now take the `CounterCubit` we've implemented and put it to use!

Expand All @@ -140,7 +140,7 @@ We can now take the `CounterCubit` we've implemented and put it to use!

In the above snippet, we start by creating an instance of the `CounterCubit`. We then print the current state of the cubit which is the initial state (since no new states have been emitted yet). Next, we call the `increment` function to trigger a state change. Finally, we print the state of the `Cubit` again which went from `0` to `1` and call `close` on the `Cubit` to close the internal state stream.

#### Stream Usage
#### [Stream Usage](#stream-usage)

`Cubit` exposes a `Stream` which allows us to receive real-time state updates:

Expand All @@ -156,7 +156,7 @@ In the above snippet, we are subscribing to the `CounterCubit` and calling print
Only subsequent state changes will be received when calling `listen` on a `Cubit`.
:::

### Observing a Cubit
### [Observing a Cubit](#observing-a-cubit)

When a `Cubit` emits a new state, a `Change` occurs. We can observe all changes for a given `Cubit` by overriding `onChange`.

Expand All @@ -174,7 +174,7 @@ The above example would output:
A `Change` occurs just before the state of the `Cubit` is updated. A `Change` consists of the `currentState` and the `nextState`.
:::

#### BlocObserver
#### [BlocObserver](#blocobserver)

One added bonus of using the bloc library is that we can have access to all `Changes` in one place. Even though in this application we only have one `Cubit`, it's fairly common in larger applications to have many `Cubits` managing different parts of the application's state.

Expand Down Expand Up @@ -202,7 +202,7 @@ The internal `onChange` override is called first, which calls `super.onChange` n
In `BlocObserver` we have access to the `Cubit` instance in addition to the `Change` itself.
:::

### Cubit Error Handling
### [Cubit Error Handling](#cubit-error-handling)

Every `Cubit` has an `addError` method which can be used to indicate that an error has occurred.

Expand All @@ -224,13 +224,13 @@ If we run the same program again we should see the following output:
Just as with `onChange`, the internal `onError` override is invoked before the global `BlocObserver` override.
:::

## Bloc
## [Bloc](#bloc)

A `Bloc` is a more advanced class which relies on `events` to trigger `state` changes rather than functions. `Bloc` also extends `BlocBase` which means it has a similar public API as `Cubit`. However, rather than calling a `function` on a `Bloc` and directly emitting a new `state`, `Blocs` receive `events` and convert the incoming `events` into outgoing `states`.

![Bloc Architecture](~/assets/concepts/bloc_architecture_full.png)

### Creating a Bloc
### [Creating a Bloc](#creating-a-bloc)

Creating a `Bloc` is similar to creating a `Cubit` except in addition to defining the state that we'll be managing, we must also define the event that the `Bloc` will be able to process.

Expand All @@ -240,7 +240,7 @@ Events are the input to a Bloc. They are commonly added in response to user inte

Just like when creating the `CounterCubit`, we must specify an initial state by passing it to the superclass via `super`.

### Bloc State Changes
### [Bloc State Changes](#bloc-state-changes)

`Bloc` requires us to register event handlers via the `on<Event>` API, as opposed to functions in `Cubit`. An event handler is responsible for converting any incoming events into zero or more outgoing states.

Expand Down Expand Up @@ -268,11 +268,11 @@ Blocs should never directly `emit` new states. Instead every state change must b
Both blocs and cubits will ignore duplicate states. If we emit `State nextState` where `state == nextState`, then no state change will occur.
:::

### Using a Bloc
### [Using a Bloc](#using-a-bloc)

At this point, we can create an instance of our `CounterBloc` and put it to use!

#### Basic Usage
#### [Basic Usage](#basic-usage)

<CounterBlocUsageSnippet />

Expand All @@ -282,7 +282,7 @@ In the above snippet, we start by creating an instance of the `CounterBloc`. We
`await Future.delayed(Duration.zero)` is added to ensure we wait for the next event-loop iteration (allowing the `EventHandler` to process the event).
:::

#### Stream Usage
#### [Stream Usage](#stream-usage)

Just like with `Cubit`, a `Bloc` is a special type of `Stream`, which means we can also subscribe to a `Bloc` for real-time updates to its state:

Expand All @@ -294,7 +294,7 @@ In the above snippet, we are subscribing to the `CounterBloc` and calling print
`await Future.delayed(Duration.zero)` is added for this example to avoid canceling the subscription immediately.
:::

### Observing a Bloc
### [Observing a Bloc](#observing-a-bloc)

Since `Bloc` extends `BlocBase`, we can observe all state changes for a `Bloc` using `onChange`.

Expand Down Expand Up @@ -324,7 +324,7 @@ If we then rerun the same `main.dart` snippet from before, we should see the fol
`onTransition` is invoked before `onChange` and contains the event which triggered the change from `currentState` to `nextState`.
:::

#### BlocObserver
#### [BlocObserver](#blocobserver)

Just as before, we can override `onTransition` in a custom `BlocObserver` to observe all transitions that occur from a single place.

Expand Down Expand Up @@ -356,7 +356,7 @@ We can run the same `main.dart` as before and should see the following output:
`onEvent` is called as soon as the event is added. The local `onEvent` is invoked before the global `onEvent` in `BlocObserver`.
:::

### Bloc Error Handling
### [Bloc Error Handling](#bloc-error-handling)

Just like with `Cubit`, each `Bloc` has an `addError` and `onError` method. We can indicate that an error has occurred by calling `addError` from anywhere inside our `Bloc`. We can then react to all errors by overriding `onError` just as with `Cubit`.

Expand All @@ -378,13 +378,13 @@ The local `onError` is invoked first followed by the global `onError` in `BlocOb
Any unhandled exceptions that occur within an `EventHandler` are also reported to `onError`.
:::

## Cubit vs. Bloc
## [Cubit vs. Bloc](#cubit-vs-bloc)

Now that we've covered the basics of the `Cubit` and `Bloc` classes, you might be wondering when you should use `Cubit` and when you should use `Bloc`.

### Cubit Advantages
### [Cubit Advantages](#cubit-advantages)

#### Simplicity
#### [Simplicity](#simplicity)

One of the biggest advantages of using `Cubit` is simplicity. When creating a `Cubit`, we only have to define the state as well as the functions which we want to expose to change the state. In comparison, when creating a `Bloc`, we have to define the states, events, and the `EventHandler` implementation. This makes `Cubit` easier to understand and there is less code involved.

Expand All @@ -400,9 +400,9 @@ Now let's take a look at the two counter implementations:

The `Cubit` implementation is more concise and instead of defining events separately, the functions act like events. In addition, when using a `Cubit`, we can simply call `emit` from anywhere in order to trigger a state change.

### Bloc Advantages
### [Bloc Advantages](#bloc-advantages)

#### Traceability
#### [Traceability](#traceability)

One of the biggest advantages of using `Bloc` is knowing the sequence of state changes as well as exactly what triggered those changes. For state that is critical to the functionality of an application, it might be very beneficial to use a more event-driven approach in order to capture all events in addition to state changes.

Expand All @@ -420,7 +420,7 @@ The above `Transition` gives us all the information we need to understand why th

This tells us that the user was logged out but it doesn't explain why which might be critical to debugging and understanding how the state of the application is changing over time.

#### Advanced Event Transformations
#### [Advanced Event Transformations](#advanced-event-transformations)

Another area in which `Bloc` excels over `Cubit` is when we need to take advantage of reactive operators such as `buffer`, `debounceTime`, `throttle`, etc.

Expand Down
16 changes: 8 additions & 8 deletions docs/src/content/docs/faqs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import BlocInternalAddEventSnippet from '~/components/faqs/BlocInternalAddEventS
import BlocInternalEventSnippet from '~/components/faqs/BlocInternalEventSnippet.astro';
import BlocExternalForEachSnippet from '~/components/faqs/BlocExternalForEachSnippet.astro';

## State Not Updating
## [State Not Updating](#state-not-updating)

❔ **Question**: I'm emitting a state in my bloc but the UI is not updating. What
am I doing wrong?
Expand Down Expand Up @@ -84,7 +84,7 @@ like:

<NoEquatableBlocTestSnippet />

## Handling Errors
## [Handling Errors](#handling-errors)

❔ **Question**: How can I handle an error while still showing previous data?

Expand All @@ -102,7 +102,7 @@ when an error has occurred.

<SingleStateUsageSnippet />

## Bloc vs. Redux
## [Bloc vs. Redux](#bloc-vs-redux)

❔ **Question**: What's the difference between Bloc and Redux?

Expand Down Expand Up @@ -137,7 +137,7 @@ across multiple blocs. Furthermore, there is no concept of middleware in bloc
and bloc is designed to make async state changes very easy, allowing you to emit
multiple states for a single event.

## Bloc vs. Provider
## [Bloc vs. Provider](#bloc-vs-provider)

❔ **Question**: What's the difference between Bloc and Provider?

Expand All @@ -147,7 +147,7 @@ multiple states for a single event.
internally to make it easy to provide and access blocs throughout the widget
tree.

## BlocProvider.of() Fails to Find Bloc
## [BlocProvider.of() Fails to Find Bloc](#blocproviderof-fails-to-find-bloc)

❔ **Question**: When using `BlocProvider.of(context)` it cannot find the bloc.
How can I fix this?
Expand All @@ -166,7 +166,7 @@ provided so you must ensure `BlocProvider.of()` is called within a child

<BlocProviderBad1Snippet />

## Project Structure
## [Project Structure](#project-structure)

❔ **Question**: How should I structure my project?

Expand All @@ -180,7 +180,7 @@ recommended references are
The most important thing is having a **consistent** and **intentional** project
structure.

## Adding Events within a Bloc
## [Adding Events within a Bloc](#adding-events-within-a-bloc)

❔ **Question**: Is it okay to add events within a bloc?

Expand Down Expand Up @@ -227,7 +227,7 @@ The drawbacks of the above approach are:
- We need to expose a public `Started` event which must be added externally
- We cannot use a custom `transformer` to adjust how we react to user updates

## Exposing Public Methods
## [Exposing Public Methods](#exposing-public-methods)

❔ **Question**: Is it okay to expose public methods on my bloc and cubit
instances?
Expand Down
Loading
Loading