Skip to content

AngularDart Counter

beginner

In the following tutorial, we’re going to build a Counter in AngularDart using the Bloc library.

demo

Setup

We’ll start off by creating a brand new AngularDart project with stagehand.

If you don’t have stagehand installed, activate it via:

Terminal window
dart pub global activate stagehand

Then generate a new project via:

Terminal window
stagehand web-angular

We can then go ahead and replace the contents of pubspec.yaml with:

pubspec.yaml
name: angular_counter
description: A web app that uses angular_bloc
environment:
sdk: ">=3.0.0 <4.0.0"
dependencies:
angular_bloc: ^10.0.0-dev.2
bloc: ^8.1.1
ngdart: ^8.0.0-dev.4
dev_dependencies:
build_daemon: ^4.0.0
build_runner: ^2.0.0
build_web_compilers: ^4.0.0

and then install all of our dependencies

Terminal window
dart pub get

Our counter app is just going to have two buttons to increment/decrement the counter value and an element to display the current value. Let’s get started designing the CounterEvents.

Counter Bloc

Since our counter’s state can be represented by an integer we don’t need to create a custom class and we can co-locate the events and bloc.

lib/src/counter_page/counter_bloc.dart
import 'package:bloc/bloc.dart';
/// Base counter event.
sealed class CounterEvent {}
/// Notifies bloc to increment state.
final class CounterIncrementPressed extends CounterEvent {}
/// Notifies bloc to decrement state.
final class CounterDecrementPressed extends CounterEvent {}
/// {@template counter_bloc}
/// A simple [Bloc] that manages an `int` as its state.
/// {@endtemplate}
class CounterBloc extends Bloc<CounterEvent, int> {
/// {@macro counter_bloc}
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit) => emit(state + 1));
on<CounterDecrementPressed>((event, emit) => emit(state - 1));
}
}

Counter App

Now that we have our CounterBloc fully implemented, we can get started creating our AngularDart App Component.

Our app.component.dart should look like:

lib/app_component.dart
import 'package:angular_counter/src/counter_page/counter_page_component.dart';
import 'package:ngdart/angular.dart';
/// Top level application component.
@Component(
selector: 'my-app',
templateUrl: 'app_component.html',
directives: [CounterPageComponent],
)
class AppComponent {}

and our app.component.html should look like:

lib/app_component.html
<counter-page></counter-page>

Counter Page

Finally, all that’s left is to build our Counter Page Component.

Our counter_page_component.dart should look like:

lib/src/counter_page/counter_page_component.dart
import 'package:angular_bloc/angular_bloc.dart';
import 'package:angular_counter/src/counter_page/counter_bloc.dart';
import 'package:ngdart/angular.dart';
/// {@template counter_page}
/// Counter page component which renders a counter
/// and allows users to increment/decrement the counter.
/// {@endtemplate}
@Component(
selector: 'counter-page',
templateUrl: 'counter_page_component.html',
styleUrls: ['counter_page_component.css'],
providers: [ClassProvider(CounterBloc)],
pipes: [BlocPipe],
)
class CounterPageComponent implements OnDestroy {
/// {@macro counter_page}
const CounterPageComponent(this.counterBloc);
/// The associated [CounterBloc] which manages the count.
final CounterBloc counterBloc;
@override
void ngOnDestroy() {
counterBloc.close();
}
/// Increment the count.
void increment() => counterBloc.add(CounterIncrementPressed());
/// Decrement the count.
void decrement() => counterBloc.add(CounterDecrementPressed());
}

Lastly, our counter_page_component.html should look like:

lib/src/counter_page/counter_page_component.html
<div class="counter-page-container">
<h1>Counter App</h1>
<h2>Current Count: {{ $pipe.bloc(counterBloc) }}</h2>
<button class="counter-button" (click)="increment()"></button>
<button class="counter-button" (click)="decrement()"></button>
</div>

That’s it! We’ve separated our presentation layer from our business logic layer. Our CounterPageComponent has no idea what happens when a user presses a button; it just adds an event to notify the CounterBloc. Furthermore, our CounterBloc has no idea what is happening with the state (counter value); it’s simply converting the CounterEvents into integers.

We can run our app with webdev serve and can view it at http://localhost:8080.

The full source for this example can be found here.