MOCKSTACKS
EN
Questions And Answers

More Tutorials









Flutter State & BLoCs w/Streams Approach

Introduction

BLoC stands for ‘Business Logic Components’.
It’s a pattern for state management recommended by Google developers.
The purpose of this chapter is to learn this pattern for state management.

BLoC Pattern

This pattern is about storing the app main state in a central place (a business logic object stored in a Stateful Widget) and having it communicate with the rest of the app’s Widgets using streams and RxDart.
Note that this pattern uses InheritedWidget to store the Business Logic Component within a widget in the hierarchy.

Reactive Programming

Reactive Programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. It is all about asynchronously emitting data to these streams or listening to those streams and doing something with the data (perform operations on it). To oversimplify things, Observable objects write to these streams and Subscribers listen to these streams. Operators do something with the stream data, like create it, transform it, filter it, combine it etc. It sounds complicated but it can make your code much simpler when you get the hang of it.

One great thing about streams is that you can use them to commutate between software components. For example, rather than have ‘Component 1’ directly call a method in ‘Component 2’ when something happens, you could have Component 2 subscribe to an event stream in Component 1. When something happens in Component 1, it posts to the event stream and Component 2 is notified and does something.

RxDart

The BLoC pattern uses the RxDart package.
RxDart is a reactive functional programming library for Google Dart, based on ReactiveX. Google Dart comes with a very decent Streams API out-of-the-box; rather than
ttempting to provide an alternative to this API, RxDart adds functionality on top of it. So basically, RxDart enhances the Dart support for Streams!

StreamBuilder

This approach uses the StreamBuilder class to build stateless child Widgets. StreamBuilder is a Widget that builds itself based on the latest update from a Stream.
StreamBuilders listen for changes in streams and build Widgets when the stream data changes. Thus, your Widgets can update when the state changes and the state change is pushed to a stream.

Exercise – ‘state_and_block_with_streams’

In this exercise, we use a BLoC with states and streams to enable the user to re-order a list of customers.

Step 1 – Create Default Flutter App

Follow the instructions in Generate Your First App Leave project open.

Step 2 – Add the RxDart Dependency

Add the following dependencies to your ‘pubspec.yaml’ file. After that you will need to do a ‘flutter packages get’ on the command line in the root of your project to download the dependencies.

dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
rxdart: 0.18.1
dev_dependencies:
flutter_test:
sdk: flutter

Step 3 – Replace Application Code


Replace contents of file ‘main.dart’ in folder ‘lib’ with the following:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
class Customer {
String _firstName;
String _lastName;
bool _upButton;
bool _downButton;
Customer(this._firstName, this._lastName) {
_upButton = false;
_downButton = false;
}
String get name => _firstName + " " + _lastName;
bool get upButton => _upButton;
set upButton(bool value) {
_upButton = value;
}
bool get downButton => _downButton;
set downButton(bool value) {
_downButton = value;
}
operator ==(other) =>
(other is Customer) &&
(_firstName == other._firstName) &&
(_lastName == other._lastName);
int get hashCode => _firstName.hashCode ^ _lastName.hashCode;
}
class Bloc {
// BLoC stands for Business Logic Component.
List<Customer> _customerList = [];
Bloc() {
_upActionStreamController.stream.listen(_handleUp);
_downActionStreamController.stream.listen(_handleDown);
}
List<Customer> initCustomerList() {
_customerList = [
new Customer("Fred", "Smith"),
new Customer("Brian", "Johnson"),
new Customer("James", "McGirt"),
new Customer("John", "Brown")
];
updateUpDownButtons();
return _customerList;
}
void dispose() {
_upActionStreamController.close();
_downActionStreamController.close();
}
void _handleUp(Customer customer) {
swap(customer, true);
updateUpDownButtons();
_customerListSubject.add(_customerList);
_messageSubject.add(customer.name + " moved up");
}
void _handleDown(Customer customer) {
swap(customer, false);
updateUpDownButtons();
_customerListSubject.add(_customerList);
_messageSubject.add(customer.name + " moved down");
}
void swap(Customer customer, bool up) {
int idx = _customerList.indexOf(customer);
_customerList.remove(customer);
_customerList.insert(up ? idx - 1 : idx + 1, customer);
}
void updateUpDownButtons() {
//TODO We dont really need to update them all, but this is just an example.
for (int idx = 0, lastIdx = _customerList.length - 1;
idx <= lastIdx;
idx++) {
Customer customer = _customerList[idx];
customer.upButton = (idx > 0);
customer.downButton = (idx < lastIdx);
}
}
// Streams for State Updates
Stream<List<Customer>> get customerListStream =>
_customerListSubject.stream;
final _customerListSubject = BehaviorSubject<List<Customer>>();
Stream<String> get messageStream => _messageSubject.stream;
final _messageSubject = BehaviorSubject<String>();
// Sinks for Actions
Sink<Customer> get upAction => _upActionStreamController.sink;
final _upActionStreamController = StreamController<Customer>();
Sink<Customer> get downAction => _downActionStreamController.sink;
final _downActionStreamController = StreamController<Customer>();
}
class BlocProvider extends InheritedWidget {
final Bloc bloc;
BlocProvider({
Key key, @required this.bloc, Widget child,
}) : super(key: key, child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static Bloc of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(BlocProvider) as
BlocProvider).bloc;
}
class CustomerWidget extends StatelessWidget {
final Customer _customer;
CustomerWidget(this._customer);
@override
Widget build(BuildContext context) {
final bloc = BlocProvider.of(context);
Text text = Text(_customer.name,
style: const TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold));
IconButton upButton = IconButton(
icon: new Icon(Icons.arrow_drop_up, color: Colors.blue),
onPressed: () {
bloc.upAction.add(_customer);
});
IconButton downButton = IconButton(
icon: new Icon(Icons.arrow_drop_down, color: Colors.blue),
onPressed: () {
bloc.downAction.add(_customer);
});
List<Widget> children = [];
children.add(Expanded(
child: Padding(padding: EdgeInsets.only(left: 20.0), child: text)));
if (_customer.upButton) {
children.add(upButton);
}
if (_customer.downButton) {
children.add(downButton);
}
return Padding(
padding: EdgeInsets.all(6.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Container(
decoration: BoxDecoration(color: Colors.cyan[100]),
child: Row(
children: children,
mainAxisAlignment: MainAxisAlignment.start))));
}
}
void main() => runApp(new CustomerAppWidget());
class CustomerAppWidget extends StatelessWidget {
// This widget is the root of your application.
final Bloc _bloc = new Bloc();
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: BlocProvider(
bloc: _bloc,
child: new CustomerListWidget(
title: 'Flutter '
'Demo Home Page',
messageStream: _bloc.messageStream,
),
),
);
}
}
class CustomerListWidget extends StatelessWidget {
CustomerListWidget({Key key, this.title, Stream<String> this.messageStream})
: super(key: key) {
this.messageStream.listen((message) {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(message),
duration: Duration(seconds: 1),
));
});
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final String title;
final Stream<String> messageStream;
@override
Widget build(BuildContext context) {
final bloc = BlocProvider.of(context);
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text(title),
),
body: StreamBuilder<List<Customer>>(
stream: bloc.customerListStream,
initialData: bloc.initCustomerList(),
builder: (context, snapshot) {
List<Widget> customerWidgets =
snapshot.data.map((Customer customer) {
return CustomerWidget(customer);
}).toList();
return ListView(
padding: const EdgeInsets.all(10.0),
children: customerWidgets);
}));
}
}

Step 4 – Open Emulator & Run

Follow the instructions in Open Android Emulator & Run Your First App
You can move the customers up and down using the arrow icons.
Note that the user is also presented with a message at the bottom.


Conclusion

In this page (written and validated by ) you learned about Flutter State and BLoCs w Streams Approach . What's Next? If you are interested in completing Flutter tutorial, your next topic will be learning about: Flutter Local Files.



Incorrect info or code snippet? We take very seriously the accuracy of the information provided on our website. We also make sure to test all snippets and examples provided for each section. If you find any incorrect information, please send us an email about the issue: mockstacks@gmail.com.


Share On:


Mockstacks was launched to help beginners learn programming languages; the site is optimized with no Ads as, Ads might slow down the performance. We also don't track any personal information; we also don't collect any kind of data unless the user provided us a corrected information. Almost all examples have been tested. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. By using Mockstacks.com, you agree to have read and accepted our terms of use, cookies and privacy policy.