Mastering Flutter Development: Building a Counter App with Stream and RxDart
Table of contents
Welcome to my latest project in Flutter! As a developer, I am always looking for new ways to master my skills and techniques, and what better way to do that than to create an app from scratch? In this post, I will take you through the development process of a simple counter app using Flutter.
While a counter app may seem trivial, it actually presents a great opportunity to learn and practice fundamental concepts in Flutter, such as working with widgets, managing state, and handling user input. It's also a great way to get familiar with the Flutter framework, especially for beginners just starting out.
Throughout this post, I will share my thought process, the challenges I faced, and the lessons I learned as I developed this app. By the end of it, you should have a solid understanding of how to build a simple app in Flutter and be ready to take on more complex projects. So, let's get started!
Steps
create your app with the flutter create command
flutter create counter_app
create a counter_bloc class to contain the logic
The
counter_bloc
class would have the integer variable counter initialized to zero and a counter controller is declared to be a behaviour type stream of initial seed zero, then we create a getter of typestream<int>
counter to access the streamed counter valueWe then create a function that increases the counter value by one and updates the counter stream counter value
//with bloc i have to seperate the vusiness ui from the logic //by creating a class that serves as a middleman between the ui //and logic import 'package:rxdart/rxdart.dart'; class CounterBloc { int _counter = 0; final _controlcounter = BehaviorSubject<int>.seeded(0); Stream<int> get counter => _controlcounter.stream; //we create a getter counter that returns a stream of typer int //of the counter void incrementCounter() { _counter++; _controlcounter.sink.add(_counter); // this function does two things //first it increases the value of the counter value by one //then it pushes the just pdated counter integer variable to // the control counter with the sink.add method } void dispose() { _controlcounter.close(); //the function dispose closes the controlcounter when its no longer needed } }
create a counter screen class to contain the user interface
The design for the user interface is basically a centre widget to display the counter value and a button to increase it on click.
other peculiarities are accessing the increment function in the counter bloc class by importing the file.
Then we create a variable
_counterBloc
to access thecounterBloc
class, then we use it to get the streamed counter value which theStreamBuilder
widget that returns theText
widget then shows the counter valueimport 'package:flutter/material.dart'; import 'counterbloc.dart'; class CounterScreen extends StatefulWidget { const CounterScreen({super.key}); @override State<CounterScreen> createState() => _CounterScreenState(); } class _CounterScreenState extends State<CounterScreen> { final _counterBloc = CounterBloc(); @override void dispose() { _counterBloc.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('CounterBloc App')), body: Center( child: StreamBuilder( stream: _counterBloc.counter, initialData: 0, builder: (context, snapshot) => Text( 'counter has been tapped' + snapshot.data.toString() + ' times', style: const TextStyle(fontSize: 40), ), ), ), floatingActionButton: FloatingActionButton( onPressed: () { _counterBloc.incrementCounter(); }, child: const Icon(Icons.add), ), ); } }
Challenges
while coding the app, I encountered some errors
First mistake
My wonderful self wanted to convert the whole class to string.
Center(
child: Text(
_counterBloc.toString(),
style: TextStyle(fontSize: 60),
),
),
Another mistake
Center(
child: StreamBuilder(
stream: _counterBloc.counter,
initialData: 0,
builder: (context, snapshot) => Text(
snapshot.toString(),
style: const TextStyle(fontSize: 40),
),
),
),
the mistake in simple words, the snapshot is of type AsyncSnapshot<int> and cant be converted to a type string with just the toString() method
Final Result
Center(
child: StreamBuilder(
stream: _counterBloc.counter,
initialData: 0,
builder: (context, snapshot) => Text(
'counter has been tapped' + snapshot.data.toString() + ' times',
style: const TextStyle(fontSize: 40),
),
),
),
Conclusion
In conclusion, building a counter app using Stream and RxDart was a great way to practice and improve my skills in Flutter development. By utilizing these powerful tools, I was able to create a simple but effective app that demonstrates the potential of Flutter as a mobile development platform.
Throughout the process, I encountered some challenges, such as handling errors and working with asynchronous code. However, these challenges allowed me to learn new techniques and approaches, which will undoubtedly come in handy in future projects.
Overall, I hope this post has been informative and helpful to anyone looking to improve their Flutter development skills.