How to handle state in Flutter using the BLoC pattern
The Business Logic Component (BLoC) pattern is a pattern created by Google and announced at Google I/O ’18.
A BLoC has two simple components:
Sinks and Streams, both of which are provided by a StreamController. You add streams of event/data input into a Sink and listen to them as streams of data output through a Stream.
Provider, an incredible state management library, that is simpler to use, better to manage and easier to understand than using Bloc’s! :)
..........................................................................................................................................................................................................................
import 'dart:async';
import 'dart:async';
class CounterBloc {
final counterController = StreamController(); // create a StreamController or
Stream get getCount => counterController.stream; // create a getter for our Stream
void updateCount() {
counterController.sink.add(data); // add whatever data we want into the Sink
}
void dispose() {
counterController.close(); // close our StreamController to avoid memory leak
}
}
final bloc = CounterBloc(); // create an instance of the counter bloc
//======= end of CounterBloc file
...........................................................
import 'counter_bloc.dart';
@override
void dispose() {
bloc.dispose(); // call the dispose method to close our StreamController
super.dispose();
}
@override
Widget build(BuildContext context) {
return StreamBuilder( // Wrap our widget with a StreamBuilder
stream: bloc.getCount, // pass our Stream getter here
initialData: 0, // provide an initial data
builder: (context, snapshot) => Text('${snapshot.data}'), // access the data in our Stream here
);
}
A BLoC is a simple Dart class.
we created a CounterBloc class and in it, a StreamController which we called counterController. We created a getter for our Stream called getCount, an updateCount method that adds data into our Sink when called, and a dispose method to close our StreamController.
To access the data in our Stream, we created a StreamBuilder widget and passed our Stream to its stream property and accessed the data in its builder function.
Now, our BLoC is receiving and streaming data. We can access that data and display it on a screen through a StreamBuilder. We wrap whatever widget that needs the data in a StreamBuilder widget and pass the stream containing the data to it. Add the following code to the counter.dart file
.........................................................................................................................................................................................................................
Example : #1
counter_event.dart
enum CounterEvent {
increment,
decrement
}
.........................................
counter_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:helloworld/src/events/counter_event.dart';
class CounterBloc extends Bloc<CounterEvent, int> {
@override
CounterBloc(int initialState) : super(0);
@override
int get initialState => 0;
@override
Stream<int> mapEventToState(CounterEvent event) async* {
print(state); // this is current state
switch (event) {
case CounterEvent.increment:
var newState = state + 1;
yield newState;
break;
case CounterEvent.decrement:
var newState = state -1;
yield newState;
break;
}
}
}
.............................
counter_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:helloworld/src/blocs/counter_bloc.dart';
import 'package:helloworld/src/events/counter_event.dart';
class Counterpage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc counterBloc = context.bloc<CounterBloc>();
return Scaffold(
body: SafeArea(child: BlocBuilder<CounterBloc, int>(
builder: (context, counter) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.all(15.0),
child: FlatButton(
onPressed: () {
counterBloc.add(CounterEvent.increment);
},
child: Text('Increment {+}'),
),
decoration: BoxDecoration(color: Colors.green),
),
Text('Result $counter'),
Container(
margin: EdgeInsets.all(15.0),
child: FlatButton(
onPressed: () {
counterBloc.add(CounterEvent.decrement);
},
child: Text('Increment {-}'),
),
decoration: BoxDecoration(color: Colors.amber),
),
],
);
},
)),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:helloworld/src/blocs/counter_bloc.dart';
import 'package:helloworld/src/pages/counterPage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: BlocProvider<CounterBloc>(
create: (context) => CounterBloc(0),
child: Counterpage(),
)
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
);
}
}
Happy Coding ;)
.........................................................................................................................................................................................................................
Example : #2 [Bloc Counter without library ]
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
CounterBloc counterBloc = CounterBloc();
// @override
// void initState() {
// counterBloc;
// super.initState();
// }
@override
void dispose() {
super.dispose();
counterBloc.dispose();
}
@override
Widget build(BuildContext context) {
print('Every time Build Method is called');
return Scaffold(
appBar: AppBar(
title: Text('hello'),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterBloc.increment();
},
),
body: Center(
child: StreamBuilder(
initialData: 0,
stream: counterBloc.getStream(),
builder: (context, snapshot) {
return Text(
'${snapshot.data}',
style: Theme.of(context).textTheme.headline2,
);
},
),
));
}
}
class CounterBloc {
StreamController<int> controller = StreamController<int>();
int value = 0;
void increment() {
value++;
controller.sink.add(value);
}
Stream<int> getStream() {
return controller.stream;
}
void dispose() {
controller.close();
}
}
Happy Coding :)
.........................................................................................................................................................................................................................
Example : #3
import 'package:flutter/material.dart';
import 'dart:async';
void main()=>runApp( MaterialApp(home: MyApp(),));
class MyApp extends StatefulWidget {
MyApp({Key key, this.title}):super(key:key);
final String title;
@override MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp>{
int _Counter=0;
final StreamController<int> myStream =StreamController<int>();
@override void dispos(){
super.dispose();
myStream.close();
}
@override Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Center(
child: StreamBuilder(
stream: myStream.stream,
initialData: _Counter,
builder:(BuildContext contex, AsyncSnapshot<int>snap) {
return Text("I got it ${snap.data} times", style: TextStyle(fontSize: 40),);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
myStream.sink.add(++ _Counter);
},
child: Icon(Icons.add),
),
);
}
}
Happy Coding :)
.........................................................................................................................................................................................................................
Example : #4 [ Provider Counter Example ]
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: Counter(),
),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: " Provider Pattern"),
),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({this.title});
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _incrementCounter(BuildContext context) {
Provider.of<Counter>(context, listen: false).incrementCounter();
}
@override
Widget build(BuildContext context) {
var counter = Provider.of<Counter>(context).getCounter;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _incrementCounter(context),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class Counter extends ChangeNotifier {
var _count = 0;
int get getCounter {
return _count;
}
void incrementCounter() {
_count += 1;
notifyListeners();
}
}
..................................................
Clean Code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_)=>ClounterApp()),
],
child: MaterialApp(
home: Home(),
),
);
}
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
Future<void> increment() async {
Provider.of<ClounterApp>(context, listen: false).counterrbyOne();
}
@override
Widget build(BuildContext context) {
var c = Provider.of<ClounterApp>(context).getCounter;
return Scaffold(
appBar: AppBar(
title: const Text("Provider"),
),
floatingActionButton: FloatingActionButton(
onPressed: () => increment(),
child: const Icon(Icons.add),
),
body: Center(
child: Text(
"$c",
style: const TextStyle(fontSize: 50),
),
),
);
}
}
class ClounterApp extends ChangeNotifier {
int count = 0;
int get getCounter => count;
Future<void> counterrbyOne()async {
count++;
notifyListeners();
}
}
Happy Coding;)
.........................................................................................................................................................................................................................
Example : #5
Happy Coding;)
.........................................................................................................................................................................................................................
Example : #6
Happy Coding;)
.........................................................................................................................................................................................................................
Example : #7
Happy Coding;)
No comments:
Post a Comment