Flutter Bloc Pattern 8.0.0 Call API

 Let's Learn how to call API/Webservices using bloc pattern of flutter

What is bloc? 


So as per official site of BlocThe goal of this package is to make it easy to implement the BLoC Design Pattern (Business Logic Component).

This design pattern helps to separate presentation from business logic. Following the BLoC pattern facilitates testability and reusability. This package abstracts reactive aspects of the pattern allowing developers to focus on writing the business logic.


Bloc 


Bloc is a more advanced class which relies on events to trigger state changes rather than functions. Bloc also extends BlocBase which means it has a similar public API as Cubit. However, rather than calling a function on a Bloc and directly emitting a new stateBlocs receive events and convert the incoming events into outgoing states.

Bloc Flow


Lets Start with code. 
Create your own Bloc class


class APIBloc extends Bloc<APIEvents, APIState> {
//
final APIRepo apiRepo;

APIBloc({required this.apiRepo}) : super(APIInitState()) {
on<APIEvents>(_addToValue);
}

Future<void> _addToValue(APIEvents event, Emitter<APIState> emit) async {
print("onEvent");
emit(APILoading());
emit.forEach(
Stream.fromFuture(_callAPI(event).catchError((onError) {
return onError;
})), onData: (value) {
if (value is Response)
return APILoaded(response: value as Response);
else {
return value as APIListError;
}
});
}

Future<Object> _callAPI(APIEvents event) async {
try {
if (event is FetchDashBoard) {
return await apiRepo.getDashboardData(event.requestParams);
} else {
throw (APIListError(
error: NoInternetException('No Internet'),
));
}
} on SocketException {
throw (APIListError(
error: NoInternetException('No Internet'),
));
} on HttpException {
throw (APIListError(
error: NoServiceFoundException('No Service Found'),
));
} on FormatException {
throw (APIListError(
error: InvalidFormatException('Invalid Response format'),
));
} catch (e) {
throw (APIListError(
error: UnknownException('Unknown Error'),
));
}
}
}
Now we need 2 Abstract classes for our bloc Events and States
Events of Above bloc:
abstract class APIEvents {}

class DoLogin extends APIEvents {
Object requestParams;
DoLogin({required this.requestParams});
}

class FetchDashBoard extends APIEvents {
Object requestParams;

FetchDashBoard({required this.requestParams});
}

Manage States of Bloc:

abstract class APIState extends Equatable {
@override
List<Object> get props => [];
}

class APIInitState extends APIState {}

class APILoading extends APIState {}

class APILoaded extends APIState {
final Response response;

APILoaded({required this.response});
}

class APIListError extends APIState {
final error;

APIListError({this.error});
}

You need custom exception classes:
class NoInternetException {
var message;
NoInternetException(this.message);
}

class NoServiceFoundException {
var message;
NoServiceFoundException(this.message);
}

class InvalidFormatException {
var message;
InvalidFormatException(this.message);
}

class UnknownException {
var message;
UnknownException(this.message);
}
And Last we have to create our services who makes actuall calls to API.
abstract class APIRepo {

Future<Response> getDashboardData(Object requestParams);

}

class APIServices implements APIRepo {
//

//Pay BusinessURL
static const _payBaseUrl = 'XYZ.net';
static const String _GET_DASHBOARD_DATA = '/dashboard';
static const String _LOGIN ='/user';

@override
Future<Response> getDashboardData(Object requestParams) async {
// TODO: implement getDashboardData

Uri uri = Uri.https(_payBaseUrl, _GET_DASHBOARD_DATA);
print("body " + uri.host);


Response response = await http.post(uri, body: json.encode(requestParams));
return response;
}

}

------------------------------------------
Now lets make Actual call from our UI screens.
Make your main parent class as BlocProvider
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Preferences.init();
runApp(BlocProvider<APIBloc>(
create: (context) => APIBloc(apiRepo: APIServices()),
child: MyApp(),
));
}

now inside your UI dart file in initstate call below API.
so it call API when loading.
you can call below line anywhere like button click, link click etc
@override
void initState() {
super.initState();
_loadData();
// readJson();
}

_loadData() async {
context.read<APIBloc>().add(FetchDashBoard(requestParams: inputParams));
}

@override
Widget build(BuildContext context) {
return
Scaffold(
appBar: AppBarWidget.getAppBar(AppLocalizations.of(context)!.login),
body: Container(
child: _body(),
),
);
}
_body() {
return BlocListener<APIBloc, APIState>(
child: loadDeshboardUIAfterCall(),
listener: (context, state) {
if (state is APIListError) {
final error = state.error;
String message = '${error.message}';
WidgetsBinding.instance!.addPostFrameCallback((_) {
BasicWidget.showSnackBar(context, message);
Navigator.of(_keyLoader.currentContext!, rootNavigator: true)
.pop();
});
Navigator.of(_keyLoader.currentContext!, rootNavigator: true)
.pop();
}
if (state is APILoaded) {
print("Loaded");
WidgetsBinding.instance!.addPostFrameCallback((_) {
print(state.response.body);
Navigator.of(_keyLoader.currentContext!, rootNavigator: true)
.pop();
setState(() {
dashboard =
Dashboard.fromJson(json.decode(state.response.body));
});
});
}
if (state is APILoading) {
print("Loading");
WidgetsBinding.instance!.addPostFrameCallback((_) {
ProgressDialogs.showLoadingDialog(context, _keyLoader);
});
}
});
}

Here you go. now just load screen you will get your response and then render it in UI.


Comments

Popular posts from this blog

Dynamic and responsive ListView in flutter

Manage Firebase storage using flutter