flutter-freaccess-hub/lib/flutter_flow/flutter_flow_util.dart

797 lines
25 KiB
Dart

import 'dart:io';
import 'dart:math' show pow, pi, sin;
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:from_css_color/from_css_color.dart';
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
import 'package:hub/flutter_flow/internationalization.dart';
import 'package:hub/flutter_flow/nav/nav.dart';
import 'package:hub/shared/utils/limited_text_size.dart';
import 'package:intl/intl.dart';
import 'package:json_path/json_path.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:url_launcher/url_launcher.dart';
import '../main.dart';
export 'dart:convert' show jsonEncode, jsonDecode;
export 'dart:math' show min, max;
export 'dart:typed_data' show Uint8List;
export 'package:intl/intl.dart';
export 'package:page_transition/page_transition.dart';
export 'custom_icons.dart' show FFIcons;
export 'flutter_flow_model.dart';
export 'internationalization.dart' show FFLocalizations;
export 'keep_alive_wrapper.dart';
export 'lat_lng.dart';
export 'place.dart';
export 'uploaded_file.dart';
T valueOrDefault<T>(T? value, T defaultValue) =>
(value is String && value.isEmpty) || value == null ? defaultValue : value;
void _setTimeagoLocales() {
timeago.setLocaleMessages('pt', timeago.PtBrMessages());
timeago.setLocaleMessages('pt_short', timeago.PtBrShortMessages());
timeago.setLocaleMessages('en', timeago.EnMessages());
timeago.setLocaleMessages('en_short', timeago.EnShortMessages());
}
String dateTimeFormat(String format, DateTime? dateTime, {String? locale}) {
if (dateTime == null) {
return '';
}
if (format == 'relative') {
_setTimeagoLocales();
return timeago.format(dateTime, locale: locale, allowFromNow: true);
}
return DateFormat(format, locale).format(dateTime);
}
Theme wrapInMaterialDatePickerTheme(
BuildContext context,
Widget child, {
required Color headerBackgroundColor,
required Color headerForegroundColor,
required TextStyle headerTextStyle,
required Color pickerBackgroundColor,
required Color pickerForegroundColor,
required Color selectedDateTimeBackgroundColor,
required Color selectedDateTimeForegroundColor,
required Color actionButtonForegroundColor,
required double iconSize,
}) {
final baseTheme = Theme.of(context);
final dateTimeMaterialStateForegroundColor =
WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) {
return pickerForegroundColor.withOpacity(0.60);
}
if (states.contains(WidgetState.selected)) {
return selectedDateTimeForegroundColor;
}
if (states.isEmpty) {
return pickerForegroundColor;
}
return null;
});
final dateTimeMaterialStateBackgroundColor =
WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return selectedDateTimeBackgroundColor;
}
return null;
});
return Theme(
data: baseTheme.copyWith(
textSelectionTheme: baseTheme.textSelectionTheme.copyWith(
cursorColor: headerBackgroundColor,
selectionColor: headerBackgroundColor.withOpacity(0.4),
selectionHandleColor: headerBackgroundColor,
),
inputDecorationTheme: baseTheme.inputDecorationTheme.copyWith(
isDense: true,
hoverColor: headerBackgroundColor.withOpacity(0.04),
floatingLabelStyle: baseTheme.textTheme.labelMedium!.copyWith(
color: headerBackgroundColor,
),
labelStyle: baseTheme.textTheme.labelMedium!.copyWith(
color: headerBackgroundColor,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor.withOpacity(0.6),
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
),
colorScheme: baseTheme.colorScheme.copyWith(
onSurface: pickerForegroundColor,
),
disabledColor: pickerForegroundColor.withOpacity(0.3),
textTheme: baseTheme.textTheme.copyWith(
headlineSmall: headerTextStyle,
headlineMedium: headerTextStyle,
),
iconTheme: baseTheme.iconTheme.copyWith(
size: iconSize,
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
foregroundColor: WidgetStatePropertyAll(
actionButtonForegroundColor,
),
overlayColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.hovered)) {
return actionButtonForegroundColor.withOpacity(0.04);
}
if (states.contains(WidgetState.focused) ||
states.contains(WidgetState.pressed)) {
return actionButtonForegroundColor.withOpacity(0.12);
}
return null;
})),
),
datePickerTheme: DatePickerThemeData(
backgroundColor: pickerBackgroundColor,
headerBackgroundColor: headerBackgroundColor,
headerForegroundColor: headerForegroundColor,
weekdayStyle: baseTheme.textTheme.labelMedium!.copyWith(
color: pickerForegroundColor,
),
dayBackgroundColor: dateTimeMaterialStateBackgroundColor,
todayBackgroundColor: dateTimeMaterialStateBackgroundColor,
yearBackgroundColor: dateTimeMaterialStateBackgroundColor,
dayForegroundColor: dateTimeMaterialStateForegroundColor,
todayForegroundColor: dateTimeMaterialStateForegroundColor,
yearForegroundColor: dateTimeMaterialStateForegroundColor,
),
),
child: child,
);
}
Theme wrapInMaterialTimePickerTheme(
BuildContext context,
Widget child, {
required Color headerBackgroundColor,
required Color headerForegroundColor,
required TextStyle headerTextStyle,
required Color pickerBackgroundColor,
required Color pickerDialForegroundColor,
required Color pickerForegroundColor,
required Color selectedDateTimeBackgroundColor,
required Color selectedDateTimeForegroundColor,
required Color actionButtonForegroundColor,
required double iconSize,
}) {
final baseTheme = Theme.of(context);
final textScaler = MediaQuery.textScalerOf(context);
final double baseFontSize = 14.0;
final double scaledFontSize = baseFontSize * textScaler.scale(1);
final double limitedFontSize = scaledFontSize > 14.0 ? 8 : scaledFontSize;
return Theme(
data: baseTheme.copyWith(
focusColor: headerBackgroundColor,
iconTheme: baseTheme.iconTheme.copyWith(
size: iconSize,
),
textSelectionTheme: baseTheme.textSelectionTheme.copyWith(
cursorColor: headerBackgroundColor,
selectionColor: headerBackgroundColor.withOpacity(0.4),
selectionHandleColor: headerBackgroundColor,
),
inputDecorationTheme: baseTheme.inputDecorationTheme.copyWith(
isDense: true,
focusColor: headerBackgroundColor,
hoverColor: headerBackgroundColor.withOpacity(0.04),
floatingLabelStyle: baseTheme.textTheme.labelMedium!.copyWith(
color: headerBackgroundColor,
),
labelStyle: baseTheme.textTheme.labelMedium!.copyWith(
color: headerBackgroundColor,
),
outlineBorder: BorderSide(
color: headerBackgroundColor,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor.withOpacity(0.6),
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: headerBackgroundColor,
),
),
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
foregroundColor: WidgetStatePropertyAll(
actionButtonForegroundColor,
),
overlayColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.hovered)) {
return actionButtonForegroundColor.withOpacity(0.04);
}
if (states.contains(WidgetState.focused) ||
states.contains(WidgetState.pressed)) {
return actionButtonForegroundColor.withOpacity(0.12);
}
return null;
})),
),
timePickerTheme: baseTheme.timePickerTheme.copyWith(
hourMinuteColor: selectedDateTimeBackgroundColor,
backgroundColor: pickerBackgroundColor,
hourMinuteTextColor: pickerForegroundColor,
dialHandColor: selectedDateTimeBackgroundColor,
dialTextColor: WidgetStateColor.resolveWith((states) =>
states.contains(WidgetState.selected)
? selectedDateTimeForegroundColor
: pickerDialForegroundColor),
dayPeriodBorderSide: BorderSide(
color: pickerForegroundColor,
),
dialTextStyle: baseTheme.textTheme.headlineMedium!.copyWith(
color: pickerDialForegroundColor,
fontSize: limitedFontSize,
),
dayPeriodTextColor: WidgetStateColor.resolveWith((states) =>
states.contains(WidgetState.selected)
? selectedDateTimeForegroundColor
: pickerForegroundColor),
dayPeriodColor: WidgetStateColor.resolveWith((states) =>
states.contains(WidgetState.selected)
? selectedDateTimeBackgroundColor
: Colors.transparent),
entryModeIconColor: pickerForegroundColor,
),
),
child: child,
);
}
Future launchURL(String url) async {
var uri = Uri.parse(url);
try {
await launchUrl(uri);
} catch (e) {
throw 'Could not launch $uri: $e';
}
}
Color colorFromCssString(String color, {Color? defaultColor}) {
try {
return fromCssColor(color);
} catch (_) {}
return defaultColor ?? Colors.black;
}
enum FormatType {
decimal,
percent,
scientific,
compact,
compactLong,
custom,
}
enum DecimalType {
automatic,
periodDecimal,
commaDecimal,
}
String formatNumber(
num? value, {
required FormatType formatType,
DecimalType? decimalType,
String? currency,
bool toLowerCase = false,
String? format,
String? locale,
}) {
if (value == null) {
return '';
}
var formattedValue = '';
switch (formatType) {
case FormatType.decimal:
switch (decimalType!) {
case DecimalType.automatic:
formattedValue = NumberFormat.decimalPattern().format(value);
break;
case DecimalType.periodDecimal:
formattedValue = NumberFormat.decimalPattern('en_US').format(value);
break;
case DecimalType.commaDecimal:
formattedValue = NumberFormat.decimalPattern('es_PA').format(value);
break;
}
break;
case FormatType.percent:
formattedValue = NumberFormat.percentPattern().format(value);
break;
case FormatType.scientific:
formattedValue = NumberFormat.scientificPattern().format(value);
if (toLowerCase) {
formattedValue = formattedValue.toLowerCase();
}
break;
case FormatType.compact:
formattedValue = NumberFormat.compact().format(value);
break;
case FormatType.compactLong:
formattedValue = NumberFormat.compactLong().format(value);
break;
case FormatType.custom:
final hasLocale = locale != null && locale.isNotEmpty;
formattedValue =
NumberFormat(format, hasLocale ? locale : null).format(value);
}
if (formattedValue.isEmpty) {
return value.toString();
}
if (currency != null) {
final currencySymbol = currency.isNotEmpty
? currency
: NumberFormat.simpleCurrency().format(0.0).substring(0, 1);
formattedValue = '$currencySymbol$formattedValue';
}
return formattedValue;
}
DateTime get getCurrentTimestamp => DateTime.now();
DateTime dateTimeFromSecondsSinceEpoch(int seconds) {
return DateTime.fromMillisecondsSinceEpoch(seconds * 1000);
}
extension DateTimeConversionExtension on DateTime {
int get secondsSinceEpoch => (millisecondsSinceEpoch / 1000).round();
}
extension DateTimeComparisonOperators on DateTime {
bool operator <(DateTime other) => isBefore(other);
bool operator >(DateTime other) => isAfter(other);
bool operator <=(DateTime other) => this < other || isAtSameMomentAs(other);
bool operator >=(DateTime other) => this > other || isAtSameMomentAs(other);
}
T? castToType<T>(dynamic value) {
if (value == null) {
return null;
}
switch (T) {
case double:
// Doubles may be stored as ints in some cases.
return value.toDouble() as T;
case int:
// Likewise, ints may be stored as doubles. If this is the case
// (i.e. no decimal value), return the value as an int.
if (value is num && value.toInt() == value) {
return value.toInt() as T;
}
break;
default:
break;
}
return value as T;
}
dynamic getJsonField(
dynamic response,
String jsonPath, [
bool isForList = false,
]) {
final field = JsonPath(jsonPath).read(response);
if (field.isEmpty) {
return null;
}
if (field.length > 1) {
return field.map((f) => f.value).toList();
}
final value = field.first.value;
if (isForList) {
return value is! Iterable
? [value]
: (value is List ? value : value.toList());
}
return value;
}
Rect? getWidgetBoundingBox(BuildContext context) {
try {
final renderBox = context.findRenderObject() as RenderBox?;
return renderBox!.localToGlobal(Offset.zero) & renderBox.size;
} catch (_) {
return null;
}
}
bool get isAndroid => !kIsWeb && Platform.isAndroid;
bool get isiOS => !kIsWeb && Platform.isIOS;
bool get isWeb => kIsWeb;
const kBreakpointSmall = 479.0;
const kBreakpointMedium = 767.0;
const kBreakpointLarge = 991.0;
bool isMobileWidth(BuildContext context) =>
MediaQuery.sizeOf(context).width < kBreakpointSmall;
bool responsiveVisibility({
required BuildContext context,
bool phone = true,
bool tablet = true,
bool tabletLandscape = true,
bool desktop = true,
}) {
final width = MediaQuery.sizeOf(context).width;
if (width < kBreakpointSmall) {
return phone;
} else if (width < kBreakpointMedium) {
return tablet;
} else if (width < kBreakpointLarge) {
return tabletLandscape;
} else {
return desktop;
}
}
const kTextValidatorUsernameRegex = r'^[a-zA-Z][a-zA-Z0-9_-]{2,16}$';
// https://stackoverflow.com/a/201378
const kTextValidatorEmailRegex =
"^(?:[a-zA-Z0-9!#\$%&\'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#\$%&\'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-zA-Z0-9-]*[a-zA-Z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])\$";
const kTextValidatorWebsiteRegex =
r'(https?:\/\/)?(www\.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,10}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)|(https?:\/\/)?(www\.)?(?!ww)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,10}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)';
extension FFTextEditingControllerExt on TextEditingController? {
String get text => this == null ? '' : this!.text;
set text(String newText) => this?.text = newText;
}
extension IterableExt<T> on Iterable<T> {
List<T> sortedList<S extends Comparable>(
{S Function(T)? keyOf, bool desc = false}) {
final sortedAscending = toList()
..sort(keyOf == null ? null : ((a, b) => keyOf(a).compareTo(keyOf(b))));
if (desc) {
return sortedAscending.reversed.toList();
}
return sortedAscending;
}
List<S> mapIndexed<S>(S Function(int, T) func) => toList()
.asMap()
.map((index, value) => MapEntry(index, func(index, value)))
.values
.toList();
}
void setAppLanguage(BuildContext context, String language) =>
App.of(context).setLocale(language);
void setDarkModeSetting(BuildContext context, ThemeMode themeMode) =>
App.of(context).setThemeMode(themeMode);
void showSnackbarMessenger(
BuildContext context,
String message,
bool error, {
bool loading = false,
int duration = 4,
}) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(showSnackbar(context, message, error));
}
SnackBar showSnackbar(
BuildContext context,
String message,
bool error, {
bool loading = false,
int duration = 4,
}) {
return SnackBar(
content: Row(
children: [
if (loading)
Padding(
padding: const EdgeInsetsDirectional.only(end: 10.0),
child: SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
color: FlutterFlowTheme.of(context).info,
),
),
),
Text(
message,
style: TextStyle(
color: FlutterFlowTheme.of(context).info,
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
),
),
],
),
duration: Duration(seconds: duration),
backgroundColor: error
? FlutterFlowTheme.of(context).error
: FlutterFlowTheme.of(context).success,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
);
}
void showAlertDialog(BuildContext context, String title, String content,
Future<void> Function() action) {
double limitedBodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context);
double limitedHeaderFontSize = LimitedFontSizeUtil.getHeaderFontSize(context);
double limitedInputFontSize = LimitedFontSizeUtil.getInputFontSize(context);
showDialog(
context: context,
builder: (context) {
return AlertDialog(
key: ValueKey<String>('AlertDialogKey'),
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
title: Text(title,
style: TextStyle(
color: FlutterFlowTheme.of(context).primaryText,
fontSize: limitedHeaderFontSize),
textAlign: TextAlign.center),
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(content,
style: TextStyle(
color: FlutterFlowTheme.of(context).primaryText,
fontSize: limitedBodyFontSize)),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
FFButtonWidget(
key: ValueKey<String>('RejectOptionKey'),
onPressed: () => context.pop(), //Navigator.pop(context),
options: FFButtonOptions(
width: MediaQuery.of(context).size.width * 0.3,
height: 50,
color: FlutterFlowTheme.of(context).primaryBackground,
textStyle: TextStyle(
color: FlutterFlowTheme.of(context).primaryText,
fontSize: limitedInputFontSize),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context).primaryBackground,
width: 1,
),
splashColor: const Color.fromARGB(255, 129, 129, 129),
borderRadius: BorderRadius.circular(10),
elevation: 0,
),
text: FFLocalizations.of(context).getVariableText(
enText: 'No',
ptText: 'Não',
),
),
FFButtonWidget(
key: ValueKey<String>('AcceptOptionKey'),
onPressed: () async {
action();
},
options: FFButtonOptions(
width: MediaQuery.of(context).size.width * 0.3,
height: 50,
color: FlutterFlowTheme.of(context).primaryBackground,
elevation: 0,
textStyle: TextStyle(
color: FlutterFlowTheme.of(context).primaryText,
fontSize: limitedInputFontSize),
splashColor: const Color.fromARGB(255, 129, 129, 129),
borderSide: BorderSide(
color: FlutterFlowTheme.of(context).primaryBackground,
width: 1,
),
borderRadius: BorderRadius.circular(10),
),
text: FFLocalizations.of(context).getVariableText(
enText: 'Yes',
ptText: 'Sim',
),
),
],
),
],
);
});
}
extension FFStringExt on String {
String maybeHandleOverflow({int? maxChars, String replacement = ''}) =>
maxChars != null && length > maxChars
? replaceRange(maxChars, null, replacement)
: this;
}
extension ListFilterExt<T> on Iterable<T?> {
List<T> get withoutNulls => where((s) => s != null).map((e) => e!).toList();
}
extension MapFilterExtensions<T> on Map<String, T?> {
Map<String, T> get withoutNulls => Map.fromEntries(
entries
.where((e) => e.value != null)
.map((e) => MapEntry(e.key, e.value as T)),
);
}
extension MapListContainsExt on List<dynamic> {
bool containsMap(dynamic map) => map is Map
? any((e) => e is Map && const DeepCollectionEquality().equals(e, map))
: contains(map);
}
extension ListDivideExt<T extends Widget> on Iterable<T> {
Iterable<MapEntry<int, Widget>> get enumerate => toList().asMap().entries;
List<Widget> divide(Widget t, {bool Function(int)? filterFn}) => isEmpty
? []
: (enumerate
.map((e) => [e.value, if (filterFn == null || filterFn(e.key)) t])
.expand((i) => i)
.toList()
..removeLast());
List<Widget> around(Widget t) => addToStart(t).addToEnd(t);
List<Widget> addToStart(Widget t) =>
enumerate.map((e) => e.value).toList()..insert(0, t);
List<Widget> addToEnd(Widget t) =>
enumerate.map((e) => e.value).toList()..add(t);
List<Padding> paddingTopEach(double val) =>
map((w) => Padding(padding: EdgeInsets.only(top: val), child: w))
.toList();
}
extension StatefulWidgetExtensions on State<StatefulWidget> {
/// Check if the widget exist before safely setting state.
void safeSetState(VoidCallback fn) {
if (mounted) {
// ignore: invalid_use_of_protected_member
setState(fn);
}
}
}
// For iOS 16 and below, set the status bar color to match the app's theme.
// https://github.com/flutter/flutter/issues/41067
Brightness? _lastBrightness;
void fixStatusBarOniOS16AndBelow(BuildContext context) {
if (!isiOS) {
return;
}
final brightness = Theme.of(context).brightness;
if (_lastBrightness != brightness) {
_lastBrightness = brightness;
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarBrightness: brightness,
systemStatusBarContrastEnforced: true,
),
);
}
}
extension ListUniqueExt<T> on Iterable<T> {
List<T> unique(dynamic Function(T) getKey) {
var distinctSet = <dynamic>{};
var distinctList = <T>[];
for (var item in this) {
if (distinctSet.add(getKey(item))) {
distinctList.add(item);
}
}
return distinctList;
}
}
String roundTo(double value, int decimalPoints) {
final power = pow(10, decimalPoints);
return ((value * power).round() / power).toString();
}
double computeGradientAlignmentX(double evaluatedAngle) {
evaluatedAngle %= 360;
final rads = evaluatedAngle * pi / 180;
double x;
if (evaluatedAngle < 45 || evaluatedAngle > 315) {
x = sin(2 * rads);
} else if (45 <= evaluatedAngle && evaluatedAngle <= 135) {
x = 1;
} else if (135 <= evaluatedAngle && evaluatedAngle <= 225) {
x = sin(-2 * rads);
} else {
x = -1;
}
return double.parse(roundTo(x, 2));
}
double computeGradientAlignmentY(double evaluatedAngle) {
evaluatedAngle %= 360;
final rads = evaluatedAngle * pi / 180;
double y;
if (evaluatedAngle < 45 || evaluatedAngle > 315) {
y = -1;
} else if (45 <= evaluatedAngle && evaluatedAngle <= 135) {
y = sin(-2 * rads);
} else if (135 <= evaluatedAngle && evaluatedAngle <= 225) {
y = 1;
} else {
y = sin(2 * rads);
}
return double.parse(roundTo(y, 2));
}