| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- import 'dart:io';
- import 'package:flutter/foundation.dart' show kIsWeb;
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:collection/collection.dart';
- import 'package:from_css_color/from_css_color.dart';
- import 'dart:math' show pow, pi, sin;
- 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 'debug_util.dart';
- export 'debug_util.dart';
- export 'package:debug_panel_proto/debug_panel_proto.dart';
- export 'nav/serialization_util.dart';
- import '../main.dart';
- import 'lat_lng.dart';
- export 'keep_alive_wrapper.dart';
- export 'lat_lng.dart';
- export 'place.dart';
- export 'uploaded_file.dart';
- export '../app_state.dart';
- export 'flutter_flow_model.dart';
- export 'dart:math' show min, max;
- export 'dart:typed_data' show Uint8List;
- export 'dart:convert' show jsonEncode, jsonDecode;
- export 'package:intl/intl.dart';
- export 'package:page_transition/page_transition.dart';
- export 'internationalization.dart' show FFLocalizations;
- export 'nav/nav.dart';
- final RouteObserver<ModalRoute> routeObserver = RouteObserver<ModalRoute>();
- T valueOrDefault<T>(T? value, T defaultValue) =>
- (value is String && value.isEmpty) || value == null ? defaultValue : value;
- void _setTimeagoLocales() {
- 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);
- }
- 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:
- if (currency != null) {
- formattedValue = NumberFormat('#,##0.00', 'en_US').format(value);
- } else {
- formattedValue = NumberFormat.decimalPattern('en_US').format(value);
- }
- break;
- case DecimalType.commaDecimal:
- if (currency != null) {
- formattedValue = NumberFormat('#,##0.00', 'es_PA').format(value);
- } else {
- 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) =>
- MyApp.of(context).setLocale(language);
- void setDarkModeSetting(BuildContext context, ThemeMode themeMode) =>
- MyApp.of(context).setThemeMode(themeMode);
- void showSnackbar(
- BuildContext context,
- String message, {
- bool loading = false,
- int duration = 4,
- }) {
- ScaffoldMessenger.of(context).hideCurrentSnackBar();
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Row(
- children: [
- if (loading)
- Padding(
- padding: EdgeInsetsDirectional.only(end: 10.0),
- child: Container(
- height: 20,
- width: 20,
- child: const CircularProgressIndicator(
- color: Colors.white,
- ),
- ),
- ),
- Text(message),
- ],
- ),
- duration: Duration(seconds: duration),
- ),
- );
- }
- extension FFStringExt on String {
- String maybeHandleOverflow({int? maxChars, String replacement = ''}) =>
- maxChars != null && length > maxChars
- ? replaceRange(maxChars, null, replacement)
- : this;
- String toCapitalization(TextCapitalization textCapitalization) {
- switch (textCapitalization) {
- case TextCapitalization.none:
- return this;
- case TextCapitalization.words:
- return split(' ').map(toBeginningOfSentenceCase).join(' ');
- case TextCapitalization.sentences:
- return toBeginningOfSentenceCase(this);
- case TextCapitalization.characters:
- return toUpperCase();
- }
- }
- }
- 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 ColorOpacityExt on Color {
- Color applyAlpha(double val) => withValues(alpha: val);
- }
- 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));
- }
- 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 getCurrentRoute(BuildContext context) =>
- context.mounted ? MyApp.of(context).getRoute() : '';
- List<String> getCurrentRouteStack(BuildContext context) =>
- context.mounted ? MyApp.of(context).getRouteStack() : [];
|