From 59540a9e045082eb77bff5dc7eb34dd3e2b3063d Mon Sep 17 00:00:00 2001 From: reinjens Date: Thu, 28 Nov 2024 08:49:17 +0100 Subject: [PATCH] Added int, gmaps and context extensions --- lib/src/color_extensions.dart | 20 +++++++++----- lib/src/context_extensions.dart | 39 +++++++++----------------- lib/src/datetime_extensions.dart | 36 ++++++++++++++++-------- lib/src/duration_extensions.dart | 14 ++++------ lib/src/google_maps_extensions.dart | 43 +++++++++++++++++------------ lib/src/int_extensions.dart | 36 +++++++++++++++++++----- lib/src/string_extensions.dart | 31 +++++++++++++++------ pubspec.yaml | 2 +- 8 files changed, 133 insertions(+), 88 deletions(-) diff --git a/lib/src/color_extensions.dart b/lib/src/color_extensions.dart index 0397043..b465bb8 100644 --- a/lib/src/color_extensions.dart +++ b/lib/src/color_extensions.dart @@ -2,15 +2,21 @@ import 'package:flutter/material.dart'; extension OpenColorExtensions on Color { static Color fromHex(String hexString) { + final cleanedHex = hexString.replaceFirst('#', ''); final buffer = StringBuffer(); - if (hexString.length == 6 || hexString.length == 7) buffer.write('ff'); - buffer.write(hexString.replaceFirst('#', '')); + if (cleanedHex.length == 6) buffer.write('ff'); + buffer.write(cleanedHex); return Color(int.parse(buffer.toString(), radix: 16)); } - String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}' - '${alpha.toRadixString(16).padLeft(2, '0')}' - '${red.toRadixString(16).padLeft(2, '0')}' - '${green.toRadixString(16).padLeft(2, '0')}' - '${blue.toRadixString(16).padLeft(2, '0')}'; + String toHex({bool leadingHashSign = true}) { + final hex = [ + alpha.toRadixString(16).padLeft(2, '0'), + red.toRadixString(16).padLeft(2, '0'), + green.toRadixString(16).padLeft(2, '0'), + blue.toRadixString(16).padLeft(2, '0'), + ].join(); + + return '${leadingHashSign ? '#' : ''}$hex'; + } } diff --git a/lib/src/context_extensions.dart b/lib/src/context_extensions.dart index eb3021c..cc6bc45 100644 --- a/lib/src/context_extensions.dart +++ b/lib/src/context_extensions.dart @@ -1,43 +1,30 @@ import 'package:flutter/material.dart'; extension OpenContextExtensions on BuildContext { - bool get isMobile => MediaQuery.of(this).size.width <= 500.0; + // Screen size properties + double get width => mediaQuery.size.width; + double get height => mediaQuery.size.height; + Size get size => mediaQuery.size; - bool get isTablet => - MediaQuery.of(this).size.width < 1024.0 && - MediaQuery.of(this).size.width >= 650.0; - - bool get isSmallTablet => - MediaQuery.of(this).size.width < 650.0 && - MediaQuery.of(this).size.width > 500.0; - - bool get isDesktop => MediaQuery.of(this).size.width >= 1024.0; - - bool get isSmall => - MediaQuery.of(this).size.width < 850.0 && - MediaQuery.of(this).size.width >= 560.0; - - double get width => MediaQuery.of(this).size.width; - - double get height => MediaQuery.of(this).size.height; - - Size get size => MediaQuery.of(this).size; + // Screen type properties + bool get isMobile => width <= 500.0; + bool get isTablet => width >= 650.0 && width < 1024.0; + bool get isSmallTablet => width > 500.0 && width < 650.0; + bool get isDesktop => width >= 1024.0; + bool get isSmall => width >= 560.0 && width < 850.0; + // Theme and styling ThemeData get theme => Theme.of(this); - TextTheme get textTheme => theme.textTheme; - ColorScheme get colorScheme => theme.colorScheme; - DefaultTextStyle get defaultTextStyle => DefaultTextStyle.of(this); + // Media query data MediaQueryData get mediaQuery => MediaQuery.of(this); + // Widget-related properties NavigatorState get navigator => Navigator.of(this); - FocusScopeNode get focusScope => FocusScope.of(this); - ScaffoldState get scaffold => Scaffold.of(this); - ScaffoldMessengerState get scaffoldMessenger => ScaffoldMessenger.of(this); } diff --git a/lib/src/datetime_extensions.dart b/lib/src/datetime_extensions.dart index 7f0e6cd..4e226e2 100644 --- a/lib/src/datetime_extensions.dart +++ b/lib/src/datetime_extensions.dart @@ -1,30 +1,38 @@ extension OpenDatetimeExtensions on DateTime { - static final DateTime _now = DateTime.now(); + /// Returns the current UTC time as an ISO 8601 string. + String nowString() => DateTime.now().toUtc().toIso8601String(); - String nowString() => _now.toUtc().toIso8601String(); + /// Checks if the date is today. + bool get isToday => _isSameDay(this, DateTime.now()); - bool isToday() => _isSameDay(this, _now); + /// Checks if the date is yesterday. + bool get isYesterday => + _isSameDay(this, DateTime.now().subtract(const Duration(days: 1))); - bool isYesterday() => - _isSameDay(this, _now.subtract(const Duration(days: 1))); + /// Checks if the date is tomorrow. + bool get isTomorrow => + _isSameDay(this, DateTime.now().add(const Duration(days: 1))); - bool isTomorrow() => _isSameDay(this, _now.add(const Duration(days: 1))); - - bool isMorning() => hour >= 4 && hour < 12; - bool isAfternoon() => hour >= 12 && hour < 18; - bool isEvening() => hour < 4 || hour >= 18; + /// Time of day checks. + bool get isMorning => hour >= 4 && hour < 12; + bool get isAfternoon => hour >= 12 && hour < 18; + bool get isEvening => hour < 4 || hour >= 18; + /// Returns the time of day as a string. String get timeOfDay { - if (isMorning()) return 'morning'; - if (isAfternoon()) return 'afternoon'; + if (isMorning) return 'morning'; + if (isAfternoon) return 'afternoon'; return 'evening'; } + /// Returns the first date of the current week. DateTime get firstDateOfTheWeek => subtract(Duration(days: weekday - 1)); + /// Returns the last date of the current week. DateTime get lastDateOfTheWeek => add(Duration(days: DateTime.daysPerWeek - weekday)); + /// Formats a duration (in seconds) into "HH:mm:ss". String formatSeconds(int value) { final hours = (value ~/ 3600).toString().padLeft(2, '0'); final minutes = ((value % 3600) ~/ 60).toString().padLeft(2, '0'); @@ -32,15 +40,19 @@ extension OpenDatetimeExtensions on DateTime { return "$hours:$minutes:$seconds"; } + /// Formats a duration (in minutes) into "HH:mm". + /// If `allowZero` is false, replaces "HH:00" with "HH:01". String formatMinutes(int value, {bool allowZero = false}) { final hours = (value ~/ 60).toString().padLeft(2, '0'); final minutes = (value % 60).toString().padLeft(2, '0'); return minutes == '00' && !allowZero ? "$hours:01" : "$hours:$minutes"; } + /// Returns the last day of the current month. DateTime get lastDayOfMonth => (month < 12) ? DateTime(year, month + 1, 0) : DateTime(year + 1, 1, 0); + /// Private helper method to check if two dates fall on the same calendar day. static bool _isSameDay(DateTime date1, DateTime date2) { return date1.year == date2.year && date1.month == date2.month && diff --git a/lib/src/duration_extensions.dart b/lib/src/duration_extensions.dart index a85dfc9..ee489cd 100644 --- a/lib/src/duration_extensions.dart +++ b/lib/src/duration_extensions.dart @@ -1,11 +1,9 @@ extension OpenDurationExtensions on Duration { - /// Converts the duration into a readable string in the format "HH:MM" - String toHoursMinutes() { - return "${inHours.toString().padLeft(2, '0')}:${inMinutes.remainder(60).toString().padLeft(2, '0')}"; - } + /// Converts the duration into a readable string in the format "HH:MM". + String toHoursMinutes() => + "${inHours.toString().padLeft(2, '0')}:${(inMinutes % 60).toString().padLeft(2, '0')}"; - /// Converts the duration into a readable string in the format "HH:MM:SS" - String toHoursMinutesSeconds() { - return "${toHoursMinutes()}:${inSeconds.remainder(60).toString().padLeft(2, '0')}"; - } + /// Converts the duration into a readable string in the format "HH:MM:SS". + String toHoursMinutesSeconds() => + "${toHoursMinutes()}:${(inSeconds % 60).toString().padLeft(2, '0')}"; } diff --git a/lib/src/google_maps_extensions.dart b/lib/src/google_maps_extensions.dart index 792b1a0..b3f65d3 100644 --- a/lib/src/google_maps_extensions.dart +++ b/lib/src/google_maps_extensions.dart @@ -3,36 +3,43 @@ import 'dart:math'; import 'package:google_maps_flutter/google_maps_flutter.dart'; extension OpenGoogleMapsExtensions on GoogleMap { - LatLngBounds getBoundsFromLatLngList(List list) { - assert(list.isNotEmpty, 'The list cannot be empty'); + /// Calculates the bounding box (LatLngBounds) from a list of LatLng points. + LatLngBounds getBoundsFromLatLngList(List points) { + assert(points.isNotEmpty, 'The points list cannot be empty.'); - // Initialize bounds to the first point - double x0 = list[0].latitude; - double x1 = x0; - double y0 = list[0].longitude; - double y1 = y0; + double minLat = points.first.latitude; + double maxLat = points.first.latitude; + double minLng = points.first.longitude; + double maxLng = points.first.longitude; - // Update bounds based on each point in the list - for (var point in list.skip(1)) { - x0 = min(x0, point.latitude); - x1 = max(x1, point.latitude); - y0 = min(y0, point.longitude); - y1 = max(y1, point.longitude); + for (final point in points) { + minLat = min(minLat, point.latitude); + maxLat = max(maxLat, point.latitude); + minLng = min(minLng, point.longitude); + maxLng = max(maxLng, point.longitude); } - return LatLngBounds(northeast: LatLng(x1, y1), southwest: LatLng(x0, y0)); + return LatLngBounds( + northeast: LatLng(maxLat, maxLng), + southwest: LatLng(minLat, minLng), + ); } + /// Calculates the geographical center of a list of LatLng points. LatLng calculateCenter(List points) { assert(points.isNotEmpty, 'The points list cannot be empty.'); - final total = points.fold( + final total = points.fold( const LatLng(0.0, 0.0), - (LatLng acc, LatLng point) => LatLng( - acc.latitude + point.latitude, acc.longitude + point.longitude), + (acc, point) => LatLng( + acc.latitude + point.latitude, + acc.longitude + point.longitude, + ), ); return LatLng( - total.latitude / points.length, total.longitude / points.length); + total.latitude / points.length, + total.longitude / points.length, + ); } } diff --git a/lib/src/int_extensions.dart b/lib/src/int_extensions.dart index b269fe1..dd72ed9 100644 --- a/lib/src/int_extensions.dart +++ b/lib/src/int_extensions.dart @@ -1,13 +1,35 @@ import 'package:open_exts/open_exts.dart'; extension OpenIntExtensions on int { + Iterable to(int other, [bool inclusive = true]) sync* { + if (this == other) { + if (inclusive) yield this; + return; + } + + final step = this < other ? 1 : -1; + for (int i = this; + inclusive ? (i != other + step) : (i != other); + i += step) { + yield i; + } + } + + /// Converts the integer to a binary string with optional padding, grouping, and separation. + /// - [length]: Total length of the binary string, padded with zeros if needed. + /// - [groupSize]: Number of binary digits per group for separation. + /// - [separator]: String used to separate binary groups. String toBinary( - int len, { - int separateAtLength = 4, + int length, { + int groupSize = 4, String separator = ',', - }) => - toRadixString(2) - .padLeft(len, '0') - .splitByLength(separateAtLength) - .join(separator); + }) { + assert(length > 0, 'Length must be greater than zero.'); + assert(groupSize > 0, 'Group size must be greater than zero.'); + + return toRadixString(2) + .padLeft(length, '0') + .splitByLength(groupSize) + .join(separator); + } } diff --git a/lib/src/string_extensions.dart b/lib/src/string_extensions.dart index 7fddb25..2570ea5 100644 --- a/lib/src/string_extensions.dart +++ b/lib/src/string_extensions.dart @@ -1,36 +1,49 @@ extension OpenStringExtension on String { + /// Removes all occurrences of [rhs] from the string. String operator -(String rhs) => replaceAll(rhs, ''); - String truncateTo(int maxLength) => - (length <= maxLength) ? this : '${substring(0, maxLength - 3)}...'; + /// Truncates the string to [maxLength] and appends '...' if truncated. + String truncateTo(int maxLength) { + if (maxLength <= 0) return ''; + return length <= maxLength ? this : '${substring(0, maxLength - 3)}...'; + } + /// Shortens the string to [maxLength] characters, preserving whole words. String shortenByWords(int maxLength) => shortenByDelimiterWithCount(maxLength); + /// Shortens the string to [maxLength] characters using [delimiter] as a word boundary. String shortenByDelimiterWithCount(int maxLength, [String delimiter = ' ']) { + if (maxLength <= 0) return ''; if (length <= maxLength) return this; - int endIndex = substring(0, maxLength).lastIndexOf(delimiter); - if (endIndex == -1) return substring(0, maxLength); + final cutIndex = substring(0, maxLength).lastIndexOf(delimiter); + if (cutIndex == -1) return substring(0, maxLength); - String shortened = substring(0, endIndex); - int remainingWordsCount = + final shortened = substring(0, cutIndex).trim(); + final remainingWordsCount = split(delimiter).length - shortened.split(delimiter).length; + return remainingWordsCount > 0 ? '$shortened +$remainingWordsCount' : shortened; } + /// Splits the string into chunks of length [len], padding with [filler] if necessary. Iterable splitByLength(int len, {String filler = '0'}) sync* { - String paddedString = padRight((length + len - 1) ~/ len * len, filler); + if (len <= 0) + throw ArgumentError.value(len, 'len', 'Must be greater than 0'); + final paddedString = padRight((length + len - 1) ~/ len * len, filler); for (var i = 0; i < paddedString.length; i += len) { yield paddedString.substring(i, i + len); } } + /// Capitalizes the first character of the string. String capitalize() => - length > 0 ? substring(0, 1).toUpperCase() + substring(1) : this; + isNotEmpty ? '${this[0].toUpperCase()}${substring(1)}' : this; + /// Decapitalizes the first character of the string. String decapitalize() => - length > 0 ? substring(0, 1).toLowerCase() + substring(1) : this; + isNotEmpty ? '${this[0].toLowerCase()}${substring(1)}' : this; } diff --git a/pubspec.yaml b/pubspec.yaml index 4ad0d9a..36039b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: open_exts description: "Extensions. Its that simple..." -version: 0.0.3 +version: 0.0.4 homepage: http://reinemuth.rocks:3000/jens/open_exts publish_to: