0 purchases
template package
template_package #
A template package for flutter apps.
This package will give you a set of tools to speed up and keep clean the development of your app.
State Management
Manages the flutter state in a similar but more flexible way then the common flutter bloc
UseCase interface to implement and organize the application logic of your app
LoggerDefault to log all your required logs
Flutter toasts are few of the utilities already incorporated.
BaseBlocDataState , BaseBlocPrimaryState states and BaseBlocEvent to organize the flow of data
between your widget and bloc
Features
Modules
State management
BaseWidget and TemplateBloc
Navigation
BaseBlocPrimaryState and sinkState
Logs
LoggerDefault
business rules modules
ABR EBR
dependency injection
BaseDependencyModule
Getting Started #
State Management #
https://www.youtube.com/watch?v=31tItITkkEs
Convert the widget
Let's start by converting your StatefullWidget in a widget that accepts states from the bloc (
TemplateBloc)
Extend your stateFull widget with BaseWidget and your state class with BaseState also add an extra
generic type in the <PageName, BaseBloc>
class MyWidget extends BaseWidget {
MyWidget(BaseBloc Function() getBloc) : super(getBloc);
copied to clipboard
class _MyWidgetState extends BaseState<MyWidget, BaseBloc> {
copied to clipboard
complete and simple widget example
import 'package:example/features/simple/my_event.dart';
import 'package:example/features/simple/my_state.dart';
import 'package:flutter/material.dart';
import 'package:template_package/base_widget/base_widget.dart';
import 'package:template_package/template_package.dart';
class MyWidget extends BaseWidget {
MyWidget(BaseBloc Function() getBloc) : super(getBloc);
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends BaseState<MyWidget, BaseBloc> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: myWidgetListener(),
);
}
// Note the streamBuilder listening the events from **MyCustomDataState** Stream
Widget myWidgetListener() {
return StreamBuilder(
stream: bloc.getStreamOfType<MyCustomDataState>(),
builder: (BuildContext context, AsyncSnapshot<MyCustomDataState> snapshot) {
if (snapshot.data == null)
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('no data received'),
],
));
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(snapshot.data?.myCustomData ?? ''),
SizedBox(height: 10),
sendDataToBlocButton(),
],
));
});
}
ElevatedButton sendDataToBlocButton() {
return ElevatedButton(
onPressed: () {
bloc.event.add(MyButtonTapCustomEvent('myAnalyticEvent', dataFromUi: "data from UI"));
},
child: Text('Send Data To Bloc'));
}
}
copied to clipboard
Create the bloc
import 'dart:async';
import 'package:example/features/initial/initial_event.dart';
import 'package:example/features/initial/initial_state.dart';
import 'package:template_package/template_package.dart';
class MyBloc extends BaseBloc {
/// create your custom streamController which will be listened by a unique widget inside your BaseWidget
final StreamController myDataStateController = StreamController<MyCustomDataState>();
MyBloc() {
// IMPORTANT to register your custom controllers here otherwise you will get a FlutterError
registerStreams([
myDataStateController.stream,
// someOtherController.stream
]);
}
@override
void onUiDataChange(BaseBlocEvent event) {
/// Any event sent by our base widget will end up here
/// Make sure you filter them by exact type (the Event needs to extend BaseBlocEvent)
if (event is MyCustomEvent) {
callMethodThatWillDoSomething(event.dataFromUi);
} else if (event is MyButtonTapCustomEvent) {
doSomethingOnButtonTap(event.dataFromUi);
}
}
void doSomethingOnButtonTap(String dataFromUi) {
print(dataFromUi);
myDataStateController.add(MyCustomDataState(myCustomData: "$dataFromUi with my custom data"));
}
void callMethodThatWillDoSomething(String dataFromUi) {
// todo do something
}
@override
void dispose() {
// dispose your controllers here
myDataStateController.close();
super.dispose();
}
}
copied to clipboard
create a file for your DataStates and create your dataStates
class MyCustomDataState extends BaseBlocDataState {
final String myCustomData;
MyCustomDataState({required this.myCustomData});
}
copied to clipboard
create a file for your Events and create your baseBlocEvents
class MyCustomEvent extends BaseBlocEvent {
final String myCustomDataFromUi;
MyCustomEvent(String? analyticEventName, this.myCustomDataFromUi) : super(analyticEventName);
}
copied to clipboard
Here is an overview of how the comunication between these elements will work
lets see how we compose these dependencies
/// if you don't have a custom analytics you can use a default one AnalyticsProxy()
/// The params passed in the constructor of the Bloc are very custom and with no limitations like getIt
final page = MyWidget(() => MyBloc());
copied to clipboard
Navigation #
https://www.youtube.com/watch?v=ErPXlRS5ACY
Navigation class
class NavigateToMyPage extends BaseBlocPrimaryState {
final Function(dynamic result)? onPop;
final String variable;
NavigateToMyPage({this.onPop, required this.variable, bool rootNavigator = true}) : super();
@override
call(context) {
final page = MyWidget(() => MyBloc());
Navigator.of(context).push(MaterialPageRoute(builder: (context) => page)).then((value) {
return onPop?.call(value);
});
}
}
copied to clipboard
How to navigate from a widget
void methodInMyWidget(BuildContext context) {
NavigateToMyPage(params).call(context);
}
copied to clipboard
How to navigate from a bloc
void methodInMyBloc() {
sinkState?.add(NavigateToMyPage(params));
}
copied to clipboard
Dependency injection #
https://www.youtube.com/watch?v=WWaI2dvCdBk
Create one or more SubModule (A place to create SomeModules)
class BlocSubModule extends ISubModule {
// late ServiceSubModule serviceSubModule;
@override
init(List<ISubModule> subModules) {
// You can get access to all the subModules of your app from this method
// serviceSubModule = subModules.singleWhere((element) => element is ServiceSubModule) as ServiceSubModule;
}
SecondBloc secondBloc(String someData) => SecondBloc(someData, serviceSubModule.userService());
}
copied to clipboard
Create the module that will initialise all your submodules
class DependencyModule extends BaseDependencyModule {
@override
List<ISubModule> createSubmodules() {
return [
BlocSubModule(),
// ServiceSubModule()
// CoreSubModule()
];
}
}
copied to clipboard
Create a provider, this will allow access to the subModules you need
99% of the cases you should only need access to the blocSubModule
class DProvider extends InheritedWidget {
final List<ISubModule> _subModuleList;
@override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
///Must be initialized only on the top of the widget three (Should Be The App Parent)
const DProvider(this._subModuleList, {required Widget child, Key? key}) : super(child: child, key: key);
static DProvider? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<DProvider>();
///Bloc SubModule
BlocSubModule get blocSubModule => _subModuleList.whereType<BlocSubModule>().first;
///Bloc SubModule
EbrSubModule get ebrSubModule => _subModuleList.whereType<EbrSubModule>().first;
}
copied to clipboard
Initialize the Dependency Module
Initialise your DependencyModule and pass all the submodules to the DependencyProvider Make sure
your App is wrapped by DProvider
void main() {
final dependencyModule = DependencyModule();
final app = DProvider(dependencyModule.getReadySubModules(), child: const MyApp());
runApp(app);
}
copied to clipboard
Get your dependency
final exampleDependency = DProvider.of(context)!.myCustomSubModule.myCustomDependency();
SecondPage(() => DProvider.of(context)!.blocSubModule.secondBloc(someData));
copied to clipboard
UseCases #
class SomeUseCase extends UseCase<InitialRepository> {
SomeUseCase(InitialRepository repository) : super(repository);
Future<void> getSomeData(RequestObserver<dynamic, SomeModel?> requestObserver) async {
await repository.getSomeData(RequestObserver<dynamic, SomeModel?>(
onListen: (SomeModel? data) {
// some extra logic
requestBehaviour.onListen?.call(data);
},
onError: requestBehaviour.onError));
}
Future<void> setSomeData(RequestObserver<SomeModel?, dynamic> requestObserver) async {
await repository.setSomeData(RequestObserver<SomeModel?, dynamic>(
requestData: requestBehaviour.requestData,
onListen: (_) {
requestBehaviour.onListen?.call(_);
},
onError: requestBehaviour.onError));
}
}
copied to clipboard
Full Clean Architecture Overview #
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.