Saturday, September 26, 2020

BLoC pattern | Provider | Flutter

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';

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