Last active
October 27, 2023 12:51
-
-
Save roipeker/674eb79ec06b099166f140f1b655e29c to your computer and use it in GitHub Desktop.
TextField concept for GetX (WIP) ...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// copyright 2020, roipeker | |
class FormValidations { | |
static String email(String val) { | |
val = val.trim(); | |
if (val.isEmpty) return 'Email cant be empty'; | |
if (val.length <= 4) return 'Email is too short'; | |
if (!val.isEmail) return 'Invalid email'; | |
return null; | |
} | |
static String matchPasswords(String val1, String val2) { | |
if (val1.isEmpty || val2.isEmpty) | |
return 'Password cant be empty'; | |
if (val1 != val2) return 'Passwords doesnt match'; | |
return null; | |
} | |
static String password(String val) { | |
if (val.isEmpty) return 'Password cant be empty'; | |
if (val.length <= 4) return 'Password is too short'; | |
if (val.isAlphabetOnly) return 'Password must be alpha numeric'; | |
return null; | |
} | |
static String name(String val, {String label}) { | |
val = val.trim(); | |
if (val.isEmpty) return '$label can not be empty'; | |
if (val.length <= 2) return '$label is too short'; | |
return null; | |
} | |
static String phone(String val) { | |
val = val.trim(); | |
val = val.replaceAll(' ', ''); | |
if (val.isEmpty) return 'Phone is required'; | |
if (val.length <= 7) return 'Phone seems to be too short'; | |
if (!val.isNumericOnly) return 'Use only numbers'; | |
return null; | |
} | |
} | |
class FormInputFormatters { | |
static List<TextInputFormatter> priceFormatter = [ | |
TextInputFormatter.withFunction((oldValue, newValue) { | |
return newValue; | |
}), | |
FilteringTextInputFormatter.allow(RegExp('[0-9\\.,\$]')), | |
]; | |
} | |
class FormFormatters { | |
static var filterName = | |
FilteringTextInputFormatter.allow(RegExp(r'[a-z A-Z รก-รบ ร-ร]')); | |
static var filterPhone = FilteringTextInputFormatter.digitsOnly; | |
} | |
/// Format zipcode in format #####-#### | |
class ZipTextInputFormatter extends TextInputFormatter { | |
static final instance = ZipTextInputFormatter(); | |
@override | |
TextEditingValue formatEditUpdate( | |
TextEditingValue oldValue, | |
TextEditingValue newValue, | |
) { | |
final int newTextLength = newValue.text.length; | |
int selectionIndex = newValue.selection.end; | |
int usedSubstringIndex = 0; | |
final StringBuffer newText = StringBuffer(); | |
if (newTextLength >= 6) { | |
newText.write(newValue.text.substring(0, usedSubstringIndex = 5) + '-'); | |
if (newValue.selection.end >= 5) selectionIndex++; | |
} | |
if (newTextLength >= usedSubstringIndex) { | |
newText.write(newValue.text.substring(usedSubstringIndex)); | |
} | |
return TextEditingValue( | |
text: newText.toString(), | |
selection: TextSelection.collapsed( | |
offset: selectionIndex, | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// copyright 2020, roipeker | |
//import 'package:descontar_app/const/const.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:get/get.dart'; | |
enum ValidationPlace { | |
/// calls [validate()] when you focus out the TextField. | |
focus, | |
/// calls [validate()] as you type the text. | |
change, | |
/// in [manual] you take care of calling of the errors on each TextField. | |
/// you can use [TextConfig.validateAll([])] to run the [validate()] on each | |
/// TextField, or [TextConfig.getErrors([])] to get run [onValidate()] and | |
/// get a List<String> of errors (and no TextField UI invalidation). | |
manual, | |
} | |
enum ErrorMode { none, fixed, float } | |
class GetInputTextConfig extends GetxController { | |
final InputBorder border; | |
final InputBorder focusedBorder; | |
final InputBorder enabledBorder; | |
final InputBorder errorBorder; | |
final InputBorder disabledBorder; | |
final InputBorder focusedErrorBorder; | |
final ValidationPlace validationPlace; | |
final TextInputType keyboardType; | |
final TextInputAction textInputAction; | |
final TextCapitalization textCapitalization; | |
TextStyle style; | |
TextStyle disabledStyle; | |
final bool showCounter; | |
final String label; | |
final IconData icon; | |
final bool isPassword; | |
final ErrorMode errorMode; | |
final bool autocorrect; | |
// another instance will control this one. | |
GetInputTextConfig _obscureController; | |
GetInputTextConfig get obscureController => _obscureController; | |
set obscureController(GetInputTextConfig value) { | |
_obscureController = value; | |
_obscureController?._childObscureToControl = this; | |
} | |
// this is the child instance to control by the obscureController. | |
GetInputTextConfig _childObscureToControl; | |
final Iterable<String> autofillHints; | |
final List<TextInputFormatter> | |
inputFormatters; //FilteringTextInputFormatter.digitsOnly, | |
// decoration stuffs. | |
final FloatingLabelBehavior floatingLabelBehavior; | |
/// should be in an InputDecoration object | |
final bool isCollapsed; | |
int maxLength; | |
bool _obscureText = false; | |
FocusNode _focus; | |
TextEditingController _controller; | |
FormFieldValidator<String> onValidate; | |
bool clearErrorOnFocus = true; | |
bool clearErrorOnTyping = true; | |
String lastError; | |
bool _enabled; | |
bool get enabled => _enabled; | |
TextStyle get _actualStyle { | |
if (_enabled ?? true) return style; | |
return disabledStyle; | |
} | |
set enabled(bool val) { | |
if (_enabled == val) return; | |
_enabled = val; | |
update(); | |
} | |
static List<String> getErrors(List<GetInputTextConfig> inputs) { | |
final output = <String>[]; | |
for (var i in inputs) { | |
final error = i.onValidate(i.value); | |
if (!error.isNullOrBlank) output.add(error); | |
} | |
return output; | |
} | |
/// Runs [validate()] on each element in [inputs]. | |
/// [stopOnError] will return false on the first element with an error. | |
/// otherwise it will validate() the entire [inputs] List. | |
static bool validateAll(List<GetInputTextConfig> inputs, {bool stopOnError = true}) { | |
bool hasError = false; | |
for (var i in inputs) { | |
if (!i.validate()) { | |
hasError = true; | |
if (stopOnError) break; | |
} | |
} | |
return !hasError; | |
} | |
GetInputTextConfig({ | |
this.onValidate, | |
this.validationPlace = ValidationPlace.manual, | |
this.errorMode = ErrorMode.fixed, | |
bool enabled, | |
this.showCounter = false, | |
this.isCollapsed = false, | |
this.floatingLabelBehavior = FloatingLabelBehavior.auto, | |
this.keyboardType, | |
this.textInputAction, | |
this.textCapitalization, | |
this.maxLength, | |
GetInputTextConfig obscureController, | |
this.autocorrect = true, | |
this.inputFormatters, | |
this.autofillHints, | |
this.style, | |
// this.disabledStyle = const TextStyle(color: Styles.darkGrey), | |
this.label, | |
this.icon, | |
this.border = const UnderlineInputBorder(), | |
this.focusedBorder, | |
this.enabledBorder, | |
this.errorBorder, | |
this.disabledBorder, | |
this.focusedErrorBorder, | |
this.isPassword = false, | |
}) { | |
_obscureText = isPassword; | |
obscureController?._childObscureToControl = this; | |
this.enabled = enabled; | |
} | |
FocusNode get focus => _focus ??= FocusNode(); | |
TextEditingController get controller => | |
_controller ??= TextEditingController(); | |
String get value => controller.text; | |
set value(String val) { | |
val ??= ''; | |
if (val == controller.text) return; | |
controller.value = controller.value.copyWith(text: val); | |
} | |
bool get obscureText => _obscureText; | |
set obscureText(bool flag) { | |
if (_obscureText == flag) return; | |
_obscureText = flag; | |
update(); | |
} | |
@override | |
void onInit() { | |
focus.addListener(_handleFocus); | |
controller.addListener(_handleTextChange); | |
} | |
bool get hasFocus => focus.hasFocus; | |
String _value; | |
void _handleTextChange() { | |
var val = controller.text; | |
if (val == _value) return; | |
_value = val; | |
if (onChanged != null) onChanged(_value); | |
if (validationPlace == ValidationPlace.change) { | |
validate(); | |
} else { | |
if (clearErrorOnTyping) error = ''; | |
} | |
} | |
void _handleFocus() { | |
if (onFocus != null) onFocus(hasFocus); | |
if (!hasFocus) { | |
if (validationPlace == ValidationPlace.focus) { | |
validate(); | |
} | |
} else { | |
if (hasError && clearErrorOnFocus) { | |
error = ''; | |
} | |
} | |
} | |
bool validate() { | |
if (onValidate != null) { | |
error = onValidate(value); | |
lastError = error; | |
} | |
return !hasError; | |
} | |
bool get hasError { | |
// return !_error.isNullOrBlank; | |
return !lastError.isNullOrBlank; | |
// return _actualErrorText != null; | |
} | |
/// todo: give ability to access last error.../ | |
/// make a private var, and do error public and holding the last | |
/// message. | |
String get error => _error; | |
set error(String val) { | |
if (_error == val) return; | |
_error = val; | |
if (onErrorChange != null) | |
onErrorChange(_error.isNullOrBlank ? null : _error); | |
update(); | |
} | |
Widget get _counterWidget { | |
if (errorMode == ErrorMode.fixed) return null; | |
return (showCounter ?? false) ? null : Container(); | |
} | |
String get _counterText { | |
if (errorMode == ErrorMode.fixed) return ' '; | |
return null; // ErrorMode.float. | |
} | |
// used by widget | |
String get _actualErrorText { | |
if (errorMode == ErrorMode.none) return null; | |
return _error.isNullOrBlank ? null : _error; | |
} | |
String _error; | |
Function(String) onChanged; | |
Function(bool) onFocus; | |
Function(String) onErrorChange; | |
@override | |
void onClose() { | |
controller?.removeListener(_handleTextChange); | |
controller?.dispose(); | |
focus?.removeListener(_handleFocus); | |
focus?.dispose(); | |
} | |
Widget getSuffix() { | |
if (!isPassword) return null; | |
if (obscureController != null) return null; | |
return GestureDetector( | |
behavior: HitTestBehavior.translucent, | |
onTap: () { | |
obscureText = !obscureText; | |
_childObscureToControl?.obscureText = obscureText; | |
}, | |
child: Icon( | |
obscureText ? Icons.visibility : Icons.visibility_off, | |
// color: Styles.lightGrey, | |
size: 18, | |
).paddingSymmetric(horizontal: 10, vertical: 6), | |
); | |
} | |
} | |
class GetInputText extends StatelessWidget { | |
final GetInputTextConfig config; | |
const GetInputText({Key key, this.config}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return GetBuilder( | |
init: config, | |
global: false, | |
assignId: true, | |
builder: (_) { | |
return TextField( | |
controller: config.controller, | |
focusNode: config.focus, | |
style: config._actualStyle, | |
obscureText: config.obscureText, | |
keyboardType: config.keyboardType, | |
maxLength: config.maxLength, | |
maxLengthEnforced: !config.maxLength.isNull, | |
autocorrect: config.autocorrect ?? true, | |
autofillHints: (config.enabled ?? true) ? config.autofillHints : null, | |
textInputAction: config.textInputAction, | |
inputFormatters: config.inputFormatters, | |
enabled: config.enabled, | |
textCapitalization: | |
config.textCapitalization ?? TextCapitalization.none, | |
//const InputDecoration() | |
decoration: InputDecoration( | |
labelText: config.label, | |
border: config.border, | |
focusedBorder: config.focusedBorder, | |
enabledBorder: config.enabledBorder, | |
errorBorder: config.errorBorder, | |
disabledBorder: config.disabledBorder, | |
focusedErrorBorder: config.focusedErrorBorder, | |
// errorBorder: UnderlineInputBorder( | |
// borderSide: BorderSide(color: Styles.lightGrey), | |
// ), | |
contentPadding: EdgeInsets.symmetric(vertical: 6), | |
alignLabelWithHint: true, | |
floatingLabelBehavior: | |
config.floatingLabelBehavior ?? FloatingLabelBehavior.auto, | |
errorText: config._actualErrorText, | |
errorMaxLines: 1, | |
counterText: config._counterText, | |
counter: config._counterWidget, | |
icon: config.icon != null | |
? Icon(config.icon, size: 20).paddingOnly(top: 10) | |
: null, | |
isCollapsed: config.isCollapsed ?? false, | |
suffixIconConstraints: BoxConstraints(maxWidth: 24, maxHeight: 24), | |
// suffixIcon: config.isPassword ? config.getSuffix() : null, | |
suffix: config.isPassword ? config.getSuffix() : null, | |
), | |
); | |
}, | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// copyright 2020, roipeker | |
import 'package:get/get.dart'; | |
import 'signup_fields.dart'; | |
class SignupController extends GetxController { | |
final fields = SignupFields(); | |
@override | |
void onReady() { | |
fields.phone.focus.requestFocus(); | |
} | |
void onSend() { | |
if (fields.validate()) { | |
print("All valid, send form"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// copyright 2020, roipeker | |
import 'package:descontar_app/const/const.dart'; | |
import 'package:descontar_app/widgets/widgets.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:get/get.dart'; | |
import 'signup_controller.dart'; | |
class SignupView extends GetView<SignupController> { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
extendBodyBehindAppBar: true, | |
appBar: AppBar( | |
backgroundColor: Colors.transparent, | |
toolbarHeight: 40, | |
elevation: 0, | |
), | |
body: CommonBackground( | |
showBottom: false, | |
children: [ | |
Center( | |
child: SingleChildScrollView( | |
child: SizedBox( | |
width: 250, | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
children: [ | |
Image.asset(Images.logo, fit: BoxFit.contain), | |
GetInputText(config: controller.fields.name), | |
GetInputText(config: controller.fields.lastname), | |
GetInputText(config: controller.fields.email), | |
GetInputText(config: controller.fields.phone), | |
GetInputText(config: controller.fields.password), | |
GetInputText(config: controller.fields.repeatPassword), | |
const Gap(24), | |
LineButton( | |
label: 'REGISTRARSE', | |
onTap: controller.onSend, | |
), | |
Gap(40), | |
], | |
), | |
), | |
), | |
) | |
], | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// copyright 2020, roipeker | |
import 'package:descontar_app/utils/form_utils.dart'; | |
import 'package:descontar_app/widgets/widgets.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
class SignupFields { | |
GetInputTextConfig name = GetInputTextConfig( | |
label: AppStrings.name_label, | |
textCapitalization: TextCapitalization.words, | |
textInputAction: TextInputAction.next, | |
keyboardType: TextInputType.name, | |
onValidate: (val) => FormValidations.name(val, label: 'nombre'), | |
maxLength: 15, | |
inputFormatters: [FormFormatters.filterName], | |
autofillHints: [ | |
AutofillHints.name, | |
AutofillHints.creditCardName, | |
], | |
errorMode: ErrorMode.fixed, | |
// validationPlace: ValidationPlace.manual, | |
); | |
GetInputTextConfig lastname = GetInputTextConfig( | |
label: AppStrings.lastname_label, | |
onValidate: (val) => FormValidations.name(val, label: 'apellido'), | |
textCapitalization: TextCapitalization.words, | |
textInputAction: TextInputAction.next, | |
inputFormatters: [FormFormatters.filterName], | |
keyboardType: TextInputType.name, | |
maxLength: 26, | |
autofillHints: [ | |
AutofillHints.familyName, | |
AutofillHints.givenName, | |
AutofillHints.creditCardFamilyName | |
], | |
errorMode: ErrorMode.fixed, | |
// validationPlace: ValidationPlace.manual, | |
); | |
GetInputTextConfig email = GetInputTextConfig( | |
label: AppStrings.email_label, | |
onValidate: (val) => FormValidations.email(val), | |
textCapitalization: TextCapitalization.none, | |
textInputAction: TextInputAction.next, | |
keyboardType: TextInputType.emailAddress, | |
maxLength: 200, | |
autofillHints: [ | |
AutofillHints.username, | |
AutofillHints.email, | |
], | |
// errorMode: ErrorMode.fixed, | |
// validationPlace: ValidationPlace.manual, | |
); | |
GetInputTextConfig phone = GetInputTextConfig( | |
label: AppStrings.phone_label, | |
onValidate: (val) => FormValidations.phone(val), | |
// inputFormatters: [FormFormatters.filterPhone], | |
textCapitalization: TextCapitalization.none, | |
textInputAction: TextInputAction.next, | |
keyboardType: TextInputType.phone, | |
maxLength: 28, | |
autofillHints: [ | |
AutofillHints.telephoneNumberDevice, | |
AutofillHints.telephoneNumber, | |
AutofillHints.telephoneNumberNational, | |
], | |
// errorMode: ErrorMode.fixed, | |
// validationPlace: ValidationPlace.manual, | |
); | |
GetInputTextConfig password = GetInputTextConfig( | |
onValidate: (val) => FormValidations.password(val), | |
label: AppStrings.password_label, | |
maxLength: 255, | |
isPassword: true, | |
textCapitalization: TextCapitalization.none, | |
textInputAction: TextInputAction.send, | |
keyboardType: TextInputType.visiblePassword, | |
autofillHints: [AutofillHints.newPassword], | |
// errorMode: ErrorMode.float, | |
// validationPlace: ValidationPlace.manual, | |
); | |
GetInputTextConfig repeatPassword = GetInputTextConfig( | |
onValidate: (val) => FormValidations.password(val), | |
label: AppStrings.repeat_password_label, | |
maxLength: 255, | |
isPassword: true, | |
// obscureController: password, | |
textCapitalization: TextCapitalization.none, | |
textInputAction: TextInputAction.send, | |
keyboardType: TextInputType.visiblePassword, | |
autofillHints: [AutofillHints.newPassword], | |
// errorMode: ErrorMode.fixed, | |
// validationPlace: ValidationPlace.manual, | |
); | |
List<GetInputTextConfig> allFields; | |
SignupFields() { | |
allFields = [name, lastname, email, phone, password, repeatPassword]; | |
repeatPassword.obscureController = password; | |
} | |
bool validate() { | |
var error = ''; | |
for (var i in allFields) { | |
if (!i.validate()) { | |
error = i.error; | |
break; | |
} | |
} | |
if (error.isNotEmpty) { | |
AppGetUtils.showError(error); | |
return false; | |
} else { | |
// validate passwords stand alone. | |
error = FormValidations.matchPasswords( | |
password.value, repeatPassword.value) ?? | |
''; | |
if (error.isNotEmpty) { | |
repeatPassword.error = error; | |
AppGetUtils.showError(error); | |
return false; | |
} | |
} | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// copyright 2020, roipeker | |
import 'dart:ui' as ui; | |
import 'package:flutter/material.dart'; | |
import 'package:get/get.dart'; | |
// ignore: implementation_imports | |
//import 'package:get/src/core/get_interface.dart'; | |
extension MyGet on GetInterface { | |
double get devicePixelRatio => ui.window.devicePixelRatio; | |
} | |
extension MyExtVisible on Widget { | |
Widget visible(bool flag, {bool keepSize = true}) { | |
return Visibility( | |
child: this, | |
visible: flag, | |
maintainInteractivity: false, | |
maintainSize: keepSize, | |
maintainState: keepSize, | |
maintainAnimation: keepSize, | |
); | |
} | |
Widget opacity({double opacity = 1}) { | |
return Opacity( | |
child: this, | |
opacity: opacity, | |
); | |
} | |
Widget ignorePointer(bool flag, {bool ignoreSemantics = false}) { | |
return IgnorePointer( | |
child: this, | |
ignoring: flag, | |
ignoringSemantics: ignoreSemantics, | |
); | |
} | |
Widget animOpacity({ | |
double opacity = 1, | |
Duration duration = const Duration(milliseconds: 300), | |
Curve curve = Curves.decelerate, | |
VoidCallback onEnd, | |
}) { | |
return AnimatedOpacity( | |
child: this, | |
opacity: opacity, | |
duration: duration, | |
curve: curve, | |
onEnd: onEnd, | |
); | |
} | |
} | |
class AppGetUtils { | |
static bool isPreloading = false; | |
static Future<void> hidePreloader() async { | |
if (!isPreloading) return; | |
isPreloading = false; | |
if (!Get.isSnackbarOpen) { | |
Get.back(); | |
} | |
} | |
static Future<void> showPreloader() async { | |
if (isPreloading) return; | |
isPreloading = true; | |
Get.dialog( | |
Center(child: PreloaderCircular()), | |
barrierDismissible: false, | |
); | |
} | |
static bool get isSnackbarOpened => | |
_lastSnackbarStatus != SnackbarStatus.OPEN; | |
static SnackbarStatus _lastSnackbarStatus; | |
static void showError( | |
String errors, { | |
String title = 'Error:', | |
SnackbarStatusCallback onStatus, | |
}) { | |
if (isPreloading && Get.isDialogOpen) { | |
hidePreloader(); | |
} | |
Get.snackbar( | |
title, | |
errors, | |
colorText: Colors.white, | |
borderRadius: 0, | |
snackbarStatus: (status) { | |
_lastSnackbarStatus = status; | |
onStatus?.call(status); | |
}, | |
// barBlur: 0, | |
backgroundColor: Colors.red.shade800, | |
icon: Icon( | |
Icons.error, | |
color: Colors.white, | |
), | |
animationDuration: 0.45.seconds, | |
forwardAnimationCurve: Curves.fastLinearToSlowEaseIn, | |
reverseAnimationCurve: Curves.easeOutExpo, | |
// overlayBlur: 0, | |
// overlayColor: Styles.primaryColor.withOpacity(.8), | |
// overlayColor: Colors.black54, | |
overlayColor: Colors.white54, | |
// overlayBlur: GetPlatform.isAndroid ? .1 : 5, | |
overlayBlur: .1, | |
margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12), | |
snackStyle: SnackStyle.FLOATING, | |
snackPosition: SnackPosition.BOTTOM, | |
// snackStyle: SnackStyle.GROUNDED, | |
// snackPosition: SnackPosition.TOP, | |
); | |
} | |
static void showSuccess( | |
String message, { | |
String title, | |
SnackbarStatusCallback onStatus, | |
}) { | |
if (isPreloading && Get.isDialogOpen) { | |
hidePreloader(); | |
} | |
Get.snackbar( | |
title, | |
message, | |
colorText: Colors.white, | |
borderRadius: 0, | |
backgroundColor: Colors.green.shade800, | |
icon: Icon( | |
Icons.check, | |
color: Colors.white, | |
), | |
snackbarStatus: (status) { | |
_lastSnackbarStatus = status; | |
onStatus?.call(status); | |
}, | |
animationDuration: 0.3.seconds, | |
forwardAnimationCurve: Curves.fastLinearToSlowEaseIn, | |
reverseAnimationCurve: Curves.easeOutExpo, | |
shouldIconPulse: false, | |
margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12), | |
overlayColor: Colors.white54, | |
snackStyle: SnackStyle.FLOATING, | |
snackPosition: SnackPosition.BOTTOM, | |
); | |
} | |
static void unfocus() { | |
Get.focusScope?.unfocus(); | |
} | |
static void showFCM({String title, String body}) { | |
///{google.c.sender.id: 943373216722, google.c.a.e: 1, gcm.notification.sound2: default, aps: {alert: {title: ahi va, body: probandooo}, sound: default, mutable-content: 1}, gcm.n.e: 1, google.c.a.c_id: 1528606179344859117, google.c.a.udt: 0, gcm.message_id: 1601334741915383, google.c.a.ts: 1601334741, click_action: FLUTTER_NOTIFICATION_CLICK} | |
Get.snackbar( | |
title, | |
body, | |
colorText: Colors.white, | |
borderRadius: 0, | |
duration: 6.seconds, | |
barBlur: 0, | |
// backgroundColor: Colors.green.shade800, | |
backgroundColor: Colors.black87, | |
icon: Icon( | |
Icons.notification_important, | |
color: Colors.white, | |
), | |
// snackbarStatus: (status) { | |
// _lastSnackbarStatus = status; | |
// onStatus?.call(status); | |
// }, | |
animationDuration: 0.8.seconds, | |
forwardAnimationCurve: Curves.easeOut, | |
reverseAnimationCurve: Curves.easeOutExpo, | |
// overlayColor: Styles.primaryColor.withOpacity(.8), | |
shouldIconPulse: true, | |
margin: EdgeInsets.zero, | |
// overlayColor: Colors.black54, | |
// overlayBlur: .1, | |
overlayColor: Colors.black38, | |
snackStyle: SnackStyle.GROUNDED, | |
snackPosition: SnackPosition.TOP, | |
); | |
} | |
} | |
class RootWidget extends StatelessWidget { | |
const RootWidget({Key key, this.child}) : super(key: key); | |
final Widget child; | |
@override | |
Widget build(BuildContext context) { | |
ImageUtils.imageScale = Get.devicePixelRatio; | |
return Builder(builder: (ctx) { | |
return RootKeyboardManager( | |
child: child, | |
); | |
}); | |
} | |
static Widget builder(BuildContext context, Widget screen) { | |
return RootWidget(child: screen); | |
} | |
} | |
class RootKeyboardManager extends StatelessWidget { | |
final Widget child; | |
/// Allow close keyboard on tap | |
static bool allowCloseKeyboard = true; | |
const RootKeyboardManager({Key key, this.child}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return GestureDetector( | |
onTap: () { | |
if (allowCloseKeyboard) { | |
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus(); | |
} | |
}, | |
child: child, | |
); | |
} | |
} | |
class PreloaderCircular extends StatelessWidget { | |
final double size; | |
final EdgeInsets padding; | |
final Color preloaderColor; | |
final Color preloaderInactiveColor; | |
final Color bgColor; | |
const PreloaderCircular({ | |
Key key, | |
this.size, | |
this.padding, | |
this.bgColor = Colors.white, | |
this.preloaderColor = Styles.circularProgress, | |
this.preloaderInactiveColor, | |
}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Center( | |
child: Container( | |
width: size ?? 64, | |
height: size ?? 64, | |
decoration: BoxDecoration( | |
// color: Colors.white12, | |
color: bgColor, | |
borderRadius: BorderRadius.circular(6), | |
), | |
padding: padding ?? EdgeInsets.all(20), | |
child: Theme( | |
data: ThemeData(accentColor: Styles.circularProgress), | |
child: CircularProgressIndicator( | |
strokeWidth: 2, | |
// backgroundColor: Colors.white10, | |
// backgroundColor: Colors.grey.shade100, | |
backgroundColor: preloaderInactiveColor ?? Colors.grey.shade100, | |
), | |
), | |
), | |
); | |
} | |
} | |
How to achieve navigation from 1 column to other column, if user presses enter key. And i believe this code can be used for flutter web
where do we get the below packages ๐
import 'package:descontar_app/const/const.dart';
import 'package:descontar_app/utils/form_utils.dart';
import 'package:descontar_app/utils/utils.dart';
import 'package:descontar_app/widgets/widgets.dart';
Don't mind about missing files. Try to workaround the code from those dependencies, as this was taken from an actual project, i just saved it for future references of how to implement a custom TextField with GetX.
Thanks Roi
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
where do we get the below packages ๐
import 'package:descontar_app/const/const.dart';
import 'package:descontar_app/utils/form_utils.dart';
import 'package:descontar_app/utils/utils.dart';
import 'package:descontar_app/widgets/widgets.dart';