**1.0.0**
This commit is contained in:
@@ -17,19 +17,19 @@
|
||||
"name": "flutter",
|
||||
"rootUri": "file:///C:/Users/micwa/fvm/versions/stable/packages/flutter",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "flutter_lints",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_lints-5.0.0",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_lints-6.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "lints",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/lints-5.1.1",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/lints-6.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "material_color_utilities",
|
||||
@@ -39,33 +39,32 @@
|
||||
},
|
||||
{
|
||||
"name": "meta",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.16.0",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.17.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "sky_engine",
|
||||
"rootUri": "file:///C:/Users/micwa/fvm/versions/stable/bin/cache/pkg/sky_engine",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "vector_math",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/vector_math-2.1.4",
|
||||
"rootUri": "file:///C:/Users/micwa/AppData/Local/Pub/Cache/hosted/pub.dev/vector_math-2.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.14"
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "astromic_extensions",
|
||||
"rootUri": "../",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
"languageVersion": "3.10"
|
||||
}
|
||||
],
|
||||
"generated": "2025-03-30T10:13:12.420517Z",
|
||||
"generator": "pub",
|
||||
"generatorVersion": "3.7.0",
|
||||
"generatorVersion": "3.10.1",
|
||||
"flutterRoot": "file:///C:/Users/micwa/fvm/versions/stable",
|
||||
"flutterVersion": "3.29.0",
|
||||
"flutterVersion": "3.38.3",
|
||||
"pubCache": "file:///C:/Users/micwa/AppData/Local/Pub/Cache"
|
||||
}
|
||||
|
||||
74
.dart_tool/package_graph.json
Normal file
74
.dart_tool/package_graph.json
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"roots": [
|
||||
"astromic_extensions"
|
||||
],
|
||||
"packages": [
|
||||
{
|
||||
"name": "astromic_extensions",
|
||||
"version": "1.0.0",
|
||||
"dependencies": [
|
||||
"flutter"
|
||||
],
|
||||
"devDependencies": [
|
||||
"flutter_lints"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "flutter_lints",
|
||||
"version": "6.0.0",
|
||||
"dependencies": [
|
||||
"lints"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "flutter",
|
||||
"version": "0.0.0",
|
||||
"dependencies": [
|
||||
"characters",
|
||||
"collection",
|
||||
"material_color_utilities",
|
||||
"meta",
|
||||
"sky_engine",
|
||||
"vector_math"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lints",
|
||||
"version": "6.0.0",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "sky_engine",
|
||||
"version": "0.0.0",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "vector_math",
|
||||
"version": "2.2.0",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "meta",
|
||||
"version": "1.17.0",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "material_color_utilities",
|
||||
"version": "0.11.1",
|
||||
"dependencies": [
|
||||
"collection"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "collection",
|
||||
"version": "1.19.1",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "characters",
|
||||
"version": "1.4.0",
|
||||
"dependencies": []
|
||||
}
|
||||
],
|
||||
"configVersion": 1
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
3.29.0
|
||||
3.38.3
|
||||
@@ -1,8 +1,3 @@
|
||||
## 0.1.2
|
||||
- **FEAT** Added Quick widget extensions.
|
||||
# 1.0.0
|
||||
|
||||
## 0.1.1
|
||||
- **FEAT** Added Map Extensions file and `MergeAllMaps` function.
|
||||
|
||||
## 0.1.0
|
||||
- **INIT**: Initial commit in the new repo.
|
||||
* **INIT**: Initial commit in the new repo.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
This repository is Developed, Maintained, and is property of Michael W. Aziz (Micazi).
|
||||
25
LICENSE
Normal file
25
LICENSE
Normal file
@@ -0,0 +1,25 @@
|
||||
# License Agreement
|
||||
|
||||
Copyright (c) 2026 Michael W. Aziz (Micazi). All rights reserved.
|
||||
|
||||
Proprietary and Confidential.
|
||||
|
||||
1. PERMITTED USE
|
||||
This software is the exclusive property of Michael W. Aziz. Use of this
|
||||
software is strictly limited to:
|
||||
(a) Personal projects developed and maintained by Michael W. Aziz.
|
||||
(b) Professional projects or client applications where Michael W. Aziz
|
||||
is a direct contributor or the primary developer.
|
||||
|
||||
2. RESTRICTIONS
|
||||
Unauthorized copying, modification, or distribution of this software
|
||||
outside of the permitted uses defined in Section 1 is strictly prohibited.
|
||||
Clients are granted a license to use the compiled output of this software
|
||||
within their specific project but do not acquire ownership of the
|
||||
underlying source code or the right to redistribute it as a standalone package.
|
||||
|
||||
3. WARRANTY & LIABILITY
|
||||
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES,
|
||||
OR OTHER LIABILITY, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE THEREOF.
|
||||
113
README.md
113
README.md
@@ -1,42 +1,77 @@
|
||||
## Atromic Extensions
|
||||
# Astromic Extensions
|
||||
|
||||
The extensions module of the **Astromic** Presentation System
|
||||
Developed, Maintained, and is property of Michael W. Aziz (Micazi)
|
||||
**The Foundation of the Astromic Presentation System.**
|
||||
|
||||
### Content
|
||||
A collection of high-performance Dart extensions designed to eliminate UI boilerplate and simplify RTL (Right-to-Left) layout logic.
|
||||
|
||||
- on _BorderDirectional_
|
||||
- **all()** : Generate a uniform border with one borderSide. ☑️
|
||||
- on _EdgeInsetsGeometry_
|
||||
- **copyWith()** ☑️
|
||||
- **resolveToDirectional()** : Automatically reverse the EdgeInsets based on the dynamic *TextDirection*. ☑️
|
||||
- on _List<E>_
|
||||
- **containsAll()** : Checker to see if a list contains all of the other list. ☑️
|
||||
- **getUnique()** : Get unique items only of a supplied list. ☑️
|
||||
- on _List<Map<T,B>>_
|
||||
- **mergeAllMaps()** : Merges a list of maps into one with a conditional functionality to approve the merge. ☑️
|
||||
- on _TextAlignVertical_
|
||||
- **toAlignment()** : convert a `TextAlignVertical` to an `Alignment` object. ☑️
|
||||
- on _BorderRadiusGeometry_
|
||||
- **copyWith()** ☑️
|
||||
- **resolveToDirectional()** : Automatically reverse the BorderRadius based on the dynamic *TextDirection*. ☑️
|
||||
- on _String_
|
||||
- **toColor()** : Parse a HexCode `String` into a `Color` object. ☑️
|
||||
- **capitalize()** : Capitalize the word. ☑️
|
||||
- **rich()** : Convert the string to a RichText Widget. ☑️
|
||||
- on _Widget_
|
||||
- **center**
|
||||
- **circular**
|
||||
- **align**
|
||||
- **positioned**
|
||||
- **sized**
|
||||
- **rotated**
|
||||
- **flipH**
|
||||
- **flipV**
|
||||
- **safeArea**
|
||||
- **opacity**
|
||||
- **padAll**
|
||||
- **padSymH**
|
||||
- **padSymV**
|
||||
- **padTop**
|
||||
- **padBot**
|
||||
---
|
||||
|
||||
## 🛠 Features & API Reference
|
||||
|
||||
### Layout & Spacing
|
||||
|
||||
* **.resolveToDirectional()** (`EdgeInsetsGeometry`): Automatically flips insets based on the current `TextDirection`.
|
||||
|
||||
* **.resolveToDirectional()** (`BorderRadiusGeometry`): Reverses corner radii (e.g., top-left to top-right) for RTL support.
|
||||
|
||||
* **.copyWith()** (`EdgeInsetsGeometry`): An additive copy method for combining geometries.
|
||||
|
||||
* **.padAll / .symH / .symV** (`num`): Quick-access padding generators directly from numbers.
|
||||
|
||||
### Widget Wrappers (Fluent UI)
|
||||
|
||||
* **Layout**: `.center()`, `.align()`, `.positioned()`, `.sized()`, `.rotated()`
|
||||
|
||||
* **Transform**: `.flipH()`, `.flipV()`, `.opacity()`
|
||||
|
||||
* **Spacing**: `.padAll()`, `.padSymH()`, `.padSymV()`, `.padTop()`, `.padBot()`
|
||||
|
||||
* **Utility**: `.safeArea()`, `.circular()`
|
||||
|
||||
### Data & Styling Utilities
|
||||
|
||||
* **containsAll()** (`List<E>`): Optimized $O(n)$ check to see if a list contains all elements of another.
|
||||
|
||||
* **getUnique()** (`List<E>`): Returns a list with duplicates removed using high-performance Set hashing.
|
||||
|
||||
* **mergeAllMaps()** (`List<Map>`): Performs a conditional deep merge of map collections.
|
||||
|
||||
* **.toColor()** (`String`): Parses Hex strings into Flutter `Color` objects.
|
||||
|
||||
* **.capitalize()** (`String`): Standardizes string casing.
|
||||
|
||||
* **.rich()** (`String`): Shorthand to convert a string directly into a `RichText` widget.
|
||||
|
||||
* **.toAlignment()** (`TextAlignVertical`): Converts vertical text alignment to an `Alignment` object.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
astromic_extensions:
|
||||
git:
|
||||
url: git.micazi.dev/micazi/astromic_extensions.git
|
||||
ref: main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Client Handoff & Continuity
|
||||
|
||||
To ensure project stability following the engagement, clients should follow these steps to secure a local version of this library:
|
||||
|
||||
1. Mirror Repository: Clone this repository and re-upload it to your internal Git organization.
|
||||
|
||||
2. Update Source: Update the pubspec.yaml in your application to point to your internal Git URL.
|
||||
|
||||
3. Versioning: Always use a specific Git ref (tag or commit hash) to ensure build reproducibility.
|
||||
|
||||
Note: Access to the Micazi master repository is strictly controlled. Unauthorized redistribution of this source code is prohibited under the included LICENSE terms.
|
||||
|
||||
---
|
||||
|
||||
## ⚖️ License
|
||||
|
||||
Developed, Maintained, and property of Michael W. Aziz (Micazi). Refer to the [LICENSE](LICENSE) file for permitted use in client projects.
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
include: package:lints/recommended.yaml
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
analyzer:
|
||||
language:
|
||||
# Ensures the compiler never implicitly assumes 'dynamic' when it cannot infer a type.
|
||||
strict-inference: true
|
||||
# Disallows the use of generic types without explicit type arguments (e.g., List instead of List<int>).
|
||||
strict-raw-types: true
|
||||
errors:
|
||||
# Elevates missing required parameters from a warning to a hard compile error.
|
||||
missing_required_param: error
|
||||
# Elevates missing return statements in non-void functions to a hard compile error.
|
||||
missing_return: error
|
||||
|
||||
linter:
|
||||
rules:
|
||||
# ==== Project Organization Rules ====
|
||||
# Enforces relative imports to maintain project structure and avoid unnecessary long paths
|
||||
# Requires explicit return types for all functions and methods to ensure API clarity.
|
||||
- always_declare_return_types
|
||||
# Discourages redundant type annotations on closure parameters where inference is sufficient.
|
||||
- avoid_types_on_closure_parameters
|
||||
# Requires the @override annotation when a class member overrides a member from a superclass.
|
||||
- annotate_overrides
|
||||
# Enforces the use of relative imports for files within the same package to maintain portability.
|
||||
- prefer_relative_imports
|
||||
|
||||
# Ensures that files are named in lowercase_with_underscores format
|
||||
- file_names
|
||||
|
||||
# ==== Best Practices ====
|
||||
# Enforces the closing of streams and sinks to avoid memory leaks
|
||||
- close_sinks
|
||||
|
||||
# Avoids empty 'else' blocks to reduce confusion and improve code readability
|
||||
- avoid_empty_else
|
||||
|
||||
# Prefer using 'const' constructors wherever possible for better performance and immutability
|
||||
# Enables the use of the 'super.parameter' syntax in constructors to reduce boilerplate.
|
||||
- use_super_parameters
|
||||
# Encourages 'const' constructors for classes to optimize memory and performance.
|
||||
- prefer_const_constructors
|
||||
|
||||
# Avoid leading underscores for local variable names to prevent conflicts and improve clarity
|
||||
- no_leading_underscores_for_local_identifiers
|
||||
|
||||
# ==== Code Consistency ====
|
||||
# Avoids the use of 'print' statements in production code, encouraging proper logging instead
|
||||
- avoid_print
|
||||
|
||||
# Encourages using 'final' for fields that are not reassigned to promote immutability
|
||||
# Requires 'const' for variable declarations that are initialized with a constant value.
|
||||
- prefer_const_declarations
|
||||
# Encourages declaring local variables as 'final' if they are not reassigned.
|
||||
- prefer_final_locals
|
||||
# Encourages declaring private fields as 'final' if they are not reassigned.
|
||||
- prefer_final_fields
|
||||
|
||||
# Ensures that all types are explicitly specified for better readability and type safety
|
||||
- always_specify_types
|
||||
|
||||
# Ensures constructors are at the top of the class for better readability and consistency
|
||||
- sort_constructors_first
|
||||
|
||||
# Avoids redundant default argument values to keep the code clean
|
||||
- avoid_redundant_argument_values
|
||||
|
||||
# Enforces consistency by preferring single quotes over double quotes for string literals
|
||||
# Triggers a warning when a future is not awaited or explicitly handled, preventing race conditions.
|
||||
- unawaited_futures
|
||||
# Prevents accessing BuildContext across asynchronous gaps to avoid runtime crashes.
|
||||
- use_build_context_synchronously
|
||||
# Ensures that Sink and StreamController instances are properly closed to prevent memory leaks.
|
||||
- close_sinks
|
||||
# Prevents the use of control flow statements (like return or throw) inside a finally block.
|
||||
- control_flow_in_finally
|
||||
# Standardizes string literals to use single quotes unless the string contains a single quote.
|
||||
- prefer_single_quotes
|
||||
|
||||
# ==== Documentation Rules ====
|
||||
# Enforces documentation for all public classes, methods, and fields to improve API clarity
|
||||
# - public_member_api_docs
|
||||
|
||||
# ==== Null Safety ====
|
||||
# Avoids unnecessary null checks and encourages the use of null-aware operators
|
||||
- unnecessary_null_checks
|
||||
# Requires constructors to be placed before other members in a class.
|
||||
- sort_constructors_first
|
||||
# Disallows leading underscores for local variables to distinguish them from private class members.
|
||||
- no_leading_underscores_for_local_identifiers
|
||||
@@ -1,2 +0,0 @@
|
||||
[0330/121753.404:ERROR:crashpad_client_win.cc(811)] not connected
|
||||
[0330/121753.573:ERROR:crashpad_client_win.cc(811)] not connected
|
||||
@@ -1,10 +1,5 @@
|
||||
library;
|
||||
|
||||
export './src/border_extensions.dart';
|
||||
export './src/insets_extension.dart';
|
||||
export './src/list_extensions.dart';
|
||||
export './src/map_extensions.dart';
|
||||
export 'src/alignment_extensions.dart';
|
||||
export './src/radius_extensions.dart';
|
||||
export './src/string_extensions.dart';
|
||||
export './src/widget_extensions.dart';
|
||||
export './src/data_and_logic.dart';
|
||||
export './src/layout_and_spacing.dart';
|
||||
export './src/widget_wrappers.dart';
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension TextAlignVerticalExtension on TextAlignVertical {
|
||||
Alignment toAlignment() {
|
||||
switch (this) {
|
||||
case TextAlignVertical.top:
|
||||
return Alignment.topCenter;
|
||||
case TextAlignVertical.center:
|
||||
return Alignment.center;
|
||||
case TextAlignVertical.bottom:
|
||||
return Alignment.bottomCenter;
|
||||
default:
|
||||
return Alignment.center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import 'package:flutter/painting.dart';
|
||||
|
||||
extension BorderExtension on BorderDirectional {
|
||||
BorderDirectional all(BorderSide side) {
|
||||
return BorderDirectional(top: side, bottom: side, start: side, end: side);
|
||||
}
|
||||
}
|
||||
95
lib/src/data_and_logic.dart
Normal file
95
lib/src/data_and_logic.dart
Normal file
@@ -0,0 +1,95 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension StringExtensions on String {
|
||||
/// Parses a HexCode string into a [Color] object.
|
||||
/// Supports formats like '#FFFFFF', 'FFFFFF', or '0xFFFFFFFF'.
|
||||
Color get toColor {
|
||||
if (this == '') return const Color(0xFF000000);
|
||||
String hexColor = replaceAll('#', '');
|
||||
hexColor = hexColor.replaceAll('0x', '');
|
||||
hexColor = hexColor.padLeft(6, '0');
|
||||
hexColor = hexColor.padLeft(8, 'F');
|
||||
final int length = hexColor.length;
|
||||
return Color(int.tryParse('0x${hexColor.substring(length - 8, length)}') ?? 0xFF000000);
|
||||
}
|
||||
|
||||
/// Capitalizes the first letter of the string.
|
||||
String get capitalize => (length > 1) ? this[0].toUpperCase() + substring(1) : toUpperCase();
|
||||
|
||||
/// Converts CamelCase strings to a space-separated Title Case string.
|
||||
/// Example: "myVariableName" -> "My Variable Name"
|
||||
String get camelCaseToTitle {
|
||||
final String spaced = replaceAllMapped(
|
||||
RegExp(r'([a-z])([A-Z])'),
|
||||
(match) => '${match.group(1)} ${match.group(2)}',
|
||||
);
|
||||
return spaced.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
||||
}
|
||||
|
||||
/// Returns [anotherString] if the current string is empty or contains only whitespace.
|
||||
String replaceIfEmpty(String anotherString) => trim().isEmpty ? anotherString : this;
|
||||
}
|
||||
|
||||
extension NullableStringExtensions on String? {
|
||||
/// Returns [anotherString] if the current string is null, empty, or only whitespace.
|
||||
String replaceIfEmptyOrNull(String anotherString) => (this == null || this!.trim().isEmpty) ? anotherString : this!;
|
||||
}
|
||||
|
||||
extension ListEx<E> on List<E>? {
|
||||
/// Returns true if the list contains all elements of [otherList]. Optimized $O(n)$.
|
||||
bool containsAll(List<E> otherList) {
|
||||
if (this == null || this!.isEmpty) return false;
|
||||
if (otherList.isEmpty) return true;
|
||||
final thisSet = this!.toSet();
|
||||
return otherList.every(thisSet.contains);
|
||||
}
|
||||
|
||||
/// Returns a new list containing only unique elements from the current list.
|
||||
List<E> getUnique() => this?.toSet().toList() ?? <E>[];
|
||||
|
||||
/// Returns a list containing only non-null elements.
|
||||
List<E> get nonNulls => this?.whereType<E>().toList() ?? [];
|
||||
|
||||
/// Safely attempts to reduce the list to a single value. Returns null if empty.
|
||||
E? tryReduce(E Function(E a, E b) reducer) {
|
||||
if (this == null || this!.isEmpty) return null;
|
||||
final filtered = nonNulls;
|
||||
return filtered.isNotEmpty ? filtered.reduce(reducer) : null;
|
||||
}
|
||||
|
||||
/// Returns a sublist limited to the first [limit] items.
|
||||
List<E>? limit(int limit) => (this?.length ?? 0) > limit ? this?.sublist(0, limit) : this;
|
||||
}
|
||||
|
||||
extension MapListExtensions<T, B> on List<Map<T, B>> {
|
||||
/// Merges a list of maps into a single map.
|
||||
/// An optional [repeatingKeysValueComparer] can handle logic when keys collide.
|
||||
Map<T, B> mergeAllMaps({void Function(T key, B value1, B value2)? repeatingKeysValueComparer}) {
|
||||
final Map<T, B> r = <T, B>{};
|
||||
for (var m in this) {
|
||||
m.forEach((key, value) {
|
||||
if (!r.containsKey(key)) {
|
||||
r[key] = value;
|
||||
} else {
|
||||
repeatingKeysValueComparer?.call(key, r[key] as B, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
extension IntExtensions on int {
|
||||
/// Generates a random numeric pin code with a length equal to the integer value.
|
||||
int get generatePinCode {
|
||||
if (this <= 0) return 0;
|
||||
final min = pow(10, this - 1).toInt();
|
||||
final max = pow(10, this).toInt() - 1;
|
||||
return Random().nextInt(max - min + 1) + min;
|
||||
}
|
||||
}
|
||||
extension DoubleExtensions on double {
|
||||
/// Clamps the double value between [min] and [max].
|
||||
double clampDouble(double min, double max) => this < min ? min : (this > max ? max : this);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
extension InsetsExtension on EdgeInsetsGeometry {
|
||||
copyWith(EdgeInsetsGeometry g) => add(g);
|
||||
//
|
||||
EdgeInsetsDirectional resolveToDirectional(TextDirection direction) {
|
||||
//
|
||||
return EdgeInsetsDirectional.fromSTEB(direction == TextDirection.ltr ? resolve(direction).left : resolve(direction).right, resolve(direction).top,
|
||||
direction == TextDirection.ltr ? resolve(direction).right : resolve(direction).left, resolve(direction).bottom);
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
extension InsetsNumExtension on num {
|
||||
EdgeInsets get symH => EdgeInsets.symmetric(horizontal: toDouble());
|
||||
EdgeInsets get symV => EdgeInsets.symmetric(vertical: toDouble());
|
||||
EdgeInsets get padAll => EdgeInsets.all(toDouble());
|
||||
EdgeInsets get padTop => EdgeInsets.only(top: toDouble());
|
||||
EdgeInsets get padBot => EdgeInsets.only(bottom: toDouble());
|
||||
}
|
||||
61
lib/src/layout_and_spacing.dart
Normal file
61
lib/src/layout_and_spacing.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
extension InsetsExtension on EdgeInsetsGeometry {
|
||||
/// Combines two [EdgeInsetsGeometry] objects by adding them together.
|
||||
EdgeInsetsGeometry copyWith(EdgeInsetsGeometry g) => add(g);
|
||||
|
||||
/// Converts [EdgeInsetsGeometry] to [EdgeInsetsDirectional] based on [direction].
|
||||
EdgeInsetsDirectional resolveToDirectional(TextDirection direction) {
|
||||
final res = resolve(direction);
|
||||
final isLtr = direction == TextDirection.ltr;
|
||||
return EdgeInsetsDirectional.fromSTEB(
|
||||
isLtr ? res.left : res.right, res.top,
|
||||
isLtr ? res.right : res.left, res.bottom
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension InsetsNumExtension on num {
|
||||
/// Returns [EdgeInsets] with symmetric horizontal spacing.
|
||||
EdgeInsets get symH => EdgeInsets.symmetric(horizontal: toDouble());
|
||||
|
||||
/// Returns [EdgeInsets] with symmetric vertical spacing.
|
||||
EdgeInsets get symV => EdgeInsets.symmetric(vertical: toDouble());
|
||||
|
||||
/// Returns [EdgeInsets] with uniform spacing on all sides.
|
||||
EdgeInsets get padAll => EdgeInsets.all(toDouble());
|
||||
|
||||
/// Returns [EdgeInsets] with spacing only on the top.
|
||||
EdgeInsets get padTop => EdgeInsets.only(top: toDouble());
|
||||
|
||||
/// Returns [EdgeInsets] with spacing only on the bottom.
|
||||
EdgeInsets get padBot => EdgeInsets.only(bottom: toDouble());
|
||||
|
||||
/// Returns [EdgeInsetsDirectional] with spacing only at the start.
|
||||
EdgeInsetsDirectional get padStart => EdgeInsetsDirectional.only(start: toDouble());
|
||||
|
||||
/// Returns [EdgeInsetsDirectional] with spacing only at the end.
|
||||
EdgeInsetsDirectional get padEnd => EdgeInsetsDirectional.only(end: toDouble());
|
||||
|
||||
/// Returns a width value equal to a percentage of the screen width.
|
||||
double ofSW(BuildContext c) => MediaQuery.sizeOf(c).width * toDouble() / 100;
|
||||
|
||||
/// Returns a height value equal to a percentage of the screen height.
|
||||
double ofSH(BuildContext c) => MediaQuery.sizeOf(c).height * toDouble() / 100;
|
||||
}
|
||||
|
||||
extension RadiusExtension on BorderRadiusGeometry {
|
||||
/// Combines two [BorderRadiusGeometry] objects by adding them together.
|
||||
BorderRadiusGeometry copyWith(BorderRadiusGeometry g) => add(g);
|
||||
|
||||
/// Resolves [BorderRadiusGeometry] to [BorderRadiusDirectional] based on [direction].
|
||||
BorderRadiusDirectional resolveToDirectional(TextDirection direction) {
|
||||
final res = resolve(direction);
|
||||
return BorderRadiusDirectional.only(
|
||||
topStart: direction == TextDirection.ltr ? res.topLeft : res.topRight,
|
||||
topEnd: direction == TextDirection.rtl ? res.topLeft : res.topRight,
|
||||
bottomEnd: direction == TextDirection.rtl ? res.bottomLeft : res.bottomRight,
|
||||
bottomStart: direction == TextDirection.ltr ? res.bottomLeft : res.bottomRight,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
extension ListExtension<E> on List<E> {
|
||||
bool containsAll(List<E> otherList) {
|
||||
List<bool> checks = <bool>[];
|
||||
//
|
||||
for (E thisElement in otherList) {
|
||||
checks.add(contains(thisElement));
|
||||
}
|
||||
return !checks.contains(false);
|
||||
}
|
||||
|
||||
List<E> getUnique() {
|
||||
List<E> uniqueItems = <E>[];
|
||||
for (E thisElement in this) {
|
||||
if (!uniqueItems.contains(thisElement)) {
|
||||
uniqueItems.add(thisElement);
|
||||
}
|
||||
}
|
||||
return uniqueItems;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
extension MapExtensions<B, T> on Map<B, T> {}
|
||||
|
||||
extension MapListExtensions<T, B> on List<Map<T, B>> {
|
||||
Map<T, B> mergeAllMaps({Function(T key, B value1, B value2)? repeatingKeysValueComparer}) {
|
||||
Map<T, B> r = <T, B>{};
|
||||
//
|
||||
for (Map<T, B> m in this) {
|
||||
m.forEach((T key, B value) {
|
||||
if (!r.containsKey(key)) {
|
||||
r.addEntries(<MapEntry<T, B>>[MapEntry<T, B>(key, value)]);
|
||||
} else {
|
||||
repeatingKeysValueComparer?.call(key, r[key] as B, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
//
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
extension RadiusExtension on BorderRadiusGeometry {
|
||||
copyWith(BorderRadiusGeometry g) => add(g);
|
||||
//
|
||||
BorderRadiusDirectional resolveToDirectional(TextDirection direction) {
|
||||
//
|
||||
return BorderRadiusDirectional.only(
|
||||
topStart: direction == TextDirection.ltr ? resolve(direction).topLeft : resolve(direction).topRight,
|
||||
topEnd: direction == TextDirection.rtl ? resolve(direction).topLeft : resolve(direction).topRight,
|
||||
bottomEnd: direction == TextDirection.rtl ? resolve(direction).bottomRight : resolve(direction).bottomRight,
|
||||
bottomStart: direction == TextDirection.ltr ? resolve(direction).bottomLeft : resolve(direction).bottomRight,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension StringExtensions on String {
|
||||
/// Parse the HexCode to color
|
||||
Color get toColor {
|
||||
if (this == '') return const Color(0xFF000000);
|
||||
String hexColor = replaceAll('#', '');
|
||||
hexColor = hexColor.replaceAll('0x', '');
|
||||
hexColor = hexColor.padLeft(6, '0');
|
||||
hexColor = hexColor.padLeft(8, 'F');
|
||||
final int length = hexColor.length;
|
||||
return Color(int.tryParse('0x${hexColor.substring(length - 8, length)}') ?? 0xFF000000);
|
||||
}
|
||||
|
||||
/// Capitalize the first letter in a string.
|
||||
String get capitalize {
|
||||
return (length > 1) ? this[0].toUpperCase() + substring(1) : toUpperCase();
|
||||
}
|
||||
|
||||
/// Convert CamelCase to a capitalized space separated title.
|
||||
String get camelCaseToTitle {
|
||||
final String spaced = replaceAllMapped(
|
||||
RegExp(r'([a-z])([A-Z])'),
|
||||
(Match match) => '${match.group(1)} ${match.group(2)}',
|
||||
);
|
||||
|
||||
return spaced.split(' ').map((String word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'insets_extension.dart';
|
||||
|
||||
extension StringWidgetExtensions on String {
|
||||
/// Convert this text into a RichText with custom quick styles and tap gestures.
|
||||
RichText rich(TextStyle masterStyle, {Map<int, TextStyle>? styles, Map<int, VoidCallback>? tapCallbacks, TextAlign textAlign = TextAlign.center}) {
|
||||
Map<String, TextStyle> mappedStyles = <String, TextStyle>{};
|
||||
Map<String, VoidCallback> mappedCallbacks = <String, VoidCallback>{};
|
||||
|
||||
// Getting string pieces.
|
||||
List<String> stringPieces = RegExp(r'`([^`]*)`').allMatches(this).map((RegExpMatch m) => m.group(1)).whereType<String>().toList();
|
||||
|
||||
// Looping on the pieces...
|
||||
for (String stringPiece in stringPieces) {
|
||||
String textAfter = split('`$stringPiece`')[1];
|
||||
String textBeforeNext = textAfter.split('`')[0];
|
||||
if (RegExp(r'{(\d+)}').hasMatch(textBeforeNext)) {
|
||||
// The current piece has an index
|
||||
int? itemIndex = int.tryParse(RegExp(r'{(\d+)}').allMatches(textBeforeNext).map((RegExpMatch m) => m.group(1)).whereType<String>().first);
|
||||
if (itemIndex != null) {
|
||||
// Styles
|
||||
if (styles != null && styles.isNotEmpty && styles.keys.contains(itemIndex)) {
|
||||
// Custom Style
|
||||
mappedStyles.addEntries(<MapEntry<String, TextStyle>>[MapEntry<String, TextStyle>(stringPiece, styles[itemIndex]!)]);
|
||||
} else {
|
||||
// Master
|
||||
mappedStyles.addEntries(<MapEntry<String, TextStyle>>[MapEntry<String, TextStyle>(stringPiece, masterStyle)]);
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
if (tapCallbacks != null && tapCallbacks.isNotEmpty && tapCallbacks.keys.contains(itemIndex)) {
|
||||
// Add tap callbak
|
||||
mappedCallbacks.addEntries(<MapEntry<String, VoidCallback>>[MapEntry<String, VoidCallback>(stringPiece, tapCallbacks[itemIndex]!)]);
|
||||
}
|
||||
} else {
|
||||
debugPrint('Something wrong with applying custom indexing in QuickRichText. $itemIndex');
|
||||
}
|
||||
} else {
|
||||
mappedStyles.addEntries(<MapEntry<String, TextStyle>>[MapEntry<String, TextStyle>(stringPiece, masterStyle)]);
|
||||
}
|
||||
}
|
||||
// Adding styles to the children
|
||||
List<InlineSpan> children = mappedStyles.entries
|
||||
.toList()
|
||||
.sublist(1)
|
||||
.map((MapEntry<String, TextStyle> entry) => TextSpan(
|
||||
text: entry.key,
|
||||
style: entry.value,
|
||||
recognizer: mappedCallbacks.containsKey(entry.key) ? (TapGestureRecognizer()..onTap = mappedCallbacks[entry.key]) : null,
|
||||
))
|
||||
.toList();
|
||||
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
text: stringPieces[0],
|
||||
style: masterStyle,
|
||||
children: children,
|
||||
),
|
||||
textAlign: textAlign,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension QuickSimpleWidgets on Widget {
|
||||
/// Center the widget
|
||||
Widget get center => Center(child: this);
|
||||
|
||||
/// Make the widget circular with RRect
|
||||
Widget get circular => ClipRRect(borderRadius: BorderRadius.circular(10000000), child: this);
|
||||
|
||||
/// Wrap with a SizedBox
|
||||
Widget sized({num? w, num? h}) => SizedBox(width: w?.toDouble(), height: h?.toDouble(), child: this);
|
||||
|
||||
/// Wrap with a Positioned
|
||||
Widget positioned({num? bottom, num? top}) => Positioned(bottom: bottom?.toDouble(), top: top?.toDouble(), child: this);
|
||||
|
||||
/// Rotate the widget in quarter turns
|
||||
Widget rotated({int quarterTurns = 0}) => Transform.rotate(angle: (math.pi * 22.5) * quarterTurns, child: this);
|
||||
|
||||
//TODO - Make it directional
|
||||
/// Align
|
||||
Widget align(Alignment a) => Align(alignment: a, child: this);
|
||||
|
||||
/// Flip the widget horizontally
|
||||
Widget get flipH => Transform.flip(flipX: true, child: this);
|
||||
|
||||
/// Flip the widget vertically
|
||||
Widget get flipV => Transform.flip(flipY: true, child: this);
|
||||
|
||||
/// Wrap with a SafeArea
|
||||
Widget get safeArea => SafeArea(child: this);
|
||||
|
||||
/// Quick Opacity Adjustment
|
||||
Widget opacity(num o) => Opacity(opacity: o.toDouble(), child: this);
|
||||
|
||||
/// Padding padAll
|
||||
Widget padAll(num p) => Padding(padding: p.padAll, child: this);
|
||||
|
||||
/// Padding Symmetric Horizontally
|
||||
Widget padSymH(num p) => Padding(padding: p.symH, child: this);
|
||||
|
||||
/// Padding Symmetric Vertically
|
||||
Widget padSymV(num p) => Padding(padding: p.symV, child: this);
|
||||
//
|
||||
/// Padding Only Top
|
||||
Widget padTop(num p) => Padding(padding: p.padTop, child: this);
|
||||
|
||||
/// Padding Only Bottom
|
||||
Widget padBot(num p) => Padding(padding: p.padBot, child: this);
|
||||
}
|
||||
94
lib/src/widget_wrappers.dart
Normal file
94
lib/src/widget_wrappers.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'layout_and_spacing.dart';
|
||||
|
||||
extension ContextExtensions on BuildContext {
|
||||
/// Shortcut to get the current screen width.
|
||||
double get sw => MediaQuery.sizeOf(this).width;
|
||||
|
||||
/// Shortcut to get the current screen height.
|
||||
double get sh => MediaQuery.sizeOf(this).height;
|
||||
}
|
||||
|
||||
extension QuickSimpleWidgets on Widget {
|
||||
/// Wraps the widget in a [Center].
|
||||
Widget get center => Center(child: this);
|
||||
|
||||
/// Clips the widget into a circular shape.
|
||||
Widget get circular => ClipRRect(borderRadius: BorderRadius.circular(1e6), child: this);
|
||||
|
||||
/// Flips the widget horizontally.
|
||||
Widget get flipH => Transform.flip(flipX: true, child: this);
|
||||
|
||||
/// Flips the widget vertically.
|
||||
Widget get flipV => Transform.flip(flipY: true, child: this);
|
||||
|
||||
/// Wraps the widget in a [SafeArea].
|
||||
Widget get safeArea => SafeArea(child: this);
|
||||
|
||||
/// Wraps the widget in a [SizedBox] with specified dimensions.
|
||||
Widget sized({num? w, num? h}) => SizedBox(width: w?.toDouble(), height: h?.toDouble(), child: this);
|
||||
|
||||
/// Wraps the widget in a [Positioned] for use within a [Stack].
|
||||
Widget positioned({num? bottom, num? top}) => Positioned(bottom: bottom?.toDouble(), top: top?.toDouble(), child: this);
|
||||
|
||||
/// Rotates the widget by [quarterTurns] (90 degree increments).
|
||||
Widget rotated({int quarterTurns = 0}) => Transform.rotate(angle: (math.pi / 2) * quarterTurns, child: this);
|
||||
|
||||
/// Wraps the widget in an [Align] widget.
|
||||
Widget align(Alignment a, {TextDirection? textDirection}) => Align(alignment: textDirection != null ? a.resolve(textDirection) : a, child: this);
|
||||
|
||||
/// Changes the [opacity] of the widget.
|
||||
Widget opacity(num o) => Opacity(opacity: o.toDouble(), child: this);
|
||||
|
||||
/// Applies uniform padding around the widget.
|
||||
Widget padAll(num p) => Padding(padding: p.padAll, child: this);
|
||||
|
||||
/// Applies symmetric horizontal padding.
|
||||
Widget padSymH(num p) => Padding(padding: p.symH, child: this);
|
||||
|
||||
/// Applies symmetric vertical padding.
|
||||
Widget padSymV(num p) => Padding(padding: p.symV, child: this);
|
||||
|
||||
/// Applies padding only to the top.
|
||||
Widget padTop(num p) => Padding(padding: p.padTop, child: this);
|
||||
|
||||
/// Applies padding only to the bottom.
|
||||
Widget padBot(num p) => Padding(padding: p.padBot, child: this);
|
||||
|
||||
/// Applies padding to the start (RTL friendly).
|
||||
Widget padStart(num p) => Padding(padding: p.padStart, child: this);
|
||||
|
||||
/// Applies padding to the end (RTL friendly).
|
||||
Widget padEnd(num p) => Padding(padding: p.padEnd, child: this);
|
||||
|
||||
/// Returns the widget if [condition] is true, otherwise returns an empty [SizedBox].
|
||||
Widget visibleIf(bool condition) => condition ? this : const SizedBox.shrink();
|
||||
}
|
||||
|
||||
extension StringWidgetExtensions on String {
|
||||
/// Converts a string into a [RichText] using a custom markup: `Text`{index}.
|
||||
/// [masterStyle] is the default; [styles] and [tapCallbacks] map to the bracketed index.
|
||||
RichText rich(TextStyle masterStyle, {Map<int, TextStyle>? styles, Map<int, VoidCallback>? tapCallbacks, TextAlign textAlign = TextAlign.center}) {
|
||||
final List<InlineSpan> spans = [];
|
||||
final pattern = RegExp(r'`([^`]*)`(?:\{(\d+)\})?');
|
||||
int lastMatchEnd = 0;
|
||||
|
||||
for (final Match match in pattern.allMatches(this)) {
|
||||
if (match.start > lastMatchEnd) {
|
||||
spans.add(TextSpan(text: substring(lastMatchEnd, match.start), style: masterStyle));
|
||||
}
|
||||
final int? index = int.tryParse(match.group(2) ?? '');
|
||||
spans.add(TextSpan(
|
||||
text: match.group(1) ?? '',
|
||||
style: (index != null && styles?[index] != null) ? styles![index] ?? masterStyle : masterStyle,
|
||||
recognizer: (index != null && tapCallbacks?[index] != null) ? (TapGestureRecognizer()..onTap = tapCallbacks![index]) : null,
|
||||
));
|
||||
lastMatchEnd = match.end;
|
||||
}
|
||||
if (lastMatchEnd < length) spans.add(TextSpan(text: substring(lastMatchEnd), style: masterStyle));
|
||||
|
||||
return RichText(textAlign: textAlign, text: TextSpan(children: spans));
|
||||
}
|
||||
}
|
||||
20
pubspec.lock
20
pubspec.lock
@@ -26,18 +26,18 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
|
||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
version: "6.0.0"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
|
||||
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
version: "6.0.0"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -50,10 +50,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -63,10 +63,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
flutter: ">3.27.0"
|
||||
dart: ">=3.10.0 <4.0.0"
|
||||
flutter: ">3.38.0"
|
||||
|
||||
10
pubspec.yaml
10
pubspec.yaml
@@ -1,14 +1,14 @@
|
||||
name: astromic_extensions
|
||||
description: The extensions module of the Astromic Presentation System.
|
||||
description: A collection of high-performance Dart extensions designed to eliminate UI boilerplate and simplify RTL (Right-to-Left) layout logic.
|
||||
publish_to: "none"
|
||||
version: 0.1.2
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ">=3.6.0"
|
||||
flutter: ">3.27.0"
|
||||
sdk: ">=3.10.0"
|
||||
flutter: ">3.38.0"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^5.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
||||
Reference in New Issue
Block a user