fastor_app_ui_widget

Creator: coderz1093

Last updated:

Add to Cart

Description:

fastor app ui widget

Features #

Reduce 28% of chars in writing code & lines breaks.
Get ride of RenderFlex overflowed by pixels.
Basics ui widget with extra feature not found at normal widget. Example widget:

PageFastor, TextFastor, ImageFastor, RowFastor, ColumnFastor, TextFieldFastor ..etc


Can Write Widget Type Fastor Inside Regular Widget Flutter and vise versa:

Example : can use "ColumnFastor()" inside "Column()" regular and vise versa
Example : can use "TextFastor()" inside "Column()" regular or inside "ColumnFastor"
Don't Worry About Using Widget Type Fastor With Regular Widget


Helping make coding faster by use Utils. Example Classes:

NetworkManager, LanguageTools, ... etc.



Get Start #
1- import dependence in yaml file:
fastor_app_ui_widget:
copied to clipboard
2- at any class must import
import 'package:fastor_app_ui_widget/fastor_app_ui_widget.dart';
copied to clipboard
3- In main() write
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Fastor.initializeApp( );
runApp(const MyApp());
}
copied to clipboard
Reference #
documentation #
https://pub.dev/documentation/fastor_app_ui_widget/latest/index.html
Tutorial Content #
Content: Widget #



Page Shapes



TextView



Button


ImageView



















Row


Column


TextField


CheckboxFastor

















ScaffoldFastor
SwitchFastor
ScrollFastor
TableFastor
Progress
DropdownFastor
OTPTextFieldFastor
DateTextFieldFastor
DateRangeTextFieldFastor
CalenderFastor
MobileCountryFastor
TextFieldEmailOrPhoneFastor
TextFieldPasswordFastor
TabBarFastor
TableFastor
PageViewFastor
TimerCountDownFastor
BoarderHelper
Content: Classes Helper Tools #
NetworkManager
LangFastor
SaveFastor
ClipboardFastor
NavigationTools
ToolsWait
ToolsPhone
ZoneTools
InternetTools
ToolsValidation
ApiParserFastor
ValidateResponse
ResponsiveFastor
PaginateBarFastor


Start Your Tutorial #
PageFastor #
Scroll Screen #

Get ride of RenderFlex overflowed by pixels.



Simple Example Create "PageFastor"

@override
Widget build(BuildContext context) {
return PageFastor(this,
content: getContent() );
}
copied to clipboard

Example For Content Long Data

Widget getContent() {
return Column( children: getLongDataForTestScroll() ,);
}

List<Widget> getLongDataForTestScroll() {
List<Widget> data = [];
for (int i = 1; i <= 70; i++) {
var w = TextFastor(
"Get ride of RenderFlex, data number $i",
fontSize: 15,
width: 300,
color: Colors.yellow,
margin: EdgeInsets.all(5),
);
data.add(w);
}
return data;
}
copied to clipboard
Toolbar Custom Shape #
Create any shape of Toolbar you want in Custom shape then put it at page template by using parameter "toolbar"

@override
Widget build(BuildContext context) {
return PageFastor(this,
toolbar: ToolbarSimpleFastor( context, "Page Shapes"),
toolbar_height : 70,
content: getContent() );
}
copied to clipboard

The Default height of toolbar is 70
When the toolbar you draw different than 70 you can set height of toolbar by using "toolbar_height"

Background Feature #
Asset
set Image png to background, to make all content scrolling while the background image still hold at background

Asset with Opacity

@override
Widget build(BuildContext context) {
return PageFastor(this,
toolbar: ToolbarSimpleFastor( context, "Page Shapes"),
toolbar_height : 70,

//background
assetBackground: const AssetImage("assets/images/background.png"),
assetBackgroundOpacity: 0.3,

content: getContent() );
}
copied to clipboard
Custom Widget
set Custom Widget hold at background, to make all content scrolling while the background widget still holding while scrolling

@override
Widget build(BuildContext context) {
return PageFastor(this,
toolbar: ToolbarSimpleFastor( context, "Page Shapes"),
toolbar_height : 70,

//background
widgetBackground: CustomWidgetBackground(),

content: getContent() );
}
copied to clipboard
Navigation Bottom #
Custom Shape Navigation #

set Custom Widget hold at bottom of screen to navigate between multi screens
@override
Widget build(BuildContext context) {
return PageFastor(this,

//toolbar
toolbar: ToolbarSimpleFastor( context, "Page Shapes"),
toolbar_height : 70,

//navigation bottom
navigationBottom: NavigationFastor( context, 0),
navigationBottom_height: 70,
homeButtonsBackgroundColor: HexColor( "#1593bc"), //color background for home buttons

content: getContent() );
}
copied to clipboard
Change color #
Color of Home Buttons Android Device
By using parameter "homeButtonsBackgroundColor" you can write hexcode color




@override
Widget build(BuildContext context) {
return PageFastor(this,

homeButtonsBackgroundColor: HexColor( "#1593bc"), //color background for home buttons

content: getContent() );
}
copied to clipboard
Color of Status Bar
By using parameter "statusBarColorCustome" you can write hexcode color




@override
Widget build(BuildContext context) {
return PageFastor(this,

statusBarColorCustome: HexColor( "#595629"),

content: getContent() );
}
copied to clipboard
ScaffoldFastor #
Navigation To Transparent Page #

To Navigate Need to User class "NavigationTools" found in fastor helper classes.
Just Call for navigation push

NavigationTools.pushTransparent( SimpleScreen() );
copied to clipboard

or for animation navigation

NavigationTools.pushTransparentAnimateFade( SimpleScreen() );
copied to clipboard

In Your Screen Page set "shapeTransparent: true "

@override
Widget build(BuildContext context) {
return ScaffoldFastor(
shapeTransparent: true,
body: directionWithPageBlocState(),
);
}
copied to clipboard

to change transparent color dialog use parameter "shapeTransparentColor"



Tutorial : TextFastor #
Why use Fastor widget ? #

Reduce 28% of chars in writing code
Reduce 28% of lines breaks

Feature Fastor #

Margin without use Container
Padding without use Container
Decoration background without use Container
Text Align without use Style
Text Decoration without use Style
Font size without use Style
Font family without use Style
On tap without use GestureDetector

Table Result : Percentage of code writing reduction #
Using Fastor widget reduce writing code by 28% chars when you compare with normal flutter widget

See source code compare between Fastor and Normal at this page
Get Start #
Full Example
TextFastor(
"Text Fastor Simple" ,
textAlign: TextAlign.center,
textDecoration: TextDecoration.underline,
color: Colors.blue,
fontSize: 25,
fontFamily: FontProject.marina,
margin: EdgeInsets.all( 25 ),
padding: EdgeInsets.all( 10),
decoration: BoarderHelper.cardView(
colorLine: Colors.red,
colorBackground: Colors.yellow,
radiusSize: 15
),
// backgroundColor: Colors.green,
maxLines: 2,
onPressed: (){
Log.i( "click on fastor widget");
},
);
copied to clipboard
Compare Text() Normal Vs TextFastor() #


Widget getContent() {
return Column( children: [
textview_normal(),
textview_fastor()
],);
}

Widget textview_normal() {
return GestureDetector( child: Container(
child: Text(
"Text Normal" ,
textAlign: TextAlign.center,
maxLines: 2,
style: TextStyle(
decoration: TextDecoration.underline,
decorationColor: Colors.blue,
color: Colors.blue,
fontSize: 25,
fontFamily: FontProject.marina
),
),
margin: EdgeInsets.all( 25 ),
padding: EdgeInsets.all( 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.red ) ,
borderRadius: BorderRadius.all(
Radius.circular( 15 )
) ,
color: Colors.yellow //background color
),
// color: Colors.green,
),
onTap: (){
Log.i( "click on Normal");
},
);
}


Widget textview_fastor() {
return TextFastor(
"Text Fastor" ,
textAlign: TextAlign.center,
textDecoration: TextDecoration.underline,
color: Colors.blue,
fontSize: 25,
fontFamily: FontProject.marina,
margin: EdgeInsets.all( 25 ),
padding: EdgeInsets.all( 10),
decoration: BoarderHelper.cardView(
colorLine: Colors.red,
colorBackground: Colors.yellow,
radiusSize: 15
),
// backgroundColor: Colors.green,
maxLines: 2,
onPressed: (){
Log.i( "click on fastor widget");
},
);
}
copied to clipboard


Tutorial : ButtonFastor #
Feature Fastor #

Margin without use Container
Padding without use Container
Decoration background without use Container
Text Align without use Style
Text Decoration without use Style
Font size without use Style
Font family without use Style
On tap without use GestureDetector

Get Start #
See source code compare between Fastor and Normal at this page
Full Example
ButtonFastor(
"Button Fastor",
() {
print("click on btn type fastor");
},
margin: EdgeInsets.symmetric( vertical: 40),
textColor: Colors.blue,
background: Colors.black,
fontFamily: FontProject.marina,
textFontSize: 15,
borderLine: Colors.blue,
borderRadius: 15,
);
}
copied to clipboard
Compare Text() Normal Vs TextFastor() #

EdgeInsets.symmetric( vertical: 30),
child: ElevatedButton(
onPressed: () {
print("click on btn type normal");
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black,
shape: RoundedRectangleBorder (
borderRadius: BorderRadius.circular(15),
side: BorderSide(width: 1,color: Colors.blue)
)),
child: Text(
"Button Normal",
textAlign: TextAlign.center,
maxLines: 2,
style: TextStyle(
color: Colors.blue,
fontSize: 15,
fontFamily: FontProject.marina
),
)
),
// color: Colors.green,
);
copied to clipboard

progress of button controle by:

bool? showProgress;
double? sizeProgress;
Color? colorProgress;
copied to clipboard


Tutorial : ImageFastor #
Feature Fastor #

Background color/image without use Container
Padding without use Container
Aspect Ratio Image
Radius Corner for Image
On tap without use GestureDetector

Set Image type url + Corner Radius + Background color + opacity + onPressed + margin #

ImageFastor(
context: context,
width: 278,
height: 181,
margin: EdgeInsets.all( 10),
radius_all: 25,
boxFit_background: BoxFit.cover,
urlBackground: square_url_image_example,
colorBackground: Colors.amber,
opacity: 0.7,
onPressed: (){
print("click on image");
},
);
}
copied to clipboard
Set Image type assets + Corner Radius #

return ImageFastor(
context: context,
width: 300,
height: 600,
radius_all: 25,
assetAspectRatio: AssetImage("assets/images/background.png"),
);
copied to clipboard
Set Image auto-responsive between website screen desktop, website screen mobile and mobile real device #
same code working in all platform with save the aspect ratio size of image with all screen size of any device.
Screen Size : Website Desktop

Screen Size : Website Browser Mobile Chrome

Screen Size : Android Mobile

ImageFastor(
context: context,
width: 300,
height: 300,
radius_all: 25,
assetAspectRatio: AssetImage("assets/images/logo_example.png"),
responsive_auto: true,
);
copied to clipboard


Tutorial : RowFastor #
Feature Fastor #

"RowScrollFastor" Scroll Horizontal for get ride of RenderFlex overflowed by pixels

RowScrollFastor #
Using Fastor widget Scroll Horizontal for get ride of RenderFlex overflowed by pixels

See source code compare between Fastor and Normal at this page
RowScrollFastor( children: getChildren() );
copied to clipboard


Tutorial : ColumnFastor #
Feature Fastor #

Have Decoration
Have Space : Margin, Padding
Have Alignment
Can Set Fixed size width/height

ColumnFastor #
Using Fastor widget Scroll Horizontal for get ride of RenderFlex overflowed by pixels

See source code compare between Fastor and Normal at this page
ColumnFastor(
children: getChildren(),
margin: EdgeInsets.only(top: 20, bottom: 20, left: 60, right: 60),
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
borderRadius: BorderRadius.all(Radius.circular(15)),
color: Colors.yellow //background color
),
);
copied to clipboard


Tutorial : TextFieldFastor #
Feature Fastor #

Call from constructor of class

// validate
this.validator,
this.autovalidateMode,


//text and hint
this.hint_text,
this.text_color,
this.fontSize,
this.hint_color,

//boarder and underline
this.isRemoveUnderline = false,
this.isShowBoarder,

//background
this.background_color,
this.decoration, //at the Container

//spaces
this.padding,
this.margin,

//controller
this.controller,
this.onChanged,

//input content type
this.keyboardType,
this.obscureText = false,

//size and max/min
this.width,
this.maxLength,
this.maxLines,
this.minLines,

//other
this.textAlign ,
this.focusNode,
this.prefixIcon
copied to clipboard
Get Start #

Tutorial : Without Use Any State Management

use "setState()"
Create Variable at class

var email_txt = "";
var email_valid = AutovalidateMode.disabled;
copied to clipboard

Create Widget

return TextFieldFastor(
autovalidateMode: email_valid,
margin: EdgeInsets.only( top: 10 ),
padding: EdgeInsets.all( 5),
background_color: Colors.white,
validator: ValidatorTemplate.email( ),
keyboardType: TextInputType.emailAddress,
onChanged: (s){
email_txt = s;
Log.i( "tf_email() - change s: $s ");
}
);
copied to clipboard

Validate Form after click of button

if(validateEmailAfterClick()) {
///TO-DO : After success success field
}
copied to clipboard
bool validateEmailAfterClick() {
var result = true; //default good
//email
if ( ToolsValidation.isEmail( email_txt ) == false ){
Log.i( "missed email");
result = false;
setState(() {
email_valid = AutovalidateMode.always;
});
}
return result;
}
copied to clipboard
Tutorial : Use State Management Type "Cubit" like using "BLoc"


Using "Cubit" it similar as Using "BLoc"


Create Cubit



class AuthCubit extends Cubit<AuthState> {
final LogInUseCase logInUseCase;

AuthCubit( ) : super(AuthInitialState()) {}

static AuthCubit get(context) => BlocProvider.of(context);

AuthResponse authResponse = AuthResponse();

Future login(String email_txt, String pass_txt) async {
//....
}

void resetError() {
emit( AuthInitialState() );
}

}
copied to clipboard

Create Cubit State

abstract class AuthState {}

class AuthInitialState extends AuthState {}

class AuthLoadingState extends AuthState {}

class AuthSuccessState extends AuthState {
//.....
}

class AuthErrorState extends AuthState
{
//.....
}

copied to clipboard

Declare Variable at class


AuthState? stateCurrent;

var emailValid = AutovalidateMode.disabled;
var emailController = TextEditingController();

var passValid = AutovalidateMode.disabled;
var passController = TextEditingController();
copied to clipboard

This Way You Can Set Error Message To UI


void setErrorToInputFields() {
//email
if (ToolsValidation.isEmail(emailController.text) == false) {
Log.i("missed email");
emailValid = AutovalidateMode.always;
}
//pass
if (ToolsValidation.isPasswordValid(passController.text) == false) {
Log.i("missed pass");
passValid = AutovalidateMode.always;
}
}

copied to clipboard

Listener Cubit State


Widget getContent() {
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
stateCurrent = state;

if (state is AuthSuccessState) {
RouterPage.home(context);
}
},
builder: (context, state) {


if (state is AuthErrorState) {
setErrorToInputFields();
printMessageError();
}

return Column(children: [
tfEmail(),
tfPass(),
]);
);
}

copied to clipboard

Create Widget TextFieldFastor


Widget tfEmail() {
return TextFieldFastor(
hint_text: "Email",
controller: emailController,
autovalidateMode: emailValid,
margin: EdgeInsets.only(top: 10),
padding: EdgeInsets.all(5),
background_color: Colors.white,
validator: ValidatorTemplate.email(),
keyboardType: TextInputType.emailAddress,
onChanged: (s) {
// Log.i("tfEmail() - change s: $s ");

bool isNotInit = stateCurrent! is AuthInitialState;
if (isNotInit) {
AuthCubit.get(context).resetError();
}
});
}

Widget tfPass() {
return TextFieldFastor(
hint_text: "Password",
controller: passController,
autovalidateMode: passValid,
margin: EdgeInsets.only(top: 10),
padding: EdgeInsets.all(5),
background_color: Colors.white,
validator: ValidatorTemplate.pass(),
keyboardType: TextInputType.number,
onChanged: (s) {
// Log.i("tf_pass() - change s: $s ");
});
}

copied to clipboard
Class Utils For TextField #

class ValidatorTemplate : This have methods utils used at parameter "validator"

Class "TextFieldBackendErrorFastor" : #

This Class used for handle error from backend for every input fields.
Example error array backend response :

{
"errors": {
"email": [
"The email provided is incorrect."
]
}
}
copied to clipboard
Handle Error Array


We need in UI to handle every error array and to show to message error under every textfields


Example :


return TextFieldFastor(
hint_text: "Email",
controller: emailController,
autovalidateMode: emailValid,
validatorCustom: ValidatorTemplate.email(),
errorBackendJson: errorState?.errors,
errorBackendKeyJson: "email",
// errorBackendKeyJson2: "role",
margin: EdgeInsets.only(top: 10),
padding: EdgeInsets.all(5),
background_color: Colors.white,
keyboardType: TextInputType.emailAddress,
onChanged: (s) {
// Log.i("tfEmail() - change s: $s ");

bool isNotInit = stateCurrent! is AuthInitialState;
if (isNotInit) {
AuthCubit.get(context).resetError();
}
});

copied to clipboard


Tutorial : CheckboxFastor #
Feature Fastor #

set color from constructor for active/inactive: color_inactive, color_active
Set Text Style : color, font size, weight.
Remove default padding found when use normal checkbox widget.

Get Start #

Simple Example

Create Variable at class

bool isAgree = false;
copied to clipboard

Create Widget

return CheckboxFastor( context: context, value: isAgree,
margin: EdgeInsets.only(top: 10),
text: "Are you agree to terms and condition.",
text_color: Colors.brown,
text_dimen: 20,
color_inactive : Colors.brown,
color_active: Colors.green,
onChanged: (updatedValue) {
setState(() {
isAgree = updatedValue!;
});
}
);
copied to clipboard


SwitchFastor #

Feature Padding, Margin.
Feature send "TextStyle" in parameter constructor.
Feature Color action active and dis-active in parameter constructor.

Example Using with "Cubit" StateManagment #

ui :

Widget _switchVipButton(){
return SwitchFastor(defaultValue: cubit!.createReservation.isVip,
onChange: (updateStatus){
Log.i( "_switchVipButton() updateStatus: $updateStatus");
cubit!.switchVipStatus(updateStatus);
});
}
copied to clipboard

Save the last updated status of change in variable


when calling

cubit!.createReservation.isVip
copied to clipboard
it just boolean varaible to carry data of switch status
bool isVip = false;
copied to clipboard

create new state for class Cubit state

class ReservationCreateVipStatusChangeState extends ReservationState {}
copied to clipboard

call the change of switch value by calling "State Cubit"

void switchVipStatus(bool updateStatus) {
createReservation.isVip = updateStatus;

emit(ReservationCreateVipStatusChangeState());
}

copied to clipboard


ScrollFastor #
Example #


@override
Widget build(BuildContext context) {
return Scaffold(
body: createUI()
);
}

Widget createUI() {

var scrollPage = ScrollFastor( child: contentFormColumn() );

var cont = Container(
padding: EdgeInsets.only(top: ToolbarCustom.heigh),
margin: EdgeInsets.only( left: 16, right: 16, bottom: 16),
child: scrollPage ,
);

return Stack(
children: [
cont,

ToolbarCustom(pageContext: context, title: "Add Reservation"),

],
);
}

Widget contentFormColumn(){
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [

Text("input field 1"),
Text("input field 2"),
Text("input field 3"),
Text("input field 4"),
Text("input field 5"),
Text("input field 6"),
Text("input field 7"),
],
);
}

copied to clipboard


TableFastor #

feature scroll both horizontal and vertical axis

Progress #
TableFastor( { required this.listRow,
this.showProgress,
this.sizeProgress,
this.colorProgress,
} ){
copied to clipboard


Progress #

shape Circle

ProgressCircleFastor( {
this.color,
this.size
})
copied to clipboard

shape Spin Plugin "package:flutter_spinkit"

ProgressSpinFastor( {
this.color,
this.size
})
copied to clipboard


PageViewFastor #
Features #

easy to set colors of indicator selected and unselcted
set frame width / height.
indicator points clicked to navigate to child widget.

Example #

Widget pageView(){
return PageViewFastor(
widht: DeviceTools.isLandscape(context)? ResponsiveConstant.widthChangeInCaseLandscape: DeviceTools.getWidth(context) ,
height: 400,
colorIndicatorOn: ColorProject.primary,
colorIndicatorOff: ColorProject.black,
children: [
firstBoarder(),
secondBoarder()
],
onChangePage: (p){
Log.i( "change p : " + p.toString() );
},);
}

copied to clipboard


TimerCountDownFastor #

count down text to shape format: mm:ss
easy to use with callback when timer complete.
change font size, font family, text color.

TimerCountDownFastor(
contextPage: context ,
second: 7,
callBack: () {
///TO-DO when complete timer
}
)
copied to clipboard


DropdownFastor #
Simple Example : #

simple string[] array

return DropdownFastor(
width: widget.width - 16 -16 ,
names: HelperEumCrypto.getListNameEnumCrypto(), //string[] array
hintText: "select one",
previousSelectedText: selectedCrypto?.name,
listener: (name, position ) {


setState(() {
selectedCrypto = HelperEumCrypto.mapNameToEnum(name);
Log.i("dropdownCrypto() - currentCryptoReading: $selectedCrypto /position: $position");

widget.listener(selectedCrypto);
});
},
);
copied to clipboard
Set previous selected name #
previousSelectedText: selectedCrypto?.name,
copied to clipboard
to remove the selected item from dropdown programmatically #

example dropdown


Widget dropdownFastor(){
return DropdownFastor(
width: 150,
names: ["male", "female", "prefer not answer"],
iconSize: 24,
previousPosition: previousPosition,
listener: (name, position){
Log.i( "dropdownFastor() - name: " + name.toString() + " /position: $position" );
previousPosition = position;
},
onRemoveSelected: (){
Log.i( "dropdownFastor() - onRemoveSelected " );
removeSelectedDropdown();
});
}

copied to clipboard

After "onRemoveSelected" called, means you need to remove old selected :

removeSelectedDropdown(){
setState(() {
previousPosition = null;
});
}

copied to clipboard
Handle Direction ltr or rtl : #
return DropdownFastor(
textDirection: getTextDirection(),
copied to clipboard
Show Progress View Will Loading Data #
this.showProgress,
this.sizeProgress,
this.colorProgress,
copied to clipboard


OTPTextFieldFastor #

you can listener when user complete write "OTP CODE"
Listener while user still writing the OTP Code
write how many fields otp needed 4 or 5 or 6. maximue is 6 otp code
choose style from constructor


//size
double? widthOTP ;
double? heightByPadding ;
double? margin ;

//color
Color? colorText;
Color? colorHint;
String? fontFamily;
double? fontSize;
Decoration? decoration;
copied to clipboard

example

var otpWidget = OTPTextFieldFastor(
countNumber: 5,
decoration: BoarderHelper.box(
colorLine: ColorApp.black,
colorBackground: Colors.transparent
),
fontFamily: ProjectFonts.DarkerGrotesque_Black_900,
fontSize: 13,
// colorHint: ColorApp.gray,
colorText: ColorApp.black,
onComplete: ( isComplete ){
Log.i("otpFields() - isComplete: $isComplete");
},
onChangeCode: (codeUpdate ) {
Log.i("otpFields() - onChangeCode: $codeUpdate");
},
);
copied to clipboard


DateTextFieldFastor #

simple date picker one day in shape textfield boarder

return DateTextFieldFastor(
width: DeviceTools.getHalfWidth(context),
boarderColor: Colors.transparent,
boarderRadius: 0,
fontSize: 12,
iconColor: HexColor(ColorProject.blue_fish_front),
callback: (date){
dateSearchSelected = date;
});
copied to clipboard

add "previousSelectedText" parameter in class "DateTextFieldFastor"

return DateTextFieldFastor(
previousSelectedText: "2024-01-20",
});
copied to clipboard


DateRangeTextFieldFastor #

simple date picker for range between two days in shape textfield boarder
class "DateRangePickerResult" to handle callback listener of date picker range

return DateRangeTextFieldFastor(
width: DeviceTools.getWidth(context),
boarderRadius: 0,
dateStart : dateRangeSelected?.start,
dateEnd : dateRangeSelected?.end,
callback: (dateSelected){
dateRangeSelected = dateSelected;
});
copied to clipboard


MobileCountryFastor #

widget TextField "MobileCountryFastor" use plugin "country_code_picker" with some customization in phone

return MobileCountryFastor(
width: getWidthOfInputField(),
colorUnderlineInputField: ColorApp.underlineInputField,
textStyle: TextStyle(
color: ColorApp.black,
fontSize: 13,
fontFamily: ProjectFonts.DarkerGrotesque_Regular_400
),
controller: phoneController,
title: "ENTER PHONE NUMBER",
callback: (String country_code, String phone) {
cubit!.requestRegister.countryCode = country_code;
cubit!.requestRegister.phoneNumber = phone;
}
);
copied to clipboard

feature

MobileCountryFastor( {
required this.width,
required this.callback,
this.widthCountryCode,
this.textStyle,
this.controller,
this.decoration,
this.textInputType,
this.padding,
this.title,
this.hint,
this.hint_color,
this.colorUnderlineInputField,
this.favoriteCountryCodeArray,
this.initialSelection,
this.suffixIcon,
this.isHideCountryPicker
}) {
copied to clipboard


TextFieldEmailOrPhoneFastor #

widget "TextFieldEmailOrPhoneFastor" use plugin "country_code_picker" with some customization in phone
allow user to enter both email format or phone, the country code picker auto hide/show when user write phone character or not.


Widget emailOrPhone(){
var imageEmail = Image.asset( "assets/images/email.png", width: 28, height: 28,);
var iconEmail = Padding(child: imageEmail , padding: EdgeInsets.only(bottom: 18, right: 8) );

var mobile = TextFieldEmailOrPhoneFastor(
width: ProjectDimen.allDeviceWidthMinesMarginLeftRight(context),
colorUnderlineInputField: ColorApp.underlineInputField,
textStyle: TextStyle(
color: ColorApp.black,
fontSize: 13,
fontFamily: ProjectFonts.DarkerGrotesque_Regular_400
),
// countryCodeDefault: ConstantEnvironment.isTestEnvironment?"+20":"+966",
controller: emailOrPhoneController,
title: "ENTER EMAIL ADDRESS OR PHONE NUMBER",
hint: "Email address or phone number",
iconCaseEmail : iconEmail,
callbackEmail: ( email ) {
Log.i("callbackEmail - s: $email");
cubit!.loginRequest.emailOrPhone = email ;
// cubit!.emitToInitialState( );
},
callbackPhone: (String country_code, String phone) {
Log.i("callbackPhone - s: $phone");
cubit!.loginRequest.countryCode = country_code;
cubit!.loginRequest.emailOrPhone = phone ;
// cubit!.emitToInitialState() ;
},
);

return Container(
child: mobile,
margin: EdgeInsets.symmetric(horizontal: ProjectDimen.leftRightPageContent),
);
}

copied to clipboard
Example Arabic Change Icon Direction #

code use "textDirection"

var mobile = TextFieldEmailOrPhoneFastor(

textDirection : LanguageHelper.getTextDirection(),
....
);

copied to clipboard

user any class helper to return direction
you can also used helper fastor langauge class. "LangFastor.getTextDirection()"

class LanguageHelper {


static TextDirection getTextDirection() {
var lang = translator.activeLanguageCode;
var isArabic = lang == "ar";
if (isArabic) {
return TextDirection.rtl;
} else {
return TextDirection.ltr;
}
}

}

copied to clipboard


TextFieldPasswordFastor #

icon password click to show/hide password
can custom iconWidget for show/hide
title text optional to set above TextField
Can send custom Title Widget
optional validator fourm
optional set type of AutoValidateMode

Example Simple : Not Used Custom Icon #
return TextFieldPasswordFastor(
title: "ENTER PASSWORD",
hint_text: "Enter password",
iconPasswordShow: Icon(), //optional: this custom icon
iconPasswordShow: Icon(), //optional: this custom icon
passwordController: passwordController,
margin: EdgeInsets.only(top: 10 ), //optional:
validatorCustom: ValidatorTemplate.pass(error_text: "*Enter password at least 8 char." ), //optional
autoValidateMode: AutovalidateMode.onUserInteraction,
onChanged: (s) {

},
);
copied to clipboard
Example Use Custom Icon #
@override
Widget build(BuildContext context) {
return TextFieldPasswordFastor(
titleWidget: title(),
hint: "Enter password".trf(),
iconPasswordShow: iconEyeShow(),
iconPasswordHidden: iconEyeHide(),
passwordController: widget.passwordController,
margin: EdgeInsets.symmetric(horizontal: ProjectDimen.leftRightPageContent),
validatorCustom: ValidatorTemplate.pass(error_text: "*Enter password at least 8 char.".trf()),
autovalidateMode: AutovalidateMode.onUserInteraction,
onChanged: widget.onChanged,
colorIconEyePassword: ColorApp.pink,
);
}


Widget iconEyeShow(){
var img = Image.asset(
"assets/images/eye_show.png",
width: 14,
height: 14,
color: ColorApp.primary,
);
return Container(
child: img,
color: Colors.transparent,
padding: EdgeInsets.only( right: 10, left: 10),
);
}


Widget iconEyeHide(){
var img = Image.asset(
"assets/images/eye_hide.png",
width: 14,
height: 14,
color: ColorApp.primary,
);
return Container(
child: img,
color: Colors.transparent,
padding: EdgeInsets.only( right: 10, left: 10),
);
}


title() {
return Container(
child: TextCustomBold("ENTER PASSWORD".trf(), fontSize: 15 ),
margin: EdgeInsets.only(bottom: 20),
);
}


copied to clipboard


TabBarFastor #
example simple #

int selectedTabPosition = 0 ;

Widget tabBarFastor(){
return TabBarFastor(
width: DeviceTools.getWidth(context),
height: 50,
names: ['UPCOMING'.trf() , 'COMPLETED'.trf(), 'CANCELLED'.trf()],
indicatorColor: ColorApp.primaryByVip(),
unselectedLabelColor: ColorApp.blackOpacity,
fontSize: 16,
fontFamily: ProjectFonts.DarkerGrotesque_Medium_500,
spaceBetweenLabelAndUnderline : 10,
decorationTabBar: BoarderHelper.box(
colorBackground: Colors.grey[200],
colorLine: Colors.transparent
),
pressed: (int index , String title ) {

Log.i("tabBarFastor() - pressed - index: $index /title: $title");
setState(() {
selectedTabPosition = index;
});

});
}

copied to clipboard

to show the content ui for each tab item, just use if condition to show the view you want below tabbar

Some Features #

feature show underline in all item tabs

return TabBarFastor(
underlineShownBelowAllTabs : true,

copied to clipboard


CalenderFastor #
Feature : #

title is optional
decoration background optional
types calender "CalenderTypeFastor.birthday" for handle picker birthday logic
"height" parameter to resize to specific fixed height.
style of font:
colorTextTitle, colorTextSelected, colorTextUnSelected, fontFamily, fontSize

Example : #

example type birthday picker

var calender = CalenderFastor(
title: "ENTER DOB" ,
decoration: BoarderHelper.cardView(
colorLine: ColorApp.grayOpacity,
radiusSize: 3,
colorBackground: ColorApp.pinkOpacity
),
calenderTypeFastor: CalenderTypeFastor.birthday,
dateSelected: cubit!.requestRegister.birthday,
colorTextTitle: ColorApp.black,
colorTextSelected: ColorApp.black,
colorTextUnSelected: ColorApp.gray,
fontFamily: ProjectFonts.DarkerGrotesque_Bold_700,
callback: ( dateTime, ddMMyyyy ) {
cubit!.requestRegister.birthday = ddMMyyyy;
},
);
copied to clipboard
Types #

type of calender "CalenderTypeFastor" see Enum

enum CalenderTypeFastor { dateStartFromToday, birthday }
copied to clipboard


BoarderHelper #

BoxDecoration easy to use in Container( decoration: )
BoxDecoration utils to be used in "Container()"

Container(
decoration: BoarderHelper.cardViewBlur(
colorBackground: ColorApp.greyDarkButton,
colorBlur: ColorApp.greyOpacity,
radiusSize: 30,
widthShadow: 5,
blurStyle: BlurStyle.normal,
makeBlurColorDark: true,
)
);
copied to clipboard


Classes Helper Tools #
NetworkManager #

Feature #

Fix Error "XMLHttpRequest" found in platform Web.
Feature "CallBack()" return value like interface.
Feature NetworkType.file : to upload file in all platform( Android/IPhone/Web).
Class "NetworkHeaderTools" to generate "bearerToken()" and "basicAuth_usernameAndPassword()" easily.

Simple Example #

There is many steps to call API, you need to prepare: Body, Url, Header, token.
Here is guide

1- Generate Body
Map<String, dynamic> bodyParameter = Map();
bodyParameter[ "payment_method"] = "cash";
bodyParameter[ "price"] = "123";
copied to clipboard
2- Call Class "NetworkManagerDio.dart"
We will return the response in format JSON
Response response = await NetworkManagerDio().post( url , body: bodyParameter );
if (response.statusCode == 200) {
var result = CityResponse.fromJson( response.data);
return result;
}

copied to clipboard
NetworkConfigDio #

This class responsible for add "BaseUrl" to all EndPoint apis.

var config = NetworkConfigDio( baseUrl: "http://example.com");
NetworkManagerDio( { config: config})
copied to clipboard

This Class responsible for add header in all apis like token

var config = NetworkConfigDio(
baseUrl: "http://example.com",
headers : myHeader
);
NetworkManagerDio( { config: config})
copied to clipboard
NetworkType #
NetworkManagerDio().get();
NetworkManagerDio().post();
NetworkManagerDio().put();
NetworkManagerDio().delete();
NetworkManagerDio().file();
copied to clipboard
Enum of Network type
enum NetworkTypeDio{
get,
post,
put,
delete,
file
}
copied to clipboard
Time Out
NetworkManagerDio().get( timeOutSecond: 50 );
copied to clipboard
Handler Errors

Handler Error XMLHttpRequest by using "NetworkManagerDio" set parameter "handleErrorXMLHttpRequest = true"


Upload File

What is class "xFile" it's famous used by plugin "cross_file".
Generate XFile used by image picker

class "NetworkRequestFile"
this class used to set the path of file of "xFile" and set the key/value of file
Example upload file type "xFile"

Future _startAPI() async {

//body request
Map<String, dynamic> body = Map();
body[ "payment_method"] = "cash";
body[ "price"] = "123";

//set type requestFile
NetworkRequestFile? requestFile = NetworkRequestFile.fromXFileAndBody( xFile!, body );

//header
var token = await UserSingleTone.instance().getToken();
Map<String, String> header = NetworkHeaderTools.bearerToken( token );

NetworkManagerDio().file(urlApiLink,
requestFile: requestFile,
headers: header,
callback: (status, msg, json){

parseJson(json );

});
copied to clipboard

You Can Make Type Method PUT by call method

requestFile.setTypeMethodPut();
copied to clipboard

Types Of Webservice Network Dependence #

dio : use class "NetworkManagerDio.dart"
http : use class "NetworkManagerHttp.dart"



LangFastor #
Get Start Setup #

from main method call:

await LangFastor.setupFromMainMethod();

copied to clipboard

from MyApp() class inside build() method write :

class MyApp extends StatelessWidget {


// This widget is the root of your application.
@override
Widget build(BuildContext context) {
LangFastor.setupFromBuildMethod(context);
.....
}
}
copied to clipboard

in yaml add path of assets

assets:
- assets/lang/
copied to clipboard

create new json file named "ar.json" in path :
assets/lang/ar.json

Section: How To Change Language Direction #
Arabic right-to-left "RTL" Layout Direction
How to auto change direction from ( English => Arabic ) left/right

@override
Widget build(BuildContext context) {

return Scaffold(
body: SafeArea(child: getDirection()),
resizeToAvoidBottomInset: true,
);
}
Widget getDirection() {
return Directionality(
textDirection: LangFastor.getTextDirection(),
child: Builder(
builder: (BuildContext context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
),
child: blocConsumer(),
);
},
),
);
}

copied to clipboard
Method Handle Arabic/English #
Set/Get Arabic Value

How To Set Arabic or English

LangFastor.setArabic(context);
LangFastor.setEnglish(context);
copied to clipboard

How to check anywhere lang is arabic
this return boolean

LangFastor.isArabic
copied to clipboard
Dropdown Handle Direction Arabic #
DropdownFastor(
textDirection: LangFastor.getTextDirection(),
copied to clipboard
Handle Stack Direction Left/Right Arabic #
PositionFastor.directional(
textDirection: TextDirection.rtl,
child: Text(""),
top: 0,
start: 0 ,
)
copied to clipboard

use "PositionedFastor.langFastor()" auto choose which langauge use used when use LangFastor plugin

PositionedFastor.langFastor(
child: Text(""),
top: 0,
start: 0 ,
)
copied to clipboard
Handle The Container Alignment Left/Right Arabic #

Fix Arabic Alignment of the "Container" by using "LangFastor.getAlignmentGeometry()" return left alignment

Container(
width: DeviceTools.getWidth(contextPage),
alignment: LangFastor.getAlignmentGeometry(),// Alignment.topRight in arabic, while in english Alignment.topLeft
child: menuFrameUI(),
);
copied to clipboard


add methods "getAlignmentGeometryStart()" it's return left Container( alignment ) in english,
while in arabic return right Container( alignment )


add methods "getAlignmentGeometryEnd()" it's return right Container( alignment ) in english,
while in arabic return left Container( alignment )


Section: Arabic Translation #

There is Many Ways To Translate English text to Arabic Text :

First One Using json file to save english text as key while save arabic transalation as value
Second One Is Translate english to arabic In One Line.



First Way : Using Json File To Save Translation

write json with key/value for each english word


in path project : assets/lang/ar.json

{
"Login" : "تسجيل الدخول" ,
"" : ""
}
copied to clipboard

In Widget Text or Button-Text


Use "english-world".trf()
where .trf() means translate by fastor, where used to translate any string Object

Text( "Login".trf() )
copied to clipboard
Second Way : Write To Arabic In One Line

Use "ENGLISH-WORD".arf("ARABIC-WORD")
where arf() means arabic by fastor, where used to translate any string Object

Text( "Login".arf("تسجيل دخول" ) )
copied to clipboard
How To Add In Network Header The Lang Value #

Add in "NetworkHeaderTools" the "LangFastor" new method "bearerTokenAndLang()" return header with "lang" = ar or en
the plugin code will do this step when used method "bearerTokenAndLang()" :

header[ "Authorization"] = "Bearer " + token;
header[ "lang"] = LangFastor.isArabic ? "ar" : "en";
copied to clipboard

Example Can Use This Method NetworkHeaderTools.bearerTokenAndLang()

//header
Map<String, String> header = NetworkHeaderTools.bearerTokenAndLang( tokenFromCache );

//webservice
var responseDio = NetworkManagerDio().get(url, headers: header );
copied to clipboard
Dropdown: #

Handle Direction ltr/rtl

return DropdownFastor(
textDirection: LangFastor.getTextDirection(),
copied to clipboard


SaveFastor #


Save In Cache By Using Plugin SharedPreference


Can Save Single Map() object


Can Save List of Map() Object


Setup


class MyApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
Fastor.initializeApp(context); //init Fastor Plugin here
}
copied to clipboard

methods to save

SaveFastor.setMap( );
SaveFastor.setMapList( );
SaveFastor.setInt();
SaveFastor.setString();
SaveFastor.setBool();
copied to clipboard

methods to get

SaveFastor.getMap( );
SaveFastor.getMapList( );
SaveFastor.getInt();
SaveFastor.getString();
SaveFastor.getBool();
copied to clipboard

Example Get Username From Cache In Text Widget

Widget loginContentUI(){
return ColumnFastor(children: [
TextFastor( "Test get username: " + SaveFastor.getString( "username"))
]);
}
copied to clipboard


ClipboardFastor #

save and get to clipboard of mobile


class ClipboardFastor {

static Future save(String valueToSave ) async {
....
}

static Future<String> get() async{
...
}

}
copied to clipboard


DeviceTools #
Size #

get device width/height with some feature methods
method for calculate percentage of device size

double d = DeviceTools.getWidth(context);
double d = DeviceTools.getHeight(context);
double half = DeviceTools.getPercentageWidth(context, 0.5);
double d = DeviceTools.getHeightInsideSafeArea(context);

copied to clipboard

method get platform of this device "android/ios/browser/mobile"
method get orientation partial/landscape

PlatFrom #

check type platform
check is mobile browser or mobile application

bool b = isPlatformWeb();
bool b = isMobile();
bool b = isAndroid();
bool b = isIOS();
bool b = isBrowserMobile();
bool b = isBrowserAndroid(); // any type of platform mobile like: ios or android
bool b = isBrowserIOS();
bool b = isIOS();

copied to clipboard
Orientation #
bool b = isPortrait();
bool b = isLandscape();
bool b = isPortraitMobile();
bool b = isLandscapeMobile();

copied to clipboard


NavigationTools #

Transparent View
can make animation fade while push easily
add class "NavigationTools" for push() or pushAndRemove()
can use "resume" plugin to pushWithResumeLifecycle

Navigate To Transparent Screens #
NavigationTools.pushTransparentAnimateFade( SimpleScreen() ) ;
NavigationTools.pushTransparent( SimpleScreen() ) ;
copied to clipboard
Navigate With Animation Fade #
NavigationTools.pushAnimateFade( SimpleScreen() ) ;
copied to clipboard
Navigate With Using Plugin "resume" #
NavigationTools.pushResumeAnimateFade( SimpleScreen(), this ) ;
NavigationTools.pushResume( SimpleScreen() , this) ;

copied to clipboard

Where "this" is screen that extends "ResumableState" when using plugin "resume"

Normal Push #

Push Second Screen Above First Screen

NavigationTools.push( SimpleScreen() );
copied to clipboard

Push New Screen Will Clear All Previous Screen Opening

NavigationTools.pushAndRemoveUntil( SimpleScreen() );
copied to clipboard


ToolsWait #

Make any action after waiting some time. Juse method "ToolsWait.waitToDo()"


//wait to remove cache
ToolsWait.waitToDo(100, () {
GoTo.splashApp( context);
});
copied to clipboard


ToolsPhone #

add method "ToolsPhone.fixEgyptAndSaudiNumber()" fix egyption people ways of enter mobile number.
add method "ToolsPhone.fixEgyptAndSaudiNumber()" fix saudi people ways of enter mobile number.

bool status = await InternetTools.isConnected();
copied to clipboard


ZoneTools #

method "ZoneTools.getZoneCountryDialCode()"
method "ZoneTools.getCountryISOCode()"



InternetTools #

class "InternetTools" for check for internet connection.

bool status = await InternetTools.isConnected();
copied to clipboard


ToolsValidation #
Phone Number Checker #

this check it's like "0101234568" to be return true, if it hase any abc it will return false

ToolsValidation.checkTextIsPhoneNumberCharsOnlyWithoutAbc( text);
copied to clipboard

Valid Mobile

ToolsValidation.isPhoneMobileValid( text);

copied to clipboard

Egyptian Phone Number

ToolsValidation.egyptionCodeCountry_returnByPlus( text);

copied to clipboard

Validate Not To Write Arabic Phone Number like ١٢٣٤٥٦٧٨٩٠

ToolsValidation.isStringContainArabicNumber( text);
ToolsValidation.isPhoneMobileValidAndEnglishLetter( text );

copied to clipboard
Password #

password greater than 8 character

ToolsValidation.isPasswordValid( text);

copied to clipboard
Valid Text #
ToolsValidation.isValid( text);
ToolsValidation.isEmpty( text);
ToolsValidation.isMoreThan2( text);

copied to clipboard
Name / Email #
ToolsValidation.isNameFull( text);
ToolsValidation.isName( text);
ToolsValidation.isEmail( text);

copied to clipboard
List #
ToolsValidation.isValidList( listString );
ToolsValidation.isEmptyList( listString );
ToolsValidation.isEmptyListInteger( listInteger);
ToolsValidation.isValidListInteger( listInteger );

copied to clipboard
Zero Checker #
ToolsValidation.isZero( numString );
ToolsValidation.isNotZeroInt( numInteger );
ToolsValidation.isZeroInt( numInteger );
ToolsValidation.isValidInteger( numInteger );

copied to clipboard


ApiParserFastor #

Parse json in dynamic.
example some integer return from backend as "1" or 1 this issue cause exception in parse json.
Convert 0 and 1 to boolean variable

Parse Zero And One to Boolean #

static bool isSuccess(int? n) {
....
}

static bool isTrue(int? n) {
....
}

static bool isFalse(int? n) {
....
}

static bool isFailed(int? n) {
....
}

static bool parseBoolean_int(int? n) {
....
}


static bool parseBoolean(String? n) {
...
}
copied to clipboard
Parse Boolean to Zero And One #
int i = ApiParserFastor.convertBooleanToZeroAndOne( booleanValue );
copied to clipboard
Parse Double Or Integer to String #

fix when api return value someTime in "Int" and sometimes in "Double"
like: { "data": 15 } or some times return { "data": 13.5 }
remove command "," if found in at big numbers come from backend

static double parseDoubleOrInt(dynamic data) {
......
}
copied to clipboard
Parse Integer Any Shape to String #

In Api Json Return in integer like 1 or some time in string double qoutaiton like "1". to fix


static int parseIntDynamic(dynamic data) {
....
}
copied to clipboard
Check Pagination End #

This Working when using pagingate with backend PHP Laravel

*1- example totalBar record is 100, while pagiantor is 10
* current page is 9
* "to" is 10
* >> result false
*
*2- example totalBar record is 100, while pagiantor is 10
* current page is 10
* "to" is 10
* >> result true "there is no next pages"

*3- example totalBar record is 0 zero, while pagiantor is 10
* current page is 1
* "to" is 0
* >> result true "there is no next pages"
*/
static bool isPaginateLaravelEnd(int? currentPage, int? last_page) {
...
}

copied to clipboard


ValidateResponse #
Handle Status Code #

status code of request is between 200 - 210

Response response = await http.get(url);
if( ValidateResponse.isStatusFrom200To210Code( response.statusCode ) ) {
///To-Do after success request
}
copied to clipboard
Using Dio Network #

method check invalid token

//dio response
Response response = await dio.get(url);
if( ValidateResponse.isResponseStatusCodeInvalidToken(response) ) {
///To-Do token expire or invalid, now do action for clear cache and ask user to login again
}
copied to clipboard

example method success request

//dio response
Response response = await dio.get(url);
if( ValidateResponse.isStatusFrom200To210(response) ) {
///To-Do after success request
}
copied to clipboard

method to check the string return from api network is json or html to avoid crash in parsing json

//dio response
Response response = await dio.get(url);
if( ValidateResponse.isValidJson(response.data) ) {
///To-Do
}
copied to clipboard

method the response type data not found or url not found or bad request. where status code is 400

//dio response
Response response = await dio.get(url);
if( ValidateResponse.isStatusBadRequest(response.data) ) {
///To-Do
}
copied to clipboard


ResponsiveFastor #


Choice what view will be shown here in ui, for tablet or iphone.


Decide the view will be show for orientation: portrait and landscape


each method will return boolean value


Methods #

ResponsiveFastor.isTabletPortraitOrIsPhoneNormalSize( context );

ResponsiveFastor.isTabletLandscape( context );

ResponsiveFastor.isTabletPortrait( context );

ResponsiveFastor.isTablet( context );

ResponsiveFastor.isPhoneNormalSize( context );

ResponsiveFastor.isPhoneSmall( context );

copied to clipboard


PaginateBarFastor #

Used in Admin Panel for pagingate the list of data.
button: show current page
text: show total records - items in database - .
button GoTo to any page
button next and previous page

constructor #
PaginateBarFastor( {
required this.currentPage,
required this.itemTotal,
required this.limitPerPage,
required this.progress,
required this.paginateNumberChange,
required this.colorPrimary,
required this.colorSecondary

}){
copied to clipboard

example:

Widget getPaginateBar(){
return PaginateBarFastor(
currentPage: 1, //come from response of paginate backend
itemTotal: 100, //come from response of paginate backend
limitPerPage: 10,
progress: progressStatus,
colorPrimary: Colors.blue,
colorSecondary: Colors.green,
paginateNumberChange: (newPage){
Log.i("PaginateBarFastor() - paginateNumberChange() - newPage: $newPage ");
provider?.tasksRefreshOrDownloadNextPage(context: context, isResetPage: false, newPage: newPage);
},
);
}
copied to clipboard


At The End #
About Developer #

• 7 Years of experience build +20 App.
• Mobile Developer: Flutter & Native Android IOS.
• Skills: Dart, Java, Kotlin, Swift UIKit, SwiftUI
• OCA, Oracle Certified Associate.


copied to clipboard
Follow my Project on Instagram : #
Instagram
How to contribute #
Want to contribute to the project? We will be proud to highlight you as one of our collaborators. Here are some points where you can contribute and make Fastor (and Flutter) even better.
Write articles or make videos teaching how to use Fastor (they will be inserted in the Readme and in the future in our Wiki).
Offering PRs for code/tests.
Including new functions.
Any contribution is welcome!
Contact for contribute: #
Send Email
License BSD 3 #
Copyright (c) 2022, Abdallah Mahmoud Ahmed Shehata
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of fastor-app.com nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
copied to clipboard

License

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Customer Reviews

There are no reviews.