├── .github └── workflows │ └── dart.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── analysis_options.yaml ├── example └── main.dart ├── lib ├── animation │ ├── bottom_animation.dart │ └── fade_in_animation.dart ├── enums │ ├── fade_in_animation_enum.dart │ └── file_type_enum.dart ├── flutter_ex_kit.dart ├── functions │ ├── exit_app.dart │ ├── output.dart │ ├── page_controller_extensions.dart │ ├── pagination_function.dart │ └── scrollable_extension.dart ├── number │ └── int.dart ├── responsive │ └── media_querys.dart ├── single_ton.dart ├── string │ └── string_ex.dart ├── theme │ └── color.dart ├── ui │ ├── alignment_widget_extension.dart │ ├── center.dart │ ├── network_image.dart │ ├── num_extensions.dart │ ├── overscroll_off_extension.dart │ ├── padding.dart │ ├── scaffold_extensions.dart │ ├── shrink.dart │ └── string_text_extensions.dart └── validation │ ├── input_format_validation.dart │ └── single_space_formatter.dart ├── package.json ├── pubspec.yaml ├── screenshots └── logo.png └── test └── flutter_ex_kit_test.dart /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | name: "Dart & Flutter Flutter Ex Kit Plugin" 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | build: 17 | name: Build & Release 18 | runs-on: windows-latest 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v3 23 | 24 | - name: Create tar.gz archive 25 | run: | 26 | mkdir dist 27 | tar -czvf dist/flutter_ex_kit.tar.gz -C "${{ github.workspace }}" . 28 | 29 | - name: Create GitHub Release 30 | id: create_release 31 | uses: actions/create-release@v1 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | with: 35 | tag_name: v0.0.${{ github.run_number }} 36 | release_name: Release v0.0.${{ github.run_number }} 37 | draft: false 38 | makeLatest: true 39 | 40 | body: | 41 | 🎉 **Flutter Ex Kit - Release v0.0.${{ github.run_number }}** 🎉 42 | 43 | 🔥 **What's New**: 44 | - Implemented input validation for strings and integers. 45 | - Added string and integer extensions. 46 | - Improved media query handling for better responsiveness. 47 | - Enhanced documentation with better explanations and examples. 48 | 49 | 📢 **Developed by:** Puneet Sharma 50 | 🆔 **GitHub Id:** [@iamapuneet](https://github.com/iamapuneet) 51 | 💼 **Project:** Flutter Ex Kit 52 | 53 | 👉 **Download the latest release below!** 54 | 55 | - name: Upload tar.gz to Release 56 | uses: actions/upload-release-asset@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | upload_url: ${{ steps.create_release.outputs.upload_url }} 61 | asset_path: dist/flutter_ex_kit.tar.gz 62 | asset_name: flutter_ex_kit.tar.gz 63 | asset_content_type: application/gzip 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | build/ 30 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" 8 | channel: "stable" 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | 5 | ## 0.0.2 6 | 7 | * Added documentation comments for BottomAnimation, alignment_widget_extension, and related classes 8 | 9 | ## 0.0.3 10 | 11 | * Updated the `pubspec.yaml` file with versioning, repository information, documentation URL, and license details. 12 | 13 | ## 0.0.6 14 | 15 | - Implemented input validation for strings and integers. 16 | - Added string and integer extension. 17 | - Improved media query handling for better responsiveness. 18 | - Enhanced documentation with better explanations and examples. 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2025 Puneet Sharma 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_ex_kit 2 | 3 | `flutter_ex_kit` is a Flutter utility library that provides a collection of custom widgets, extensions, animations, and utility functions. It is designed to simplify common tasks and improve the developer experience while building Flutter applications. Whether you're looking for easy-to-use animations, advanced navigation features, or helpful logging tools, `flutter_ex_kit` has got you covered. 4 | 5 | ## Features 6 | 7 | - **Custom Animations:** Includes fade-in and slide-up animations that can be customized for smooth transitions. 8 | - **Logging Utility:** Debug logging utility to output logs with custom debug markers. 9 | - **Navigation Extensions:** Convenient methods for navigating between pages using `PageController`. 10 | - **Exit App Handling:** Custom exit behavior when the back button is pressed, with customizable delays. 11 | - **Pagination:** Functions to trigger pagination on scroll events in list views. 12 | - **Scrollable Widget Utility:** Extensions to ensure widgets are visible and scroll to a specific position. 13 | - **File Type Enum:** Enum for handling different image file types like `jpg`, `png`, `gif`, and others. 14 | 15 | ## Installation 16 | 17 | To use `flutter_ex_kit` in your Flutter project, add it as a dependency in your `pubspec.yaml` file: 18 | 19 | ```yaml 20 | dependencies: 21 | flutter: 22 | sdk: flutter 23 | flutter_ex_kit: ^0.0.6 24 | ``` 25 | ```yaml 26 | flutter pub get 27 | ``` 28 | 29 | ## Example Refactoring 30 | 31 | **Before using** `flutter_ex_kit` 32 | 33 | In the traditional approach, you would write the code as follows: 34 | 35 | dart 36 | ``` 37 | SizedBox( 38 | height: 10, 39 | ), 40 | 41 | ``` 42 | 43 | **After using** `flutter_ex_kit` 44 | 45 | With the `flutter_ex_kit` package, you can simplify this code: 46 | 47 | dart 48 | ``` 49 | 10.height, 50 | ``` 51 | 52 | ## Documentation 53 | 54 | For more detailed examples, advanced features, and in-depth documentation on how to use `flutter_ex_kit`, please refer to the full documentation: 55 | 56 | [Read Full Documentation](https://iamapuneet.blogspot.com/blog-post_18) 57 | 58 | ## 🚀 About Me 59 | I am Puneet Sharma, and I am a developer. 60 | 61 | ## License 62 | 63 | [MIT](https://github.com/iamapuneet/flutter_ex_kit/blob/master/LICENSE) 64 | 65 | 66 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | - package_api_docs 25 | - public_member_api_docs -------------------------------------------------------------------------------- /example/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_ex_kit/flutter_ex_kit.dart'; 3 | import 'package:flutter_ex_kit/number/int.dart'; 4 | 5 | class FlutterExKitExample extends StatelessWidget { 6 | const FlutterExKitExample({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Column( 11 | children: [ 12 | // this is for SizedBox height. 13 | 10.height, 14 | // this is for SizedBox height. 15 | 10.width, 16 | // this is for only padding. 17 | Container( 18 | height: 10, 19 | width: 20, 20 | color: Colors.amber, 21 | ).only( 22 | left: 10, 23 | right: 10, 24 | ), 25 | 26 | // Example of ordinal number usage 27 | Text('22 in ordinal form: ${22.ordinal}'), 28 | 29 | // Example of Roman numeral conversion 30 | Text('12 in Roman numeral: ${12.roman}'), 31 | ], 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/animation/bottom_animation.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// A widget that applies a slide-up animation to its child widget. 4 | /// 5 | /// This widget animates its child from bottom to top when it appears on the screen. 6 | class BottomAnimation extends StatefulWidget { 7 | /// The widget to be animated. 8 | final Widget child; 9 | 10 | /// Creates a [BottomAnimation] widget. 11 | /// 12 | /// The [child] parameter must not be null. 13 | const BottomAnimation({super.key, required this.child}); 14 | 15 | @override 16 | State createState() => _BottomAnimationState(); 17 | } 18 | 19 | class _BottomAnimationState extends State 20 | with SingleTickerProviderStateMixin { 21 | /// Controls the animation's state and progress. 22 | late AnimationController _controller; 23 | 24 | /// Defines the slide transition's offset, animating from bottom to top. 25 | late Animation _animation; 26 | 27 | @override 28 | void initState() { 29 | super.initState(); 30 | 31 | // Initialize the AnimationController with a duration of 600ms. 32 | _controller = AnimationController( 33 | vsync: this, // Provides a TickerProvider for animation synchronization. 34 | duration: const Duration(milliseconds: 600), 35 | ); 36 | 37 | // Define the slide animation from off-screen (bottom) to its final position. 38 | _animation = Tween( 39 | begin: const Offset(0, 1), // Start position (off-screen at the bottom) 40 | end: const Offset(0, 0), // End position (final on-screen position) 41 | ).animate( 42 | CurvedAnimation( 43 | parent: _controller, 44 | curve: Curves.easeIn, // Smooth easing effect. 45 | ), 46 | ); 47 | 48 | // Start the animation when the widget is initialized. 49 | _controller.forward(); 50 | } 51 | 52 | @override 53 | void dispose() { 54 | // Dispose of the animation controller to free up resources. 55 | _controller.dispose(); 56 | super.dispose(); 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | // The SlideTransition widget animates the position of the child widget. 62 | return SlideTransition( 63 | position: _animation, // Apply the slide animation. 64 | child: widget.child, // The animated child widget. 65 | ); 66 | } 67 | 68 | /// Returns the animation controller for external control or status monitoring. 69 | AnimationController get controller => _controller; 70 | 71 | /// Returns the animation used for sliding the child widget. 72 | Animation get animation => _animation; 73 | } 74 | -------------------------------------------------------------------------------- /lib/animation/fade_in_animation.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// A widget that applies a fade-in and sliding animation to its child widget. 4 | /// 5 | /// This widget animates its child widget by fading it in while simultaneously sliding it 6 | /// in from one of four possible directions: top-to-bottom (ttb), bottom-to-top (btt), 7 | /// left-to-right (ltr), or right-to-left (rtl). The animation duration and direction 8 | /// can be customized using the `delay`, `fadeOffset`, and `direction` parameters. 9 | /// 10 | /// Example usage: 11 | /// ```dart 12 | /// FadeInAni( 13 | /// delay: 1.5, // Adjusts the animation duration 14 | /// fadeOffset: 50.0, // Controls how far the widget slides in 15 | /// direction: FadeInDirection.ltr, // Animation direction: left-to-right 16 | /// child: YourWidget(), 17 | /// ); 18 | /// ``` 19 | /// 20 | /// Parameters: 21 | /// - [child]: The widget to animate. 22 | /// - [delay]: A multiplier for the duration of the animation, where 1.0 is the normal speed. 23 | /// - [fadeOffset]: The distance the widget moves during the fade-in animation (in logical pixels). 24 | /// - [direction]: Specifies the direction from which the widget slides in. It can be one of the following: 25 | /// - `FadeInDirection.ltr`: Left to right. 26 | /// - `FadeInDirection.rtl`: Right to left. 27 | /// - `FadeInDirection.ttb`: Top to bottom. 28 | /// - `FadeInDirection.btt`: Bottom to top. 29 | class FadeInAni extends StatefulWidget { 30 | /// Creates a [FadeInAni] widget. 31 | /// 32 | /// Requires [child], [delay], [direction], and [fadeOffset] parameters. 33 | const FadeInAni({ 34 | super.key, 35 | required this.child, 36 | required this.delay, 37 | required this.direction, 38 | required this.fadeOffset, 39 | }); 40 | 41 | /// The widget to be animated. 42 | final Widget child; 43 | 44 | /// The duration multiplier for the animation. 45 | final double delay; 46 | 47 | /// The distance the widget moves during the fade-in animation. 48 | final double fadeOffset; 49 | 50 | /// The direction from which the widget slides in. 51 | final FadeInDirection direction; 52 | 53 | @override 54 | State createState() => _FadeInAniState(); 55 | } 56 | 57 | class _FadeInAniState extends State with TickerProviderStateMixin { 58 | /// Controls the animation progress. 59 | late AnimationController controller; 60 | 61 | /// Controls the opacity transition. 62 | late Animation opacityAnimation; 63 | 64 | /// Controls the slide transition based on [FadeInDirection]. 65 | late Animation inAnimation; 66 | 67 | @override 68 | void initState() { 69 | super.initState(); 70 | 71 | // Initialize the animation controller with a duration based on the delay value. 72 | controller = AnimationController( 73 | duration: Duration(milliseconds: (500 * widget.delay).round()), 74 | vsync: this, 75 | ); 76 | 77 | // Define the sliding animation using Tween and animate based on controller. 78 | inAnimation = 79 | Tween(begin: -widget.fadeOffset, end: 0).animate(controller) 80 | ..addListener(() { 81 | setState(() {}); 82 | }); 83 | 84 | // Define the opacity animation, starting from 0 (invisible) and ending at 1 (fully visible). 85 | opacityAnimation = Tween(begin: 0, end: 1).animate(controller) 86 | ..addListener(() { 87 | setState(() {}); 88 | }); 89 | } 90 | 91 | @override 92 | Widget build(BuildContext context) { 93 | // Start the animation forward as soon as the widget is built. 94 | controller.forward(); 95 | 96 | // Apply the animation transformations: translate and fade. 97 | return Transform.translate( 98 | offset: switch (widget.direction) { 99 | FadeInDirection.ltr => 100 | Offset(inAnimation.value, 0), // Move from left to right 101 | FadeInDirection.rtl => 102 | Offset(-inAnimation.value, 0), // Move from right to left 103 | FadeInDirection.ttb => 104 | Offset(0, inAnimation.value), // Move from top to bottom 105 | FadeInDirection.btt => 106 | Offset(0, -inAnimation.value), // Move from bottom to top 107 | }, 108 | child: Opacity( 109 | opacity: opacityAnimation.value, // Apply fade-in effect 110 | child: widget.child, // Display the animated widget 111 | ), 112 | ); 113 | } 114 | 115 | @override 116 | void dispose() { 117 | // Dispose of the controller when the widget is no longer needed to free up resources. 118 | controller.dispose(); 119 | super.dispose(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/enums/fade_in_animation_enum.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Defines the possible directions for the fade-in animation. 4 | enum FadeInDirection { 5 | /// Top to Bottom. 6 | ttb, 7 | 8 | /// Bottom to Top. 9 | btt, 10 | 11 | /// Left to Right. 12 | ltr, 13 | 14 | /// Right to Left. 15 | rtl, 16 | } 17 | -------------------------------------------------------------------------------- /lib/enums/file_type_enum.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Represents the supported file types for image formats. 4 | enum FileTypeForImageEnum { 5 | /// JPEG format with `.jpg` extension. 6 | jpg("jpg"), 7 | 8 | /// JPEG format with `.jpeg` extension. 9 | jpeg("jpeg"), 10 | 11 | /// PNG format with `.png` extension. 12 | png("png"), 13 | 14 | /// GIF format with `.gif` extension. 15 | gif("gif"), 16 | 17 | /// SVG format with `.svg` extension. 18 | svg("svg"), 19 | 20 | /// WEBP format with `.webp` extension. 21 | webp("webp"); 22 | 23 | /// The string representation of the file type. 24 | final String value; 25 | 26 | /// Creates a file type with the specified string value. 27 | const FileTypeForImageEnum(this.value); 28 | 29 | /// Returns the string representation of the file type. 30 | @override 31 | String toString() => value; 32 | 33 | /// Returns the corresponding [FileTypeForImageEnum] from a string. 34 | /// 35 | /// Throws an [ArgumentError] if the provided [type] is not valid. 36 | static FileTypeForImageEnum fromString(String type) { 37 | return FileTypeForImageEnum.values.firstWhere( 38 | (e) => e.value == type, 39 | orElse: () => throw ArgumentError("Invalid FileType: $type"), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/flutter_ex_kit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_ex_kit/single_ton.dart'; 5 | part 'ui/string_text_extensions.dart'; 6 | part 'ui/scaffold_extensions.dart'; 7 | part 'ui/num_extensions.dart'; 8 | part 'ui/overscroll_off_extension.dart'; 9 | part 'ui/alignment_widget_extension.dart'; 10 | part 'ui/network_image.dart'; 11 | part 'ui/padding.dart'; 12 | part 'ui/center.dart'; 13 | part 'ui/shrink.dart'; 14 | 15 | part 'functions/scrollable_extension.dart'; 16 | part 'functions/exit_app.dart'; 17 | part 'functions/pagination_function.dart'; 18 | part 'functions/page_controller_extensions.dart'; 19 | part 'functions/output.dart'; 20 | 21 | part 'responsive/media_querys.dart'; 22 | part 'theme/color.dart'; 23 | part 'animation/fade_in_animation.dart'; 24 | part 'animation/bottom_animation.dart'; 25 | 26 | part 'enums/fade_in_animation_enum.dart'; 27 | part 'enums/file_type_enum.dart'; 28 | -------------------------------------------------------------------------------- /lib/functions/exit_app.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Handles the exit logic for a Flutter application. 4 | /// 5 | /// This function is used to manage the app's exit behavior when the back button is pressed. 6 | /// It allows the user to press the back button twice within a 2-second window to exit the app. 7 | /// 8 | /// If the user presses the back button once, it shows a message instructing them to press again. 9 | /// If the user presses the back button again within the 2-second window, the app will exit. 10 | /// 11 | /// Example usage: 12 | /// ```dart 13 | /// exitApp( 14 | /// currentBackPressTime: _lastBackPressTime, 15 | /// exit: () { 16 | /// // Custom exit behavior (if needed) 17 | /// }, 18 | /// ); 19 | /// ``` 20 | /// 21 | /// Parameters: 22 | /// - [currentBackPressTime]: The time when the back button was last pressed. This should be 23 | /// updated with the current time on each back button press to track the time difference. 24 | /// - [exit]: An optional callback function that will be executed if the back button is pressed 25 | /// for the first time. If not provided, a default message will be shown. 26 | /// - [notExit]: An optional callback that will be executed when the user chooses not to exit. 27 | /// 28 | /// If the [currentBackPressTime] is null or the time difference since the last press is greater 29 | /// than 2 seconds, it will update the [currentBackPressTime] and either call the [exit] callback 30 | /// or show a default message using the [output] function. If the time difference is less than 31 | /// 2 seconds, it will exit the app using [SystemNavigator.pop]. 32 | void exitApp({ 33 | DateTime? currentBackPressTime, 34 | VoidCallback? exit, 35 | VoidCallback? notExit, 36 | }) { 37 | DateTime now = DateTime.now(); 38 | 39 | // If it's been more than 2 seconds since the last press 40 | if (currentBackPressTime == null || 41 | now.difference(currentBackPressTime) > const Duration(seconds: 2)) { 42 | // Update the time of the last press 43 | currentBackPressTime = now; 44 | 45 | // Execute custom exit behavior or fallback to default 46 | if (exit != null) { 47 | exit.call(); 48 | } else { 49 | notExit?.call(); // Calls the notExit callback if provided 50 | } 51 | } else { 52 | // Exit the app if the second press is within 2 seconds 53 | SystemNavigator.pop(); // Exits the app 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/functions/output.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Logs the provided response to the console with custom debug markers. 4 | /// 5 | /// This function is intended for debugging purposes. It logs the provided [response] 6 | /// to the console only when [kDebugMode] is true. The output is prefixed and suffixed 7 | /// with custom emojis to visually distinguish debug logs. 8 | /// 9 | /// Example usage: 10 | /// ```dart 11 | /// output('This is a debug message'); 12 | /// ``` 13 | /// 14 | /// Parameters: 15 | /// - [response]: The object to log. It will be converted to a string using [toString()] method. 16 | /// 17 | /// Debug output is only shown in development mode, helping to ensure that sensitive information 18 | /// is not exposed in production logs. 19 | void output(Object? response) { 20 | // Accessing the singleton instance of ExKit 21 | FlutterExKit exKit = FlutterExKit(); 22 | 23 | // Log the response with emojis, but only in debug mode 24 | if (exKit.logEnable) { 25 | log(exKit.logEmoji); 26 | log(response.toString()); 27 | log(exKit.logEmojiDown); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/functions/page_controller_extensions.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on [PageController] to provide convenient methods for navigating between pages. 4 | /// 5 | /// This extension adds two methods to [PageController] for navigating to the next and previous pages. 6 | /// It simplifies the process of page navigation in a [PageView] widget by providing convenient methods. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// PageController _pageController = PageController(); 11 | /// 12 | /// // Navigate to the next page 13 | /// _pageController.goToNextPage(); 14 | /// 15 | /// // Navigate to the previous page 16 | /// _pageController.goToPreviousPage(); 17 | /// ``` 18 | /// 19 | /// Methods: 20 | /// - [goToNextPage]: Animates the page view to the next page with a duration of 300 milliseconds 21 | /// and an easing curve of [Curves.easeIn]. 22 | /// - [goToPreviousPage]: Animates the page view to the previous page with a duration of 300 milliseconds 23 | /// and an easing curve of [Curves.easeIn]. 24 | /// 25 | /// Both methods check if the [PageController] has clients before attempting to navigate. This prevents 26 | /// errors if the controller is not attached to a [PageView]. 27 | extension PageControllerExtensions on PageController { 28 | /// Navigates to the next page in the [PageView]. 29 | /// 30 | /// This method animates the page view to the next page with a duration of 300 milliseconds and 31 | /// an easing curve of [Curves.easeIn]. It only performs the animation if the [PageController] has clients. 32 | void goToNextPage() { 33 | if (hasClients) { 34 | nextPage( 35 | duration: const Duration(milliseconds: 300), 36 | curve: Curves.easeIn, 37 | ); 38 | } 39 | } 40 | 41 | /// Navigates to the previous page in the [PageView]. 42 | /// 43 | /// This method animates the page view to the previous page with a duration of 300 milliseconds and 44 | /// an easing curve of [Curves.easeIn]. It only performs the animation if the [PageController] has clients. 45 | void goToPreviousPage() { 46 | if (hasClients) { 47 | previousPage( 48 | duration: const Duration(milliseconds: 300), 49 | curve: Curves.easeIn, 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/functions/pagination_function.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Adds pagination functionality to a scrollable widget. 4 | /// 5 | /// This function allows you to trigger a callback function when the user scrolls 6 | /// to the end of the list. It uses a [ScrollController] to monitor the scroll 7 | /// position and invokes the provided [fun] callback when the maximum scroll extent 8 | /// is reached. 9 | /// 10 | /// Example usage: 11 | /// ```dart 12 | /// ScrollController _controller = ScrollController(); 13 | /// paginationFunction( 14 | /// fun: () { 15 | /// // Load more data or perform pagination 16 | /// }, 17 | /// controller: _controller, 18 | /// ); 19 | /// ``` 20 | /// 21 | /// Parameters: 22 | /// - [fun]: A required callback function to be executed when the user scrolls 23 | /// to the end of the scrollable widget. This function is called when the maximum 24 | /// scroll extent is reached. 25 | /// - [controller]: A required [ScrollController] instance that controls the scrollable 26 | /// widget and is used to listen to scroll events. 27 | /// 28 | /// The [paginationFunction] attaches a listener to the provided [ScrollController], 29 | /// which checks if the current scroll offset has reached the maximum scroll extent. 30 | /// When this condition is met, it calls the provided callback function [fun]. 31 | void paginationFunction({ 32 | required Function() fun, 33 | required ScrollController controller, 34 | }) { 35 | controller.addListener( 36 | () { 37 | if (controller.position.maxScrollExtent == controller.offset) { 38 | fun.call(); 39 | } 40 | }, 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /lib/functions/scrollable_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on [BuildContext] to provide a method for scrolling a widget into view. 4 | /// 5 | /// This extension adds a convenient method to the [BuildContext] for ensuring a widget is visible 6 | /// and optionally scrolling it by a specific number of pixels using a [ScrollController]. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// context.scrollToVisible( 11 | /// scrollController: myScrollController, 12 | /// pixels: 50.0, 13 | /// duration: Duration(milliseconds: 200), 14 | /// curve: Curves.easeInOut, 15 | /// ); 16 | /// ``` 17 | /// 18 | /// Parameters: 19 | /// - [scrollController]: An optional [ScrollController] used to perform the scroll action. If not provided, 20 | /// the method will only ensure the widget is visible without scrolling further. 21 | /// - [pixels]: The number of pixels to scroll. Defaults to 30.0. 22 | /// - [duration]: The duration of the scrolling animation. Defaults to 100 milliseconds. 23 | /// - [curve]: The animation curve used for scrolling. Defaults to [Curves.easeInOut]. 24 | /// 25 | /// This method first ensures that the widget is visible on the screen using [Scrollable.ensureVisible], 26 | /// and then, if a [ScrollController] is provided, scrolls the widget by the specified number of pixels 27 | /// after a delay. 28 | extension ScrollableExtension on BuildContext { 29 | /// Ensures that the widget associated with this [BuildContext] is visible and scrolls it by the specified number of pixels. 30 | /// 31 | /// The method uses [Scrollable.ensureVisible] to ensure that the widget is visible. If a [ScrollController] 32 | /// is provided, it scrolls the widget by the specified number of pixels after the initial visibility check. 33 | /// 34 | /// Example: 35 | /// ```dart 36 | /// context.scrollToVisible( 37 | /// scrollController: myScrollController, 38 | /// pixels: 50.0, 39 | /// duration: Duration(milliseconds: 200), 40 | /// curve: Curves.easeInOut, 41 | /// ); 42 | /// ``` 43 | /// 44 | /// [scrollController] - An optional [ScrollController] for controlling the scrolling action. If null, only visibility is ensured. 45 | /// [pixels] - The number of pixels to scroll after ensuring the widget is visible. Defaults to 30.0. 46 | /// [duration] - The duration of the scrolling animation. Defaults to 100 milliseconds. 47 | /// [curve] - The animation curve for the scrolling. Defaults to [Curves.easeInOut]. 48 | void scrollToVisible({ 49 | ScrollController? scrollController, 50 | double pixels = 30.0, 51 | Duration duration = const Duration(milliseconds: 100), 52 | Curve curve = Curves.easeInOut, 53 | }) { 54 | // Ensure the widget is visible 55 | Scrollable.ensureVisible( 56 | this, 57 | duration: Duration.zero, 58 | curve: curve, 59 | ); 60 | 61 | // Scroll by a specific number of pixels 62 | if (scrollController != null) { 63 | Future.delayed(duration, () { 64 | scrollController.animateTo( 65 | scrollController.offset - pixels, 66 | duration: duration, 67 | curve: curve, 68 | ); 69 | }); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/number/int.dart: -------------------------------------------------------------------------------- 1 | /// Int extensions 2 | extension IntExt on int { 3 | /// Converts [int] into English ordinal representation 4 | /// 5 | /// ```dart 6 | /// print(1.ordinal); // 1st 7 | /// print(22.ordinal); // 22nd 8 | /// print(143.ordinal);// 143rd 9 | /// print(0.ordinal); // 0th 10 | /// print(12.ordinal); // 12th 11 | /// print(69.ordinal); // 69th 12 | /// ``` 13 | String get ordinal { 14 | if (this % 100 >= 11 && this % 100 <= 13) return '${this}th'; 15 | switch (this % 10) { 16 | case 1: 17 | return '${this}st'; 18 | case 2: 19 | return '${this}nd'; 20 | case 3: 21 | return '${this}rd'; 22 | default: 23 | return '${this}th'; 24 | } 25 | } 26 | 27 | /// Returns the Roman numeral representation of an integer from 1 to 3999 28 | /// 29 | /// ```dart 30 | /// print(12.roman); // XII 31 | /// print(455.roman); // CDLV 32 | /// print(1.roman); // I 33 | /// print(3999.roman); // MMMCMXCIX 34 | /// ``` 35 | String get roman { 36 | if (this < 1 || this > 3999) { 37 | throw ArgumentError('Number out of range (1 to 3999)'); 38 | } 39 | 40 | final romanMap = { 41 | 1000: 'M', 42 | 900: 'CM', 43 | 500: 'D', 44 | 400: 'CD', 45 | 100: 'C', 46 | 90: 'XC', 47 | 50: 'L', 48 | 40: 'XL', 49 | 10: 'X', 50 | 9: 'IX', 51 | 5: 'V', 52 | 4: 'IV', 53 | 1: 'I', 54 | }; 55 | 56 | var num = this; 57 | final buffer = StringBuffer(); 58 | 59 | for (final entry in romanMap.entries) { 60 | while (num >= entry.key) { 61 | buffer.write(entry.value); 62 | num -= entry.key; 63 | } 64 | } 65 | 66 | return buffer.toString(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/responsive/media_querys.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on [BuildContext] to provide easy access to common MediaQuery properties. 4 | /// 5 | /// This extension allows quick retrieval of screen width, height, aspect ratio, 6 | /// and view insets (such as keyboard height). 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// double screenWidth = context.mw; 11 | /// double screenHeight = context.mh; 12 | /// ``` 13 | /// 14 | /// Provided properties: 15 | /// - [mw]: Screen width. 16 | /// - [mh]: Screen height. 17 | /// - [ar]: Screen aspect ratio. 18 | /// - [vIb]: Bottom view inset (e.g., keyboard height). 19 | extension MediaQuerysValues on BuildContext { 20 | /// Returns the screen width. 21 | double get mw => MediaQuery.of(this).size.width; 22 | 23 | /// Returns the screen height. 24 | double get mh => MediaQuery.of(this).size.height; 25 | 26 | /// Returns the screen aspect ratio. 27 | double get ar => MediaQuery.of(this).size.aspectRatio; 28 | 29 | /// Returns the bottom view inset (e.g., keyboard height). 30 | double get vIb => MediaQuery.of(this).viewInsets.bottom; 31 | } 32 | -------------------------------------------------------------------------------- /lib/single_ton.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | /// A singleton utility class that provides commonly used constants and configurations. 5 | /// 6 | /// This class ensures a single instance throughout the application lifecycle. 7 | /// It includes logging configurations, UI-related properties, and utility values. 8 | class FlutterExKit { 9 | /// Private static variable holding the single instance of [FlutterExKit]. 10 | static final FlutterExKit _instance = FlutterExKit._internal(); 11 | 12 | /// Private constructor to prevent external instantiation. 13 | FlutterExKit._internal(); 14 | 15 | /// Factory constructor that returns the single instance of [FlutterExKit]. 16 | factory FlutterExKit() { 17 | return _instance; 18 | } 19 | 20 | /// A log emoji used for debugging messages. 21 | /// 22 | /// Example usage: 23 | /// ```dart 24 | /// print('${FlutterExKit().logEmoji} Debug message'); 25 | /// ``` 26 | String get logEmoji => '💢💢💢💢💢💢'; 27 | 28 | /// A secondary log emoji used for emphasizing logs. 29 | /// 30 | /// Example usage: 31 | /// ```dart 32 | /// print('${FlutterExKit().logEmojiDown} Error message'); 33 | /// ``` 34 | String get logEmojiDown => '💯💯💯💯💯💯'; 35 | 36 | /// Determines whether logging is enabled. 37 | /// 38 | /// Logs are enabled only in debug mode (`kDebugMode`). 39 | bool get logEnable => kDebugMode; 40 | 41 | /// The default color used for image loaders. 42 | /// 43 | /// The color is set to black (`0xff000000`). 44 | Color get imageLoader => const Color(0xff000000); 45 | } 46 | -------------------------------------------------------------------------------- /lib/string/string_ex.dart: -------------------------------------------------------------------------------- 1 | /// String extensions to add useful utility methods. 2 | extension StringExt on String { 3 | /// Capitalizes each word in the string. 4 | /// 5 | /// Example: 6 | /// ```dart 7 | /// print('hello world'.capitalize()); // Hello World 8 | /// ``` 9 | String capitalize() { 10 | return split(' ').map((e) => e.capitalizeFirst()).join(' '); 11 | } 12 | 13 | /// Capitalizes only the first letter of the string. 14 | /// 15 | /// Example: 16 | /// ```dart 17 | /// print('hello world'.capitalizeFirst()); // Hello world 18 | /// ``` 19 | String capitalizeFirst() { 20 | if (isEmpty) return ''; 21 | if (length == 1) return toUpperCase(); 22 | return substring(0, 1).toUpperCase() + substring(1); 23 | } 24 | 25 | /// Mocks a string that needs to be translated. 26 | /// 27 | /// Example: 28 | /// ```dart 29 | /// print('hello'.mock); // hello 🧨 30 | /// ``` 31 | String get mock => '$this 🧨'; 32 | 33 | /// Checks if the string represents a boolean value. 34 | /// 35 | /// If [caseSensitive] is `true` (default), it only accepts `"true"` and `"false"`. 36 | /// 37 | /// Example: 38 | /// ```dart 39 | /// print('true'.isBool()); // true 40 | /// print('false'.isBool()); // true 41 | /// print('TRUE'.isBool()); // false 42 | /// print('TRUE'.isBool(caseSensitive: false)); // true 43 | /// ``` 44 | bool isBool({bool caseSensitive = true}) { 45 | final lowerCaseValue = caseSensitive ? this : toLowerCase(); 46 | return lowerCaseValue == 'true' || lowerCaseValue == 'false'; 47 | } 48 | 49 | /// Converts the string into a boolean value. 50 | /// 51 | /// Throws an error if the string is not `"true"` or `"false"`. 52 | /// 53 | /// Example: 54 | /// ```dart 55 | /// print('true'.toBool()); // true 56 | /// print('false'.toBool()); // false 57 | /// ``` 58 | bool toBool({bool caseSensitive = true}) { 59 | if (!isBool(caseSensitive: caseSensitive)) { 60 | throw FormatException('Invalid boolean value: $this'); 61 | } 62 | return (caseSensitive ? this : toLowerCase()) == 'true'; 63 | } 64 | 65 | /// Checks if the string represents a numeric value. 66 | /// 67 | /// Example: 68 | /// ```dart 69 | /// print('2021'.isNum()); // true 70 | /// print('3.14'.isNum()); // true 71 | /// print('abc'.isNum()); // false 72 | /// ``` 73 | bool isNum() => num.tryParse(this) != null; 74 | 75 | /// Converts the string into a numeric value. 76 | /// 77 | /// Throws an error if the string is not a valid number. 78 | num toNum() => num.parse(this); 79 | 80 | /// Checks if the string represents a double value. 81 | /// 82 | /// Example: 83 | /// ```dart 84 | /// print('3.14'.isDouble()); // true 85 | /// print('10'.isDouble()); // true 86 | /// print('abc'.isDouble()); // false 87 | /// ``` 88 | bool isDouble() => double.tryParse(this) != null; 89 | 90 | /// Converts the string into a double. 91 | /// 92 | /// Throws an error if the string is not a valid double. 93 | double toDouble() => double.parse(this); 94 | 95 | /// Checks if the string represents an integer value. 96 | /// 97 | /// Example: 98 | /// ```dart 99 | /// print('10'.isInt()); // true 100 | /// print('3.14'.isInt()); // false 101 | /// ``` 102 | bool isInt() => int.tryParse(this) != null; 103 | 104 | /// Converts the string into an integer. 105 | /// 106 | /// Throws an error if the string is not a valid integer. 107 | int toInt() => int.parse(this); 108 | 109 | /// Removes all whitespace from the string. 110 | /// 111 | /// Example: 112 | /// ```dart 113 | /// print('hello world'.removeAllWhitespace()); // helloworld 114 | /// ``` 115 | String removeAllWhitespace() => replaceAll(RegExp(r'\s+'), ''); 116 | 117 | /// Checks whether the string matches a given regex [pattern]. 118 | /// 119 | /// Example: 120 | /// ```dart 121 | /// print('hello123'.hasMatch(r'\d+')); // true 122 | /// ``` 123 | bool hasMatch(String pattern) => RegExp(pattern).hasMatch(this); 124 | } 125 | -------------------------------------------------------------------------------- /lib/theme/color.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// An extension on the String class to convert hex color codes to Flutter's [Color] object. 4 | /// 5 | /// This extension allows you to easily convert a string representing a hex color code 6 | /// (with or without the '#' prefix) into a [Color] object that can be used in your Flutter UI. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// String hexColor = "#FF5733"; 11 | /// Color color = hexColor.toColor(); 12 | /// ``` 13 | /// 14 | /// If the hex code is invalid, the extension will return a default color (black by default). 15 | extension HexColorExtension on String { 16 | /// Converts the current string to a [Color] object. 17 | /// 18 | /// This method assumes the string is a hex color code, either with or without the '#' prefix. 19 | /// It will add full opacity ('FF') if the color code is 6 characters long. 20 | /// If the color code is invalid, it will return a default color. 21 | /// 22 | /// Returns: 23 | /// - A [Color] object representing the color. 24 | /// - A default color (black) if the hex code is invalid. 25 | Color toColor({Color defaultColor = Colors.black}) { 26 | try { 27 | // Remove leading '#' if present 28 | String hexString = replaceAll("#", ""); 29 | 30 | // If the hex code is 6 characters long, add 'FF' for full opacity 31 | if (hexString.length == 6) { 32 | hexString = "FF$hexString"; 33 | } 34 | 35 | // Check if the string is exactly 8 characters long (for ARGB hex code) 36 | if (hexString.length != 8) { 37 | throw const FormatException("Invalid hex color format"); 38 | } 39 | 40 | // Convert the hex string to an integer and create a Color object 41 | return Color(int.parse(hexString, radix: 16)); 42 | } catch (e) { 43 | // If any error occurs (invalid hex format), return the default color 44 | return defaultColor; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/ui/alignment_widget_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension methods for aligning a widget within a parent widget. 4 | /// 5 | /// This extension adds multiple alignment helper methods to the [Widget] class, making it easier 6 | /// to align widgets at specific positions within a parent container. 7 | /// 8 | /// These alignment methods use the [Align] widget and allow the widget to be positioned at the 9 | /// top-left, top-center, top-right, center, bottom-left, bottom-center, or bottom-right of its 10 | /// parent. Additionally, optional width and height parameters are available to control the size of 11 | /// the widget during alignment. 12 | extension AlignmentWidgetExtension on Widget { 13 | /// Aligns the widget to the top-left corner of its parent container. 14 | /// Optionally, width and height can be provided to constrain the widget's size. 15 | /// 16 | /// Example usage: 17 | /// ```dart 18 | /// Text('Top Left').alignTopLeft(width: 100, height: 50); 19 | /// ``` 20 | /// 21 | /// Parameters: 22 | /// - [width]: Optional width for the widget. 23 | /// - [height]: Optional height for the widget. 24 | Widget alignTopLeft({double? width, double? height}) { 25 | return Align( 26 | alignment: Alignment.topLeft, 27 | child: SizedBox( 28 | width: width, 29 | height: height, 30 | child: this, 31 | ), 32 | ); 33 | } 34 | 35 | /// Aligns the widget to the top-center of its parent container. 36 | /// Optionally, width and height can be provided to constrain the widget's size. 37 | /// 38 | /// Example usage: 39 | /// ```dart 40 | /// Text('Top Center').alignTopCenter(width: 200, height: 50); 41 | /// ``` 42 | /// 43 | /// Parameters: 44 | /// - [width]: Optional width for the widget. 45 | /// - [height]: Optional height for the widget. 46 | Widget alignTopCenter({double? width, double? height}) { 47 | return Align( 48 | alignment: Alignment.topCenter, 49 | child: SizedBox( 50 | width: width, 51 | height: height, 52 | child: this, 53 | ), 54 | ); 55 | } 56 | 57 | /// Aligns the widget to the top-right corner of its parent container. 58 | /// Optionally, width and height can be provided to constrain the widget's size. 59 | /// 60 | /// Example usage: 61 | /// ```dart 62 | /// Text('Top Right').alignTopRight(width: 100, height: 50); 63 | /// ``` 64 | /// 65 | /// Parameters: 66 | /// - [width]: Optional width for the widget. 67 | /// - [height]: Optional height for the widget. 68 | Widget alignTopRight({double? width, double? height}) { 69 | return Align( 70 | alignment: Alignment.topRight, 71 | child: SizedBox( 72 | width: width, 73 | height: height, 74 | child: this, 75 | ), 76 | ); 77 | } 78 | 79 | /// Aligns the widget to the center of its parent container. 80 | /// Optionally, width and height can be provided to constrain the widget's size. 81 | /// 82 | /// Example usage: 83 | /// ```dart 84 | /// Text('Center').alignCenter(width: 150, height: 50); 85 | /// ``` 86 | /// 87 | /// Parameters: 88 | /// - [width]: Optional width for the widget. 89 | /// - [height]: Optional height for the widget. 90 | Widget alignCenter({double? width, double? height}) { 91 | return Align( 92 | alignment: Alignment.center, 93 | child: SizedBox( 94 | width: width, 95 | height: height, 96 | child: this, 97 | ), 98 | ); 99 | } 100 | 101 | /// Aligns the widget to the bottom-left corner of its parent container. 102 | /// Optionally, width and height can be provided to constrain the widget's size. 103 | /// 104 | /// Example usage: 105 | /// ```dart 106 | /// Text('Bottom Left').alignBottomLeft(width: 100, height: 50); 107 | /// ``` 108 | /// 109 | /// Parameters: 110 | /// - [width]: Optional width for the widget. 111 | /// - [height]: Optional height for the widget. 112 | Widget alignBottomLeft({double? width, double? height}) { 113 | return Align( 114 | alignment: Alignment.bottomLeft, 115 | child: SizedBox( 116 | width: width, 117 | height: height, 118 | child: this, 119 | ), 120 | ); 121 | } 122 | 123 | /// Aligns the widget to the bottom-center of its parent container. 124 | /// Optionally, width and height can be provided to constrain the widget's size. 125 | /// 126 | /// Example usage: 127 | /// ```dart 128 | /// Text('Bottom Center').alignBottomCenter(width: 150, height: 50); 129 | /// ``` 130 | /// 131 | /// Parameters: 132 | /// - [width]: Optional width for the widget. 133 | /// - [height]: Optional height for the widget. 134 | Widget alignBottomCenter({double? width, double? height}) { 135 | return Align( 136 | alignment: Alignment.bottomCenter, 137 | child: SizedBox( 138 | width: width, 139 | height: height, 140 | child: this, 141 | ), 142 | ); 143 | } 144 | 145 | /// Aligns the widget to the bottom-right corner of its parent container. 146 | /// Optionally, width and height can be provided to constrain the widget's size. 147 | /// 148 | /// Example usage: 149 | /// ```dart 150 | /// Text('Bottom Right').alignBottomRight(width: 100, height: 50); 151 | /// ``` 152 | /// 153 | /// Parameters: 154 | /// - [width]: Optional width for the widget. 155 | /// - [height]: Optional height for the widget. 156 | Widget alignBottomRight({double? width, double? height}) { 157 | return Align( 158 | alignment: Alignment.bottomRight, 159 | child: SizedBox( 160 | width: width, 161 | height: height, 162 | child: this, 163 | ), 164 | ); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /lib/ui/center.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on [Widget] to provide a convenience method for centering widgets. 4 | /// 5 | /// This extension allows any widget to be easily wrapped in a [Center] widget, 6 | /// making it simple to align elements centrally in a layout. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// Text('Hello').center(); 11 | /// ``` 12 | /// 13 | /// Parameters: 14 | /// - [heightFactor]: Multiplier for the child's height (optional). 15 | /// - [widthFactor]: Multiplier for the child's width (optional). 16 | /// - [key]: An optional key for the [Center] widget. 17 | extension CenteredWidget on Widget { 18 | /// Wraps the widget with a [Center] widget. 19 | /// 20 | /// Allows for optional height and width factors to control the scaling of the centered child. 21 | /// 22 | /// Example: 23 | /// ```dart 24 | /// Container(width: 100, height: 100, color: Colors.red).center(); 25 | /// ``` 26 | Widget center({Key? key, double? heightFactor, double? widthFactor}) { 27 | return Center( 28 | key: key, 29 | heightFactor: heightFactor, 30 | widthFactor: widthFactor, 31 | child: this, 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/ui/network_image.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// An extension on [String] to easily load network images with proper handling. 4 | /// 5 | /// This extension provides a method to load network images with built-in 6 | /// loading indicators, error handling, and optional support for SVG images. 7 | extension NetworkImageExt on String? { 8 | /// Loads a network image with a progress indicator and error handling. 9 | /// 10 | /// If the string is a valid image URL, it loads the image using [Image.network]. 11 | /// It also shows a circular progress indicator while loading and an error widget 12 | /// in case of failure. 13 | /// 14 | /// If the string is an SVG image (determined by its `.svg` extension), 15 | /// it returns the provided [svgImage] widget. 16 | /// 17 | /// If the string is null or not a valid image URL, it returns an empty [SizedBox]. 18 | /// 19 | /// ### Parameters: 20 | /// - [fit]: Specifies how the image should be inscribed into the space. Default is [BoxFit.contain]. 21 | /// - [errorWidget]: The widget to show if the image fails to load. 22 | /// - [progressIndicatorSize]: The stroke width of the progress indicator. Default is `4.0`. 23 | /// - [height]: The height of the image. 24 | /// - [width]: The width of the image. 25 | /// - [indicatorHeight]: The height of the loading indicator. 26 | /// - [indicatorWidth]: The width of the loading indicator. 27 | /// - [svgImage]: A widget to display if the image is an SVG format. 28 | /// 29 | /// ### Returns: 30 | /// A [Widget] that displays the network image, an error widget, or an SVG image. 31 | Widget networkImage({ 32 | BoxFit fit = BoxFit.contain, 33 | Widget? errorWidget, 34 | double? progressIndicatorSize, 35 | double? height, 36 | double? width, 37 | double? indicatorHeight, 38 | double? indicatorWidth, 39 | Widget? svgImage, 40 | }) { 41 | FlutterExKit exKit = FlutterExKit(); 42 | 43 | if (this != null && this?.isNotEmpty == true && allImages(this ?? '')) { 44 | return Image.network( 45 | this ?? '', 46 | fit: fit, 47 | height: height, 48 | width: width, 49 | loadingBuilder: (context, child, loadingProgress) { 50 | if (loadingProgress == null) return child; 51 | return Center( 52 | child: SizedBox( 53 | height: indicatorHeight, 54 | width: indicatorWidth, 55 | child: CircularProgressIndicator( 56 | value: loadingProgress.expectedTotalBytes != null 57 | ? loadingProgress.cumulativeBytesLoaded / 58 | loadingProgress.expectedTotalBytes! 59 | : null, 60 | strokeWidth: progressIndicatorSize ?? 4.0, 61 | color: exKit.imageLoader, 62 | ), 63 | ), 64 | ); 65 | }, 66 | errorBuilder: (context, error, stackTrace) { 67 | return SizedBox( 68 | height: indicatorHeight, 69 | width: indicatorWidth, 70 | child: errorWidget ?? 71 | Icon( 72 | Icons.broken_image, 73 | color: exKit.imageLoader, 74 | ), 75 | ); 76 | }, 77 | ); 78 | } else if (this != null && 79 | this?.isNotEmpty == true && 80 | this!.endsWith('.${FileTypeForImageEnum.svg}')) { 81 | return svgImage ?? const SizedBox.shrink(); 82 | } 83 | 84 | return const SizedBox.shrink(); 85 | } 86 | } 87 | 88 | /// Checks if a given string represents a valid image file format. 89 | /// 90 | /// This function verifies if the file name ends with one of the supported image formats 91 | /// (PNG, JPG, JPEG, WEBP, GIF). 92 | /// 93 | /// ### Parameters: 94 | /// - [image]: The file name or URL to be checked. 95 | /// 96 | /// ### Returns: 97 | /// `true` if the file is an image format, otherwise `false`. 98 | bool allImages(String image) => 99 | image.endsWith('.${FileTypeForImageEnum.png}') || 100 | image.endsWith('.${FileTypeForImageEnum.jpg}') || 101 | image.endsWith('.${FileTypeForImageEnum.jpeg}') || 102 | image.endsWith('.${FileTypeForImageEnum.webp}') || 103 | image.endsWith('.${FileTypeForImageEnum.gif}'); 104 | -------------------------------------------------------------------------------- /lib/ui/num_extensions.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on the [num] type to provide convenience methods for creating [SizedBox] widgets. 4 | /// 5 | /// This extension adds methods to the [num] type, allowing you to easily create [SizedBox] widgets 6 | /// with specified height or width. This is particularly useful for creating spacing in your Flutter layouts. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// // Create a SizedBox with a height of 20.0 11 | /// 20.height; 12 | /// 13 | /// // Create a SizedBox with a width of 30.0 14 | /// 30.width; 15 | /// ``` 16 | /// 17 | /// The extension methods provided are: 18 | /// - [height]: Creates a [SizedBox] with the specified height. 19 | /// - [width]: Creates a [SizedBox] with the specified width. 20 | /// 21 | /// These methods are useful for creating padding and spacing in your Flutter widgets without needing to 22 | /// instantiate [SizedBox] manually each time. 23 | extension Pad on num { 24 | /// Creates a [SizedBox] with the specified height. 25 | /// 26 | /// This method converts the [num] value to a [double] and uses it to set the height of the [SizedBox]. 27 | /// 28 | /// Example: 29 | /// ```dart 30 | /// 40.height; // Creates a SizedBox with a height of 40.0 31 | /// ``` 32 | SizedBox get height => SizedBox( 33 | height: toDouble(), 34 | ); 35 | 36 | /// Creates a [SizedBox] with the specified width. 37 | /// 38 | /// This method converts the [num] value to a [double] and uses it to set the width of the [SizedBox]. 39 | /// 40 | /// Example: 41 | /// ```dart 42 | /// 50.width; // Creates a SizedBox with a width of 50.0 43 | /// ``` 44 | SizedBox get width => SizedBox( 45 | width: toDouble(), 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /lib/ui/overscroll_off_extension.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// A widget that prevents the overscroll indicator from appearing. 4 | /// 5 | /// This widget listens for `OverscrollIndicatorNotification` and disables the overscroll 6 | /// indicator when it is triggered. It is useful for preventing the visual feedback of 7 | /// overscrolling in scrollable widgets. 8 | /// 9 | /// Example usage: 10 | /// ```dart 11 | /// SingleChildScrollView( 12 | /// child: _OverScrollOff( 13 | /// child: YourWidget(), 14 | /// ), 15 | /// ); 16 | /// ``` 17 | /// 18 | /// Alternatively, use the extension method on [SingleChildScrollView]: 19 | /// ```dart 20 | /// SingleChildScrollView( 21 | /// child: YourWidget(), 22 | /// ).overScrollOff(); 23 | /// ``` 24 | class _OverScrollOff extends StatelessWidget { 25 | /// Creates an instance of [_OverScrollOff]. 26 | /// 27 | /// [child] is the widget that will be wrapped and will not show the overscroll indicator. 28 | const _OverScrollOff({required this.child}); 29 | 30 | /// The widget that will be wrapped by [_OverScrollOff]. 31 | final Widget child; 32 | 33 | @override 34 | Widget build(BuildContext context) => 35 | NotificationListener( 36 | onNotification: (overscroll) { 37 | // Disable the overscroll indicator. 38 | overscroll.disallowIndicator(); 39 | return true; 40 | }, 41 | child: child, 42 | ); 43 | } 44 | 45 | /// Extension on [SingleChildScrollView] to prevent the overscroll indicator from appearing. 46 | /// 47 | /// This extension provides a convenient method to wrap a [SingleChildScrollView] in an 48 | /// [_OverScrollOff] widget, which disables the overscroll indicator. 49 | /// 50 | /// Example usage: 51 | /// ```dart 52 | /// SingleChildScrollView( 53 | /// child: YourWidget(), 54 | /// ).overScrollOff(); 55 | /// ``` 56 | /// 57 | /// Returns: 58 | /// A widget that prevents the overscroll indicator from being shown. 59 | extension OverScrollOffExtension on Widget { 60 | /// Wraps the [SingleChildScrollView] with an [_OverScrollOff] widget to disable the overscroll indicator. 61 | /// 62 | /// This method provides a convenient way to apply the overscroll prevention behavior 63 | /// to a [SingleChildScrollView] without manually wrapping it in an [_OverScrollOff]. 64 | Widget overScrollOff() { 65 | return _OverScrollOff(child: this); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/ui/padding.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on [Widget] to provide convenient padding methods. 4 | /// 5 | /// This extension simplifies the process of adding padding to widgets, 6 | /// allowing for cleaner and more readable code. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// Text('Hello').all(16.0); 11 | /// Container().symmetric(vertical: 8.0, horizontal: 16.0); 12 | /// Icon(Icons.star).only(left: 8.0, top: 4.0); 13 | /// ``` 14 | /// 15 | /// Methods: 16 | /// - [all]: Applies uniform padding on all sides. 17 | /// - [symmetric]: Applies padding symmetrically on vertical and horizontal axes. 18 | /// - [only]: Applies padding to specific sides. 19 | extension PaddingExtension on Widget { 20 | /// Applies uniform padding on all sides. 21 | Widget all(double value) { 22 | return Padding( 23 | padding: EdgeInsets.all(value), 24 | child: this, 25 | ); 26 | } 27 | 28 | /// Applies padding symmetrically along vertical and horizontal axes. 29 | Widget symmetric({double vertical = 0.0, double horizontal = 0.0}) { 30 | return Padding( 31 | padding: EdgeInsets.symmetric(vertical: vertical, horizontal: horizontal), 32 | child: this, 33 | ); 34 | } 35 | 36 | /// Applies padding to specific sides. 37 | Widget only({ 38 | double left = 0.0, 39 | double top = 0.0, 40 | double right = 0.0, 41 | double bottom = 0.0, 42 | }) { 43 | return Padding( 44 | padding: 45 | EdgeInsets.only(left: left, top: top, right: right, bottom: bottom), 46 | child: this, 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/ui/scaffold_extensions.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on the [Scaffold] widget to add a method for customizing the status bar and navigation bar colors. 4 | /// 5 | /// This extension allows you to easily modify the appearance of the status bar and system navigation bar when using a [Scaffold]. 6 | /// It also provides options to adjust the brightness of the icons and enforce contrast settings for better visibility. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// Scaffold( 11 | /// body: Center(child: Text('Hello, World!')), 12 | /// ).withStatusBarColor( 13 | /// statusBarColor: Colors.blue, // Background color of the status bar 14 | /// systemNavigationBarColor: Colors.black, // Background color of the system navigation bar 15 | /// statusBarIconBrightness: Brightness.light, // Brightness of the status bar icons 16 | /// ); 17 | /// ``` 18 | /// 19 | /// Parameters: 20 | /// - [statusBarColor]: The color of the status bar background. This parameter is required. 21 | /// - [systemNavigationBarColor]: The color of the system navigation bar background. Defaults to [Colors.white]. 22 | /// - [statusBarIconBrightness]: The brightness of the status bar icons. Defaults to [Brightness.light]. 23 | /// 24 | /// Returns: 25 | /// - A [Widget] wrapped in an [AnnotatedRegion] that applies the specified [SystemUiOverlayStyle] settings to the status bar and navigation bar. 26 | /// 27 | /// The [SystemUiOverlayStyle] allows for customization of system UI elements like the status bar and navigation bar. 28 | /// By using this extension, you can easily apply consistent styling to these areas across your app. 29 | extension ScaffoldExtensions on Scaffold { 30 | /// Wraps the [Scaffold] widget with an [AnnotatedRegion] that applies the specified system UI overlay style settings. 31 | /// 32 | /// This method customizes the status bar and navigation bar appearance by setting their background colors, 33 | /// icon brightness, and contrast enforcement. 34 | /// 35 | /// Example: 36 | /// ```dart 37 | /// Scaffold( 38 | /// body: Center(child: Text('Custom StatusBar and NavigationBar')), 39 | /// ).withStatusBarColor( 40 | /// statusBarColor: Colors.teal, 41 | /// systemNavigationBarColor: Colors.blueGrey, 42 | /// statusBarIconBrightness: Brightness.dark, 43 | /// ); 44 | /// ``` 45 | /// 46 | /// [statusBarColor] - The color to use for the status bar background. This is a required parameter. 47 | /// [systemNavigationBarColor] - The color to use for the system navigation bar background. Defaults to [Colors.white]. 48 | /// [statusBarIconBrightness] - The brightness level of the status bar icons. Defaults to [Brightness.light]. 49 | /// 50 | /// Returns a [Widget] that includes the specified [SystemUiOverlayStyle] settings for the status bar and navigation bar. 51 | Widget withStatusBarColor({ 52 | required Color statusBarColor, 53 | Color systemNavigationBarColor = Colors.white, 54 | Brightness statusBarIconBrightness = Brightness.light, 55 | Color? systemNavigationBarDividerColor, 56 | bool sized = true, 57 | }) { 58 | return AnnotatedRegion( 59 | value: SystemUiOverlayStyle.light.copyWith( 60 | statusBarColor: statusBarColor, 61 | statusBarIconBrightness: statusBarIconBrightness, 62 | systemNavigationBarColor: systemNavigationBarColor, 63 | statusBarBrightness: Brightness.light, 64 | systemStatusBarContrastEnforced: true, 65 | systemNavigationBarContrastEnforced: true, 66 | systemNavigationBarIconBrightness: Brightness.dark, 67 | systemNavigationBarDividerColor: systemNavigationBarDividerColor), 68 | sized: true, 69 | child: this, 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/ui/shrink.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on [Widget] to provide convenient methods for handling nullable widgets. 4 | /// 5 | /// This extension ensures that a nullable widget does not cause layout issues by returning a [SizedBox.shrink()] 6 | /// when the widget is `null`. It also includes additional methods for spacing and visibility handling. 7 | extension WidgetExtensions on Widget? { 8 | /// Returns the widget if it's not null; otherwise, returns an empty [SizedBox.shrink()]. 9 | Widget shrink() => this ?? const SizedBox.shrink(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/ui/string_text_extensions.dart: -------------------------------------------------------------------------------- 1 | part of '../flutter_ex_kit.dart'; 2 | 3 | /// Extension on the `String` class to provide a convenient method for creating styled [Text] widgets. 4 | /// 5 | /// This extension allows any [String] to be easily converted into a [Text] widget with various styling options. 6 | /// You can customize text appearance such as color, font size, weight, decoration, alignment, and more. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// 'Hello, World!'.style( 11 | /// color: Colors.blue, 12 | /// fontSize: 24.0, 13 | /// fontWeight: FontWeight.bold, 14 | /// textAlign: TextAlign.center, 15 | /// ); 16 | /// ``` 17 | extension StringTextExtensions on String { 18 | /// Converts the string into a styled [Text] widget. 19 | /// 20 | /// This method provides a flexible way to style text directly from a [String]. 21 | /// It supports a wide range of text styling properties available in [TextStyle]. 22 | /// 23 | /// ### Parameters: 24 | /// - [key]: An optional key to uniquely identify this widget. 25 | /// - [color]: The color of the text. 26 | /// - [fontSize]: The size of the font. 27 | /// - [fontWeight]: The weight of the font (e.g., bold). 28 | /// - [fontFamily]: The font family for the text; if not provided, defaults to [globalFontFamily]. 29 | /// - [fontFamilyFallback]: A list of fallback font families. 30 | /// - [fontStyle]: The style of the font (normal, italic). 31 | /// - [letterSpacing]: The space between letters. 32 | /// - [wordSpacing]: The space between words. 33 | /// - [height]: The line height of the text. 34 | /// - [textAlign]: The alignment of the text (left, center, right, etc.). 35 | /// - [textDirection]: The direction of the text (LTR/RTL). 36 | /// - [maxLines]: The maximum number of lines the text can span. 37 | /// - [overflow]: Defines how the text should handle overflow. 38 | /// - [textScaler]: Controls the scaling behavior of the text. 39 | /// - [decoration]: The type of text decoration (underline, line-through, etc.). 40 | /// - [decorationColor]: The color of the decoration. 41 | /// - [decorationThickness]: The thickness of the decoration. 42 | /// - [decorationStyle]: The style of the decoration (dashed, dotted, etc.). 43 | /// - [backgroundColor]: The background color of the text. 44 | /// - [foreground]: A paint object that replaces the text color. 45 | /// - [background]: A paint object that replaces the background color. 46 | /// - [shadows]: A list of shadows for the text. 47 | /// - [textBaseline]: The baseline alignment of the text. 48 | /// - [strutStyle]: Defines the minimum line height of the text. 49 | /// - [textHeightBehavior]: Defines how the height of the text behaves. 50 | /// - [textWidthBasis]: Defines whether the width is based on the longest or tightest line. 51 | /// - [semanticsLabel]: A label for accessibility purposes. 52 | /// - [softWrap]: Defines whether the text should break at soft line breaks. 53 | /// - [selectionColor]: The color used when the text is selected. 54 | /// - [leadingDistribution]: Defines how extra leading space is distributed. 55 | /// - [inherit]: Whether to inherit styles from the parent text theme. 56 | /// - [locale]: The locale used for text rendering. 57 | /// - [debugLabel]: Debugging label for developers. 58 | /// - [package]: The name of the package from which the font is loaded. 59 | /// - [fontVariations]: Specifies font variations. 60 | /// - [fontFeatures]: Defines OpenType font features. 61 | Widget style({ 62 | Key? key, 63 | Color? color, 64 | double? fontSize, 65 | FontWeight? fontWeight, 66 | String? fontFamily, 67 | List? fontFamilyFallback, 68 | FontStyle? fontStyle, 69 | double? letterSpacing, 70 | double? wordSpacing, 71 | double? height, 72 | TextAlign? textAlign, 73 | TextDirection? textDirection, 74 | int? maxLines, 75 | TextOverflow? overflow, 76 | TextScaler? textScaler, 77 | TextDecoration? decoration, 78 | Color? decorationColor, 79 | double? decorationThickness, 80 | TextDecorationStyle? decorationStyle, 81 | Color? backgroundColor, 82 | Paint? foreground, 83 | Paint? background, 84 | List? shadows, 85 | TextBaseline? textBaseline, 86 | StrutStyle? strutStyle, 87 | TextHeightBehavior? textHeightBehavior, 88 | TextWidthBasis? textWidthBasis, 89 | String? semanticsLabel, 90 | bool? softWrap, 91 | List? fontVariations, 92 | Locale? locale, 93 | String? debugLabel, 94 | String? package, 95 | List? fontFeatures, 96 | Color? selectionColor, 97 | bool inherit = false, 98 | TextLeadingDistribution? leadingDistribution, 99 | }) => 100 | Text( 101 | this, 102 | key: key, 103 | style: TextStyle( 104 | color: color, 105 | fontSize: fontSize, 106 | fontWeight: fontWeight, 107 | fontFamily: fontFamily, 108 | fontFamilyFallback: fontFamilyFallback, 109 | fontStyle: fontStyle, 110 | letterSpacing: letterSpacing, 111 | wordSpacing: wordSpacing, 112 | height: height, 113 | decoration: decoration, 114 | decorationColor: decorationColor, 115 | decorationThickness: decorationThickness, 116 | decorationStyle: decorationStyle, 117 | backgroundColor: backgroundColor, 118 | foreground: foreground, 119 | background: background, 120 | shadows: shadows, 121 | textBaseline: textBaseline, 122 | leadingDistribution: leadingDistribution, 123 | debugLabel: debugLabel, 124 | inherit: inherit, 125 | locale: locale, 126 | fontVariations: fontVariations, 127 | fontFeatures: fontFeatures, 128 | overflow: overflow, 129 | package: package, 130 | ), 131 | textAlign: textAlign, 132 | textDirection: textDirection, 133 | maxLines: maxLines, 134 | overflow: overflow, 135 | textScaler: textScaler, 136 | strutStyle: strutStyle, 137 | textHeightBehavior: textHeightBehavior, 138 | textWidthBasis: textWidthBasis, 139 | semanticsLabel: semanticsLabel, 140 | softWrap: softWrap, 141 | locale: locale, 142 | selectionColor: selectionColor, 143 | ); 144 | } 145 | -------------------------------------------------------------------------------- /lib/validation/input_format_validation.dart: -------------------------------------------------------------------------------- 1 | /// A mixin that provides input format validation utilities. 2 | /// This includes regular expressions to restrict certain characters, 3 | /// such as emojis, from user input. 4 | mixin InputFormatValidation { 5 | /// Regular expression to disallow emojis in user input. 6 | /// 7 | /// This regex pattern aims to identify and restrict any emoji characters 8 | /// from being entered into a text field. It covers a broad range of emojis, 9 | /// including skin tone variations and complex emoji sequences. 10 | static var emojiNoAllowRegExp = RegExp( 11 | '/\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67)\uDB40\uDC7F|(?:\uD83E\uDDD1\uD83C\uDFFF\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?|\u200D(?:\uD83D\uDC8B\u200D)?)\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69])|\uD83E\uDEF1\uD83C\uDFFF\u200D\uD83E\uDEF2)(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?|\u200D(?:\uD83D\uDC8B\u200D)?)\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69])|\uD83E\uDEF1\uD83C\uDFFE\u200D\uD83E\uDEF2)(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?|\u200D(?:\uD83D\uDC8B\u200D)?)\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69])|\uD83E\uDEF1\uD83C\uDFFD\u200D\uD83E\uDEF2)(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?|\u200D(?:\uD83D\uDC8B\u200D)?)\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69])|\uD83E\uDEF1\uD83C\uDFFC\u200D\uD83E\uDEF2)(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?|\u200D(?:\uD83D\uDC8B\u200D)?)\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69])|\uD83E\uDEF1\uD83C\uDFFB\u200D\uD83E\uDEF2)(?:\uD83C[\uDFFC-\uDFFF])|\uD83D\uDC68(?:\uD83C\uDFFB(?:\u200D(?:\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])))|\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|[\u2695\u2696\u2708]\uFE0F|[\u2695\u2696\u2708]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF]))|\u200D(?:\uD83D\uDC8B\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFF])))|\u200D(?:\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?|\u200D(?:\uD83D\uDC8B\u200D)?)\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])\uFE0F|\u200D(?:(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D[\uDC66\uDC67])|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\u200D[\u2695\u2696\u2708])?|(?:\uD83D\uDC69(?:\uD83C\uDFFB\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|(?:\uD83C[\uDFFC-\uDFFF])\u200D\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])))|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC69(?:\u200D(?:\u2764(?:\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69]))|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83E\uDDD1(?:\u200D(?:\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F?\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F?\u200D\u26A7|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3C-\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3])\uFE0F|\uD83D\uDC41\uFE0F?\u200D\uD83D\uDDE8|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|\uD83C\uDFF3\uFE0F?\u200D\uD83C\uDF08|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDE36\u200D\uD83C\uDF2B|\uD83C\uDFF3\uFE0F?\u200D\u26A7|\uD83D\uDE35\u200D\uD83D\uDCAB|\uD83D\uDE2E\u200D\uD83D\uDCA8|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83E\uDEF1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83E\uDDD1(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC69(?:\uD83C\uDFFF|\uD83C\uDFFE|\uD83C\uDFFD|\uD83C\uDFFC|\uD83C\uDFFB)?|\uD83D\uDC3B\u200D\u2744|(?:(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|\uD83D\uDC6F|\uD83E[\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF6\uD83C\uDDE6|\uD83C\uDDF4\uD83C\uDDF2|\uD83D\uDC08\u200D\u2B1B|\u2764(?:\uFE0F\u200D(?:\uD83D\uDD25|\uD83E\uDE79)|\u200D(?:\uD83D\uDD25|\uD83E\uDE79))|\uD83D\uDC41\uFE0F?|\uD83C\uDFF3\uFE0F?|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3C-\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])\u200D[\u2640\u2642]|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|[#\*0-9]\uFE0F?\u20E3|\uD83E\uDD3C(?:\uD83C[\uDFFB-\uDFFF])|\u2764\uFE0F?|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|\uD83C\uDFF4|(?:[\u270A\u270B]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF6])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270C\u270D]|\uD83D[\uDD74\uDD90])(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])|[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC08\uDC15\uDC3B\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE2E\uDE35\uDE36\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD3C\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF6]|\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD]|\uD83D\uDC6F|\uD83E[\uDDDE\uDDDF]|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]|\uD83D[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\uD83C[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDD-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7C\uDE80-\uDE86\uDE90-\uDEAC\uDEB0-\uDEBA\uDEC0-\uDEC2\uDED0-\uDED9\uDEE0-\uDEE7]'); 12 | 13 | /// Regular expression to allow only numbers (digits 0-9). 14 | static var onlyNumberAllow = RegExp(r'^[0-9]+$'); 15 | 16 | /// Regular expression to allow only text (alphabets a-z, A-Z). 17 | static var onlyTextAllow = RegExp(r'^[a-zA-Z]+$'); 18 | 19 | /// Regular expression to validate an email address. 20 | static var onlyEmailAllow = 21 | RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'); 22 | } 23 | -------------------------------------------------------------------------------- /lib/validation/single_space_formatter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | /// A custom [TextInputFormatter] that prevents consecutive spaces 4 | /// and ensures that a single space is only allowed between words. 5 | /// 6 | /// This formatter helps maintain clean and structured user input 7 | /// by restricting multiple consecutive spaces. 8 | class SingleSpaceFormatter extends TextInputFormatter { 9 | /// Tracks whether the last entered character was a space. 10 | /// 11 | /// This prevents multiple consecutive spaces in the text input. 12 | bool _isPreviousSpace = false; 13 | 14 | /// Formats the text input by allowing only a single space between words. 15 | /// 16 | /// - If the new input contains only a space, the previous value is retained. 17 | /// - If a space is entered consecutively, it is ignored. 18 | /// - Otherwise, the input is updated normally. 19 | /// 20 | /// **Parameters:** 21 | /// - [oldValue]: The previous text input value. 22 | /// - [newValue]: The updated text input value. 23 | /// 24 | /// **Returns:** 25 | /// A [TextEditingValue] that ensures only single spaces are allowed. 26 | @override 27 | TextEditingValue formatEditUpdate( 28 | TextEditingValue oldValue, TextEditingValue newValue) { 29 | if (newValue.text == ' ') { 30 | // If only a space is entered, keep the previous value 31 | return oldValue; 32 | } 33 | 34 | // Allow a single space only if not preceded by another space 35 | if (newValue.text == ' ' && _isPreviousSpace) { 36 | return TextEditingValue( 37 | text: oldValue.text, 38 | selection: TextSelection.collapsed(offset: oldValue.text.length), 39 | ); 40 | } 41 | 42 | // Update the flag based on the last character 43 | _isPreviousSpace = newValue.text.endsWith(' '); 44 | return newValue; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_ex_kit", 3 | "version": "0.0.6", 4 | "description": "`flutter_ex_kit` is a Flutter utility library that provides a collection of custom widgets, extensions, animations, and utility functions. It is designed to simplify common tasks and improve the developer experience while building Flutter applications. Whether you're looking for easy-to-use animations, advanced navigation features, or helpful logging tools, `flutter_ex_kit` has got you covered.", 5 | "author": "", 6 | "license": "ISC", 7 | "flutter": { 8 | "sdk": "flutter" 9 | } 10 | } -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_ex_kit 2 | description: "Ex Kit Flutter is a powerful and easy-to-use collection of extensions and utility functions designed to enhance your Flutter development experience." 3 | version: 0.0.6 4 | repository: https://github.com/iamapuneet/flutter_ex_kit.git 5 | issue_tracker: https://github.com/iamapuneet/flutter_ex_kit/issues 6 | homepage: https://github.com/iamapuneet/flutter_ex_kit?tab=readme-ov-file#flutter_ex_kit 7 | documentation: https://github.com/iamapuneet/flutter_ex_kit.wiki.git 8 | topics: [flutter, extensions, utilities, development] 9 | #funding: [https://github.com/sponsors/realpuneetsharam] 10 | 11 | environment: 12 | sdk: ">=3.5.3 <4.0.0" 13 | flutter: ">=1.17.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | screenshots: 24 | - description: The flutter ex kit package logo. 25 | path: screenshots/logo.png -------------------------------------------------------------------------------- /screenshots/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamapuneet/flutter_ex_kit/61c117aa192dfe64b1aba3c1818ecfccfd138264/screenshots/logo.png -------------------------------------------------------------------------------- /test/flutter_ex_kit_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_ex_kit/flutter_ex_kit.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | // The actual function to perform the addition 6 | int add(int b) { 7 | return 1 + b; 8 | } 9 | 10 | // Test block 11 | test('Addition of 1 + b', () { 12 | // Arrange 13 | int b = 2; 14 | 15 | // Act 16 | int result = add(b); 17 | 18 | // Assert 19 | expect(result, 3); // Ensure that 1 + 2 equals 3 20 | }); 21 | 22 | // Optional: Run the function and print the result 23 | output(add(2)); // This will print 3 in the console 24 | } 25 | --------------------------------------------------------------------------------