├── .DS_Store ├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── copyright │ ├── Apache.xml │ └── profiles_settings.xml ├── libraries │ └── Dart_SDK.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── .metadata ├── LICENSE ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── rallycoder │ │ │ │ │ └── tests │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.yaml └── test │ └── main.dart ├── flutter_extentions.iml ├── lib ├── dart_extensions.dart ├── emum.dart └── src │ ├── data_stractures │ └── stack.dart │ ├── date.dart │ ├── equality.dart │ ├── exceptions │ └── range_exception.dart │ ├── files.dart │ ├── flutter │ ├── align.dart │ ├── center.dart │ ├── container.dart │ ├── context.dart │ ├── duration.dart │ ├── gesture_detector.dart │ ├── icon.dart │ ├── list.dart │ ├── navigation.dart │ ├── padding.dart │ ├── responsive.dart │ ├── sized_box.dart │ ├── sliver.dart │ ├── text.dart │ ├── transforms.dart │ ├── transforms │ │ └── click_translate.dart │ └── widgets.dart │ ├── http.dart │ ├── int.dart │ ├── iterable.dart │ ├── map.dart │ ├── model │ └── user.dart │ ├── ranges.dart │ ├── search_algorithms.dart │ ├── sort_algorithms.dart │ ├── string_ext.dart │ └── utils.dart ├── pubspec.yaml └── test ├── algo_test.dart ├── date_tests.dart ├── enum_test.dart ├── int_test.dart ├── iterable_tests.dart ├── ranges_test.dart └── strings_test.dart /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/mingit/ 28 | /dev/benchmarks/mega_gallery/ 29 | /dev/bots/.recipe_deps 30 | /dev/bots/android_tools/ 31 | /dev/docs/doc/ 32 | /dev/docs/flutter.docs.zip 33 | /dev/docs/lib/ 34 | /dev/docs/pubspec.yaml 35 | /dev/integration_tests/**/xcuserdata 36 | /dev/integration_tests/**/Pods 37 | /packages/flutter/coverage/ 38 | version 39 | 40 | # packages file containing multi-root paths 41 | .packages.generated 42 | 43 | # Flutter/Dart/Pub related 44 | **/doc/api/ 45 | .dart_tool/ 46 | .flutter-plugins 47 | .flutter-plugins-dependencies 48 | .packages 49 | .pub-cache/ 50 | .pub/ 51 | build/ 52 | flutter_*.png 53 | linked_*.ds 54 | unlinked.ds 55 | unlinked_spec.ds 56 | 57 | # Android related 58 | **/android/**/gradle-wrapper.jar 59 | **/android/.gradle 60 | **/android/captures/ 61 | **/android/gradlew 62 | **/android/gradlew.bat 63 | **/android/local.properties 64 | **/android/**/GeneratedPluginRegistrant.java 65 | **/android/key.properties 66 | *.jks 67 | 68 | # iOS/XCode related 69 | **/ios/**/*.mode1v3 70 | **/ios/**/*.mode2v3 71 | **/ios/**/*.moved-aside 72 | **/ios/**/*.pbxuser 73 | **/ios/**/*.perspectivev3 74 | **/ios/**/*sync/ 75 | **/ios/**/.sconsign.dblite 76 | **/ios/**/.tags* 77 | **/ios/**/.vagrant/ 78 | **/ios/**/DerivedData/ 79 | **/ios/**/Icon? 80 | **/ios/**/Pods/ 81 | **/ios/**/.symlinks/ 82 | **/ios/**/profile 83 | **/ios/**/xcuserdata 84 | **/ios/.generated/ 85 | **/ios/Flutter/App.framework 86 | **/ios/Flutter/Flutter.framework 87 | **/ios/Flutter/Flutter.podspec 88 | **/ios/Flutter/Generated.xcconfig 89 | **/ios/Flutter/app.flx 90 | **/ios/Flutter/app.zip 91 | **/ios/Flutter/flutter_assets/ 92 | **/ios/Flutter/flutter_export_environment.sh 93 | **/ios/ServiceDefinitions.json 94 | **/ios/Runner/GeneratedPluginRegistrant.* 95 | 96 | # macOS 97 | **/macos/Flutter/GeneratedPluginRegistrant.swift 98 | **/macos/Flutter/Flutter-Debug.xcconfig 99 | **/macos/Flutter/Flutter-Release.xcconfig 100 | **/macos/Flutter/Flutter-Profile.xcconfig 101 | 102 | # Coverage 103 | coverage/ 104 | 105 | # Exceptions to above rules. 106 | !**/ios/**/default.mode1v3 107 | !**/ios/**/default.mode2v3 108 | !**/ios/**/default.pbxuser 109 | !**/ios/**/default.perspectivev3 110 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 111 | !/dev/ci/**/Gemfile.lock 112 | .idea/workspace.xml 113 | .idea/libraries/Dart_Packages.xml 114 | .idea/workspace.xml 115 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/copyright/Apache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.2.4] - 17/05/23 2 | - Sliver extensions 3 | ## [2.2.1] - 17/05/23 4 | - Flutter 3.1 ready 5 | ## [2.0.5] - 11/03/21 6 | - `delay`extension for delay, example: await 3.seconds.delay(() { some code } 7 | - `added getTextSize` - A good use-case will be drawing a divider with the exect width of the text above it. 8 | - `isVideo` - Checks if string is an video file. 9 | - `isAudio` - Checks if string is an audio file. 10 | - `isImage` - Checks if string is an image file. 11 | - `isNumericOnly` - Check if the string has any number in it. 12 | - `isAlphabetOnly` - Checks if string consist only Alphabet. (No Whitespace) 13 | - `hasCapitalletter` - Checks if string contains at least one Capital Letter. 14 | - `isHTML` - Checks if string is an html file. 15 | - `isEmail` - Checks if string is email.. 16 | - `isPhoneNumber` - Checks if string is phone number, good for login checks. 17 | - `isUsername` - Checks if string is a valid username, good for login checks. 18 | - `isCurrency` - Checks if string is Currency. 19 | - `isPalindrom` - Checks if string is Palindrom. (good to know for interviews as well) 20 | 21 | 22 | ## [2.0.0] - 11/03/21 23 | - `Support flutter 2` 24 | - `Support null safety` 25 | - `add new extensions` 26 | ## [1.0.0] - 19/03/20 27 | - `Context extensions` 28 | - `Text Extensions` 29 | - `Icon Extensions` 30 | - `List Extensions` 31 | ## [0.3.4] 9/03/20 32 | - sortBy - Sorts elements in the array in-place according to natural sort order of the value returned by specified selector function. 33 | ## [0.3.3] 19/02/20 34 | - algorithm - quick search 35 | - Flutter - Tooltip extension 36 | 37 | ## [0.2.0] - 23/01/20. 38 | - .groupBy() added Iterable + tests 39 | - .intersect() + test 40 | - .toMutableSet() + test 41 | 42 | ## [0.1.8] - 22/01/20. 43 | - .filter() added Iterable + tests 44 | - .notFilter() added Iterable + tests 45 | - .isNullOrWhiteSpace added String + tests 46 | - .insert() added Strings + test 47 | 48 | ## [0.1.7] - 19/01/202. 49 | - find - Iterable extensions + tests 50 | - added Ranges extension for int + tests 51 | 52 | ## [0.1.4] - 19/01/202. 53 | - associate in Iterables extensions + tests 54 | - added Http Extensions: - httpGet httpPost httpPut httpDelete 55 | - added int Extensions: - absolute 56 | - isEven 57 | - isOdd 58 | - isPositive 59 | - isNegative 60 | - tenth 61 | - fourth 62 | - third 63 | - half 64 | - doubled 65 | - tripled 66 | - quadrupled 67 | - squared 68 | 69 | 70 | ## [0.1.3] - 19/01/2020. 71 | - anyChar in Strings extensions + tests 72 | - replaceBefore Strings extensions + tests 73 | - toDoubleOrNull Strings extensions test 74 | - removeAllWhiteSpace Strings extensions + tests 75 | - toCharArray Strings extension + tests 76 | 77 | 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015, Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![](https://img.shields.io/badge/ver-2.2.4-green)](https://github.com/droididan/dart_extentions) ![](https://img.shields.io/badge/Code%20Coverage-96%25-green) ![](https://img.shields.io/badge/Bitrise-Pass-green) 3 | 4 | 5 | ## What New (2.2.4) 6 | * Sliver extensions 7 | 8 | Why Method Extensions? When you’re using someone else’s API or when you implement a library that’s widely used, it’s often impractical or impossible to change the API. But you might still want to add some functionality. 9 | 10 | *let me know if you want something specific or you found a bug at bar.idan@gmail.com* 11 | ## Let get started 💪🏻 12 | 13 | 1. Go to `pubspec.yaml` 14 | 2. add a dart_extensions and replace `[version]` with the latest version: 15 | 16 | ```dart 17 | dependencies: 18 | dart_extensions: ^[version] 19 | ``` 20 | 21 | 3. click the packages get button or *flutter pub get* 22 | ## Responsive UI 23 | Very common way to calculate size in percentage is using the MediaQuery like so: 24 | ```dart 25 | MediaQuery.of(context).size.width * 0.1 26 | ``` 27 | 28 | 29 | Flatten a nested Map into a single level map 30 | ```dart 31 | response.flatJson({ 32 | 'key1': {'keyA': 'valueI'}, 33 | 'key2': {'keyB': 'valueII'}, 34 | 'key3': { 35 | 'a': { 36 | 'b': {'c': 2} 37 | } 38 | } 39 | }); 40 | 41 | The result you can also specify max depth, its the maximum number of nested objects to flatten. 42 | ``` 43 | // { 44 | // 'key1.keyA': 'valueI', 45 | // 'key2.keyB': 'valueII', 46 | // 'key3.a.b.c': 2 47 | // }; 48 | 49 | 50 | Instead of the boilerplate we can use this awesome extension and get the same results. 51 | 52 | Wrap your Application with: 53 | ```dart 54 | ResponsiveApp( 55 | builder: (BuildContext context, Orientation orientation, DeviceType deviceType) { 56 | return YourAppWidget() 57 | ) 58 | ``` 59 | 60 | ```dart 61 | AnimatedList( 62 | key: chatListKey, 63 | reverse: true, 64 | padding: EdgeInsets.only(top: 10.textSizeResponsive), 65 | shrinkWrap: true, 66 | ``` 67 | Also the text should be responsive, no problem 68 | ```dart 69 | Text( 70 | 'Note added by ${message.from ?? ''}', 71 | style: avanirBook.copyWith(fontSize: 8.responsiveText), 72 | ), 73 | ``` 74 | 75 | ## Iterable Extensions 76 | 77 | ### .any() 78 | Returns `true` if at least one element matches the given predicate. 79 | ```dart 80 | final users = [User(22, "Kasey"), User(23, "Jadn")]; 81 | users.any((u) => u.name == 'Kasey') // true 82 | ``` 83 | 84 | ### .groupBy() 85 | Groups the elements in values by the value returned by key. 86 | ```dart 87 | final users = [User(22, "Kasey"), User(23, "Jadn"), User(22, "Rene"), User(32, "Aden")]; 88 | 89 | users.groupBy((u) => u.age); 90 | ``` 91 | Sort the users by age: 92 | ```dart 93 | { 94 | 22: [User:22, Kasey, User:22, Rene], 95 | 23: [User:23, Jadn], 96 | 32: [User:32, Aden] 97 | } 98 | ``` 99 | 100 | ### .sortBy() 101 | Sorts elements in the array in-place according to natural sort order of the value returned by specified selector function. 102 | ```dart 103 | final users = [User(22, "Kasey"), User(16, "Roni"), User(23, "Jadn")]; 104 | users.sortBy((u) => u.age) /// [User(16, "Roni"), [User(22, "Kasey"), User(23, "Jadn")] 105 | ``` 106 | 107 | ### .find() 108 | Returns the first element matching the given predicate, or `null` if element wasn't found. 109 | ```dart 110 | final users = [User(22, "Kasey"), User(23, "Jadn"), User(22, "Rene"), User(32, "Aden")]; 111 | 112 | users.find((u) => u.name == "Rene") // User(22, "Rene") 113 | ``` 114 | 115 | ### .chunks() 116 | Splits the Iterable into chunks of the specified `size` 117 | ```dart 118 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].chunks(3)) 119 | ``` 120 | *result* 121 | ```dart 122 | ([1, 2, 3], [4, 5, 6], [7, 8, 9], [10]) 123 | ``` 124 | 125 | ### .filter() 126 | Returns a list containing only elements matching the given predicate, the return type will be `List`, 127 | unlike the `where` operator that return `Iterator`, also it filters null. 128 | ```dart 129 | final users = [User(22, "Kasey"), User(23, "Jadn"), User(22, "Rene"), User(32, "Aden")]; 130 | final filtered = users.filter((u) => u.name == "Kasey"); // [User(22, "Kasey")] <- Type List 131 | 132 | final listWithNull = [null, User(23, "Jadn"), User(22, "Rene"), User(32, "Aden")]; 133 | final filtered = listWithNull.filter((u) => u.name == "Jadn"); // [User(23, "Jadn")] 134 | ``` 135 | 136 | ### .intersect() 137 | Returns a set containing all elements that are contained by both this set and the specified collection. 138 | ```dart 139 | Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6]) // 1,2,3,4,5,6 140 | ``` 141 | 142 | ### .filterNot() 143 | Returns a list containing only not the elements matching the given predicate, the return type will be `List`, 144 | unlike the `where` operator that return `Iterator`, also it filters null. 145 | ```dart 146 | final users = [User(22, "Kasey"), User(23, "Jadn"), User(22, "Rene"), User(32, "Aden")]; 147 | final filtered = users.filterNot((u) => u.name == "Kasey"); // [User(23, "Jadn"), User(22, "Rene"), User(32, "Aden")] <- Type List 148 | 149 | final listWithNull = [null, User(23, "Jadn"), User(22, "Rene"), User(32, "Aden")]; 150 | final filtered = listWithNull.filterNot((u) => u.name == "Jadn"); // [User(22, "Rene"), User(32, "Aden")] 151 | ``` 152 | 153 | ### .takeOnly() 154 | Returns a list containing first [n] elements. 155 | ```dart 156 | [1, 2, 3, 4].takeOnly(1) // [1] 157 | ``` 158 | 159 | ### .drop() 160 | Returns a list containing all elements except first [n] elements. 161 | ```dart 162 | [1, 2, 3, 4].drop(1) // [2, 3, 4] 163 | ``` 164 | 165 | ### .forEachIndexed() 166 | Performs the given action on each element on iterable, providing sequential `index` with the `element`. 167 | ```dart 168 | ["red","green","blue"].forEachIndexed((item, index) { 169 | print("$item, $index"); 170 | }); // 0: red // 1: green // 2: blue``` 171 | ``` 172 | 173 | ### .sortedDescending() 174 | Returns a new list with all elements sorted according to descending natural sort order. 175 | ```dart 176 | var list = [1,2,3,4,5]; 177 | final descendingList = list.sortedDescending(); 178 | print(descendingList); // [5, 4, 3, 2, 1] 179 | ``` 180 | 181 | ### .count() 182 | Return a number of the existing elements by a specific predicate 183 | ```dart 184 | final users = [User(33, "Miki"), User(45, "Anna"), User(19, "Amit")]; 185 | 186 | final aboveAgeTwenty = users.count((user) => user.age > 20); 187 | print(aboveAgeTwenty); // 2 188 | ``` 189 | 190 | ### .associate() 191 | Creates a Map instance in which the keys and values are computed from the iterable. 192 | ```dart 193 | final users = [User(33, "Miki"), User(45, "Anna"), User(19, "Amit")]; 194 | 195 | users.associate((k) => k.name, (e) => e.age) // 'Miki': 33, 'Anna': 45, 'Amit': 19} 196 | ``` 197 | 198 | ### .concatWithMultipleList() 199 | Return a list concatenates the output of the current list and multiple iterables. 200 | ```dart 201 | final listOfLists = [ 202 | [5, 6, 7], 203 | [8, 9, 10] 204 | ]; 205 | [1, 2, 3, 4].concatWithMultipleList(listOfLists); 206 | 207 | // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 208 | ``` 209 | 210 | ### .distinctBy() 211 | Returns a list containing only the elements from given collection having distinct keys. 212 | ```dart 213 | // example 1 214 | final users = ["Zack", "Ian", "Ronit"]; 215 | users.distinctBy((u) => u.toLowerCase().startsWith("z")); // Zack 216 | 217 | // example 2 218 | final users = [User(11, 'idan'), User(12, 'ronit'), User(11, 'asaf')]; 219 | 220 | final dist = users.distinctBy((u) => u.age); 221 | dist.forEach((u) => print(u.age)); // 11, 12 222 | ``` 223 | 224 | ### .zip() 225 | Zip is used to combine multiple iterables into a single list that contains the combination of them two. 226 | ```dart 227 | 228 | final soldThisMonth = [Motorcycle(2020, 'BMW R1200GS'), Motorcycle(1967, 'Honda GoldWing')]; 229 | final soldLastMonth = [Motorcycle(2014, 'Honda Transalp'), Motorcycle(2019, 'Ducati Multistrada')]; 230 | 231 | final sales = soldThisMonth.zip(soldLastMonth).toList(); 232 | 233 | print(sales); // [ 234 | [brand: BMW R1200GS year: 2020, brand: Honda Transalp year: 2014], // first pair from this month and last 235 | [brand: Honda GoldWing year: 1967, brand: Ducati Multistrada year: 2019] // second pair from last month 236 | ] 237 | ``` 238 | See [iterable.dart](https://github.com/droididan/dart_extentions/blob/master/lib/iterable.dart) for more examples. 239 | 240 | ## Flutter Extensions 241 | 242 | ### Context extensions 243 | Are you not tired from typing `MediaQuery.of(context).size...` to get height or width? here's a cool extension 244 | ```dart 245 | context.mq // returns the MediaQuery 246 | ``` 247 | 248 | ```dart 249 | context isLandscape // returns if Orientation is landscape 250 | ``` 251 | 252 | ```dart 253 | context.sizePx // returns same as MediaQuery.of(context).size 254 | ``` 255 | 256 | ```dart 257 | context.widthPx // returns same as MediaQuery.of(context).size.width 258 | ``` 259 | 260 | ```dart 261 | context.heightPx // returns same as MediaQuery.of(context).height 262 | ``` 263 | 264 | ### Text Extensions 265 | ```dart 266 | final text = Text('hello') 267 | .bold() 268 | .fontSize(25) 269 | .italic(); 270 | ``` 271 | 272 | ### List Extensions 273 | ```dart 274 | final someWidgetList = [ 275 | Text('hello'), 276 | Text('world'), 277 | ].toColumnWidget(); // toRowWidget(), toStackWidget() 278 | ``` 279 | 280 | ### Widget extensions 281 | So now we can just add round corners, shadows, align, and added gestures to our `Widgets` without the crazy water-fall effect. awesome! 282 | That's just the tip of the iceberg, expect to see very cool stuff soon. 283 | ```dart 284 | class Home extends StatefulWidget { 285 | @override 286 | _HomeState createState() => _HomeState(); 287 | } 288 | 289 | class _HomeState extends State { 290 | 291 | @override 292 | Widget build(BuildContext context) { 293 | return Scaffold( 294 | body: Container( 295 | child: Stack( 296 | children: [ 297 | Container( 298 | height: 100, 299 | width: 100, 300 | ) .withRoundCorners(backgroundColor: Colors.grey) 301 | .withShadow() 302 | .alignAtCenter() 303 | .toCenter() 304 | .withTooltip('just a tooltip') 305 | .paddingOnly(left: 10) 306 | .paddingAll(20) 307 | .onTap(() => print('tap')) 308 | .onLongPress(() => print('long press')) 309 | ], 310 | ), 311 | ), 312 | ); 313 | } 314 | } 315 | ``` 316 | 317 | ### Navigation 318 | We can navigate from every widget by calling these methods 319 | ```dart 320 | navigateTo(route: MaterialPageRoute(builder: (c) => Login())); 321 | navigateByRouteName(Routes.home, ); 322 | final result = navigateBack(); 323 | ``` 324 | 325 | 326 | ## Http Extensions 327 | 328 | ### .httpGet() 329 | Sends an HTTP GET request with the given headers to the given URL 330 | ```dart 331 | final json = await "https://jsonplaceholder.typicode.com/posts".httpGet(); 332 | ``` 333 | *result:* 334 | ```json 335 | [ 336 | { 337 | "userId": 1, 338 | "id": 1, 339 | "title": "sunt aut facere", 340 | "body": "quia et suscipit" 341 | }, 342 | { 343 | "userId": 1, 344 | "id": 2, 345 | "title": "qui est esse", 346 | "body": "dolor beatae ea dolores neque" 347 | }, 348 | ] 349 | ``` 350 | 351 | *usage with then:* 352 | ```dart 353 | "https://jsonplaceholder.typicode.com/posts".httpGet().then((result) { 354 | print(result); 355 | }).catchError((e) => print(e)); 356 | ``` 357 | 358 | ### .httpPost() 359 | Sends an HTTP POST request with the given headers and body to the given URL which can be a [Uri] or a [String]. 360 | ```dart 361 | String json = '{"title": "Hello", "body": "body text", "userId": 1}'; 362 | final json = await "https://jsonplaceholder.typicode.com/posts".httpPost(json); 363 | ``` 364 | 365 | for more examples (put, delete) See [http.dart](https://github.com/droididan/dart_extentions/blob/master/lib/http.dart) 366 | 367 | ## Range Extensions 368 | ### .until() 369 | Returns a sequence of integer, starting from the current number until the [end] number. [step] is optional, it will step number if given 370 | ```dart 371 | for(final num in 1.until(10)) { 372 | numbers.add(num); 373 | } 374 | ``` 375 | *result* 376 | ```dart 377 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 378 | ``` 379 | with step: 380 | ```dart 381 | for(final num in 1.until(10, step: 2)) { 382 | numbers.add(num); 383 | } 384 | ``` 385 | *result* 386 | ```dart 387 | [1, 3, 5, 7, 9] 388 | ``` 389 | 390 | ## String Extensions 391 | 392 | 393 | ### .asBool() 394 | Returns true if this string is any of these values: "true", "yes", "1", or if the string is a number and greater than 0, false if less than 1. This is also case insensitive. 395 | ```dart 396 | 'true'.asBool // true 397 | 'True'.asBool // true 398 | 'false'.asBool // false 399 | 'False'.asBool // false 400 | 'yes'.asBool // true 401 | 'YES'.asBool // true 402 | 'no'.asBool // false 403 | 'NO'.asBool // false 404 | ``` 405 | 406 | 407 | ### .insert() 408 | Returns a new string in which a specified string is inserted at a specified index position in this instance. 409 | ```dart 410 | 'test'.insert(1, 't') // 'ttest' 411 | '123456890'.insert(6, '7') // '1234567890' 412 | 'dart cool'.insert(4, ' is') // 'dart is cool' 413 | ``` 414 | 415 | ### .isNullOrWhiteSpace() 416 | Indicates whether a specified string is `null`, `empty`, or consists only of `white-space` characters. 417 | ```dart 418 | 'test'.isNullOrWhiteSpace // false 419 | ' '.isNullOrWhiteSpace, // true 420 | null.isNullOrWhiteSpace, // true 421 | ' te st '.isNullOrWhiteSpace // false 422 | ``` 423 | 424 | ### .replaceAfter() 425 | Replace part of string after the first occurrence of given delimiter. 426 | ```dart 427 | print("myemail@".replaceAfter("@", "gmail.com")); // myemail@gmail.com 428 | ``` 429 | ### .replaceBefore() 430 | Replace part of string before the first occurrence of given delimiter. 431 | ```dart 432 | print('@domain.com'.replaceBefore('@', "name")); // "name@domain.com" 433 | ``` 434 | 435 | ### .anyChar() 436 | Returns `true` if at least one element matches the given predicate 437 | ```dart 438 | 'test'.anyChar((c) => c == 't'); // true; 439 | 'test'.anyChar((c) => c == 'd'); // false; 440 | ``` 441 | 442 | ### .ifEmpty() 443 | If the string is empty perform an action. 444 | ```dart 445 | "".ifEmpty(() => print("do any action here")); // do any action here 446 | ``` 447 | 448 | ### .toDoubleOrNull() 449 | Parses the string as an integer or returns `null` if it is not a number. 450 | ```dart 451 | var number = '12345'.toDoubleOrNull(); // 12345 452 | var notANumber = '123-45'.toDoubleOrNull(); // null 453 | ``` 454 | 455 | ### .limitFromEnd() 456 | Limit the string to a maximum length, taking from the end of the string. 457 | ```dart 458 | var longString = "0123456789"; 459 | var noMoreThanThree = longString.limitFromEnd(3); // "789" 460 | ``` 461 | 462 | ### .limitFromStart() 463 | Limit the string to a maximum length, taking from the start of the string. 464 | ```dart 465 | var longString = "0123456789"; 466 | var noMoreThanThree = longString.limitFromStart(3); // "012" 467 | ``` 468 | 469 | ## int Extensions 470 | 471 | ### .inRangeOf() 472 | Return the min if this number is smaller then minimum 473 | Return the max if this number is bigger the the maximum 474 | Return this number if it's between the range 475 | ```dart 476 | 1.inRangeOf(0, 3) // 1 number in range so will return the number 477 | 2.inRangeOf(3, 4) // 3 number is smaller then the range so will return min 478 | 5.inRangeOf(3, 4) // 4 number is bigger then the range so will return max 479 | ``` 480 | 481 | # Flutter Extensions Full List 482 | 483 | ## Flutter 484 | - `Tooltip` 485 | - `algin` 486 | - `center` 487 | - `container` 488 | - `padding` 489 | - `navigation` 490 | - `Context` 491 | - `Text` 492 | - `List` 493 | - `Icon` 494 | 495 | ## Http Extensions 496 | - `httpGet` 497 | - `httpPost` 498 | - `httpPut` 499 | - `httpDelete` 500 | 501 | ## Iterables Extensions 502 | - `sortBy` 503 | - `toMutableSet` 504 | - `intersect` 505 | - `groupBy` 506 | - `find` 507 | - `filter` 508 | - `filterNot` 509 | - `isEmptyOrNull` 510 | - `chunks` 511 | - `zip` 512 | - `half` 513 | - `takeOnly` 514 | - `drop` 515 | - `firstHalf` 516 | - `secondHalf` 517 | - `swap` 518 | - `getRandom` 519 | - `firstOrNull` 520 | - `firstOrNullWhere` 521 | - `firstOrDefault` 522 | - `lastOrNull` 523 | - `lastOrDefault` 524 | - `forEachIndexed` 525 | - `sortedDescending` 526 | - `containsAll` 527 | - `count` 528 | - `distinctBy` 529 | - `subtract` 530 | - `concatWithSingleList` 531 | - `concatWithMultipleList` 532 | - `associate` 533 | 534 | ## Range Extensions 535 | - `until` 536 | 537 | ## Strings Extensions 538 | - `validateEmail` 539 | - `removeSurrounding` 540 | - `isNullOrEmpty` 541 | - `replaceAfter` 542 | - `replaceBefore` 543 | - `orEmpty` 544 | - `ifEmpty` 545 | - `lastIndex` 546 | - `printThis` 547 | - `equalsIgnoreCase` 548 | - `toDoubleOrNull` 549 | - `toIntOrNull` 550 | - `anyChar` 551 | - `removeAllWhiteSpace` 552 | - `isNotBlank` 553 | - `toCharArray` 554 | - `insert` 555 | - `isNullOrWhiteSpace` 556 | - `asBool` 557 | 558 | ## DateTime Extensions 559 | - `toMilliseconds` 560 | - `toSeconds` 561 | - `toMinutes` 562 | - `toHours` 563 | - `toDays` 564 | - `isToday` 565 | - `addOrRemoveYears` 566 | - `addOrRemoveMonth` 567 | - `addOrRemoveDay` 568 | - `addOrRemoveMinutes` 569 | - `addOrRemoveSeconds` 570 | - `startOfDay` 571 | - `startOfMonth` 572 | - `startOfYear` 573 | - `operator to add dates` 574 | - `operator to subtract dates` 575 | - `tomorrow` 576 | - `yesterday` 577 | - `min` 578 | - `max` 579 | - `isLeapYear` 580 | - `limitFromEnd` 581 | - `limitFromStart` 582 | 583 | ## Integers Extensions 584 | - `inRangeOf` 585 | - `absolute` 586 | - `isEven` 587 | - `isOdd` 588 | - `isPositive` 589 | - `isNegative` 590 | - `tenth` 591 | - `fourth` 592 | - `third` 593 | - `half` 594 | - `doubled` 595 | - `tripled` 596 | - `quadrupled` 597 | - `squared` 598 | - `asBool` 599 | 600 | ## Contributing 601 | 602 | If you have read up till here, then 🎉🎉🎉. There are couple of ways in which you can contribute to 603 | the growing community of `dart_extensions.dart`. 604 | 605 | 606 | - Propose any feature, enhancement 607 | - Report a bug 608 | - Fix a bug 609 | - Participate in a discussion and help in decision making 610 | - Write and improve some **documentation**. Documentation is super critical and its importance 611 | cannot be overstated! 612 | - Send in a Pull Request :-) 613 | 614 | 615 | ## Contributors ✨ 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 |

Idan Ayalon

💻 📖 👀

Xamantra

💻 📖 👀
626 | 627 | 628 | 629 | ## License 630 | ``` 631 | Copyright 2020 Idan Ayalon 632 | 633 | Licensed under the Apache License, Version 2.0 (the "License"); 634 | you may not use this file except in compliance with the License. 635 | You may obtain a copy of the License at 636 | 637 | http://www.apache.org/licenses/LICENSE-2.0 638 | Unless required by applicable law or agreed to in writing, software 639 | distributed under the License is distributed on an "AS IS" BASIS, 640 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 641 | See the License for the specific language governing permissions and 642 | limitations under the License. 643 | ``` -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /example/.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: fabeb2a16f1d008ab8230f450c49141d35669798 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015, Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | def localProperties = new Properties() 15 | def localPropertiesFile = rootProject.file('local.properties') 16 | if (localPropertiesFile.exists()) { 17 | localPropertiesFile.withReader('UTF-8') { reader -> 18 | localProperties.load(reader) 19 | } 20 | } 21 | 22 | def flutterRoot = localProperties.getProperty('flutter.sdk') 23 | if (flutterRoot == null) { 24 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 25 | } 26 | 27 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 28 | if (flutterVersionCode == null) { 29 | flutterVersionCode = '1' 30 | } 31 | 32 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 33 | if (flutterVersionName == null) { 34 | flutterVersionName = '1.0' 35 | } 36 | 37 | apply plugin: 'com.android.application' 38 | apply plugin: 'kotlin-android' 39 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 40 | 41 | android { 42 | compileSdkVersion 28 43 | 44 | sourceSets { 45 | main.java.srcDirs += 'src/main/kotlin' 46 | } 47 | 48 | lintOptions { 49 | disable 'InvalidPackage' 50 | } 51 | 52 | defaultConfig { 53 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 54 | applicationId "com.rallycoder.tests" 55 | minSdkVersion 16 56 | targetSdkVersion 28 57 | versionCode flutterVersionCode.toInteger() 58 | versionName flutterVersionName 59 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 60 | } 61 | 62 | buildTypes { 63 | release { 64 | // TODO: Add your own signing config for the release build. 65 | // Signing with the debug keys for now, so `flutter run --release` works. 66 | signingConfig signingConfigs.debug 67 | } 68 | } 69 | } 70 | 71 | flutter { 72 | source '../..' 73 | } 74 | 75 | dependencies { 76 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 77 | testImplementation 'junit:junit:4.12' 78 | androidTestImplementation 'androidx.test:runner:1.1.1' 79 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 80 | } 81 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 16 | 21 | 25 | 32 | 36 | 39 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/rallycoder/tests/MainActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | package com.rallycoder.tests 15 | 16 | import androidx.annotation.NonNull; 17 | import io.flutter.embedding.android.FlutterActivity 18 | import io.flutter.embedding.engine.FlutterEngine 19 | import io.flutter.plugins.GeneratedPluginRegistrant 20 | 21 | class MainActivity : FlutterActivity() { 22 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 23 | GeneratedPluginRegistrant.registerWith(flutterEngine); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 21 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | buildscript { 15 | ext.kotlin_version = '1.3.50' 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | 21 | dependencies { 22 | classpath 'com.android.tools.build:gradle:3.5.0' 23 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 24 | } 25 | } 26 | 27 | allprojects { 28 | repositories { 29 | google() 30 | jcenter() 31 | } 32 | } 33 | 34 | rootProject.buildDir = '../build' 35 | subprojects { 36 | project.buildDir = "${rootProject.buildDir}/${project.name}" 37 | } 38 | subprojects { 39 | project.evaluationDependsOn(':app') 40 | } 41 | 42 | task clean(type: Delete) { 43 | delete rootProject.buildDir 44 | } 45 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2020 Idan Ayalon. All rights reserved. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | # 13 | org.gradle.jvmargs=-Xmx1536M 14 | android.enableR8=true 15 | android.useAndroidX=true 16 | android.enableJetifier=true 17 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2020 Idan Ayalon. All rights reserved. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | # 13 | distributionBase=GRADLE_USER_HOME 14 | distributionPath=wrapper/dists 15 | zipStoreBase=GRADLE_USER_HOME 16 | zipStorePath=wrapper/dists 17 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 18 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | include ':app' 15 | 16 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 17 | 18 | def plugins = new Properties() 19 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 20 | if (pluginsFile.exists()) { 21 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 22 | } 23 | 24 | plugins.each { name, path -> 25 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 26 | include ":$name" 27 | project(":$name").projectDir = pluginDirectory 28 | } 29 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "20x20", 5 | "idiom": "iphone", 6 | "filename": "Icon-App-20x20@2x.png", 7 | "scale": "2x" 8 | }, 9 | { 10 | "size": "20x20", 11 | "idiom": "iphone", 12 | "filename": "Icon-App-20x20@3x.png", 13 | "scale": "3x" 14 | }, 15 | { 16 | "size": "29x29", 17 | "idiom": "iphone", 18 | "filename": "Icon-App-29x29@1x.png", 19 | "scale": "1x" 20 | }, 21 | { 22 | "size": "29x29", 23 | "idiom": "iphone", 24 | "filename": "Icon-App-29x29@2x.png", 25 | "scale": "2x" 26 | }, 27 | { 28 | "size": "29x29", 29 | "idiom": "iphone", 30 | "filename": "Icon-App-29x29@3x.png", 31 | "scale": "3x" 32 | }, 33 | { 34 | "size": "40x40", 35 | "idiom": "iphone", 36 | "filename": "Icon-App-40x40@2x.png", 37 | "scale": "2x" 38 | }, 39 | { 40 | "size": "40x40", 41 | "idiom": "iphone", 42 | "filename": "Icon-App-40x40@3x.png", 43 | "scale": "3x" 44 | }, 45 | { 46 | "size": "60x60", 47 | "idiom": "iphone", 48 | "filename": "Icon-App-60x60@2x.png", 49 | "scale": "2x" 50 | }, 51 | { 52 | "size": "60x60", 53 | "idiom": "iphone", 54 | "filename": "Icon-App-60x60@3x.png", 55 | "scale": "3x" 56 | }, 57 | { 58 | "size": "20x20", 59 | "idiom": "ipad", 60 | "filename": "Icon-App-20x20@1x.png", 61 | "scale": "1x" 62 | }, 63 | { 64 | "size": "20x20", 65 | "idiom": "ipad", 66 | "filename": "Icon-App-20x20@2x.png", 67 | "scale": "2x" 68 | }, 69 | { 70 | "size": "29x29", 71 | "idiom": "ipad", 72 | "filename": "Icon-App-29x29@1x.png", 73 | "scale": "1x" 74 | }, 75 | { 76 | "size": "29x29", 77 | "idiom": "ipad", 78 | "filename": "Icon-App-29x29@2x.png", 79 | "scale": "2x" 80 | }, 81 | { 82 | "size": "40x40", 83 | "idiom": "ipad", 84 | "filename": "Icon-App-40x40@1x.png", 85 | "scale": "1x" 86 | }, 87 | { 88 | "size": "40x40", 89 | "idiom": "ipad", 90 | "filename": "Icon-App-40x40@2x.png", 91 | "scale": "2x" 92 | }, 93 | { 94 | "size": "76x76", 95 | "idiom": "ipad", 96 | "filename": "Icon-App-76x76@1x.png", 97 | "scale": "1x" 98 | }, 99 | { 100 | "size": "76x76", 101 | "idiom": "ipad", 102 | "filename": "Icon-App-76x76@2x.png", 103 | "scale": "2x" 104 | }, 105 | { 106 | "size": "83.5x83.5", 107 | "idiom": "ipad", 108 | "filename": "Icon-App-83.5x83.5@2x.png", 109 | "scale": "2x" 110 | }, 111 | { 112 | "size": "1024x1024", 113 | "idiom": "ios-marketing", 114 | "filename": "Icon-App-1024x1024@1x.png", 115 | "scale": "1x" 116 | } 117 | ], 118 | "info": { 119 | "version": 1, 120 | "author": "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "universal", 5 | "filename": "LaunchImage.png", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "filename": "LaunchImage@2x.png", 11 | "scale": "2x" 12 | }, 13 | { 14 | "idiom": "universal", 15 | "filename": "LaunchImage@3x.png", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "version": 1, 21 | "author": "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/droididan/dart_extensions/71546e78caa0ef10036cb6d77bfd7ab612f96b09/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | tests 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:dart_extensions/dart_extensions.dart'; 15 | import 'package:flutter/material.dart'; 16 | 17 | void main() => runApp(MyApp()); 18 | 19 | class MyApp extends StatelessWidget { 20 | // This widget is the root of your application. 21 | @override 22 | Widget build(BuildContext context) { 23 | return MaterialApp( 24 | routes: {"/login": (_) => Login()}, 25 | title: 'Flutter Demo', 26 | theme: ThemeData(primarySwatch: Colors.blue), 27 | home: Padding( 28 | padding: const EdgeInsets.all(8.0), 29 | child: Home(), 30 | ), 31 | ); 32 | } 33 | } 34 | 35 | class Routes { 36 | static final login = "/login"; 37 | } 38 | 39 | class Home extends StatefulWidget { 40 | 41 | 42 | @override 43 | _HomeState createState() => _HomeState(); 44 | } 45 | 46 | class _HomeState extends State { 47 | @override 48 | Widget build(BuildContext context) { 49 | 50 | print([1,2,3,4].mapList((f) => f.toDouble())); 51 | return Scaffold( 52 | body: Container( 53 | child: Stack( 54 | children: [ 55 | Container(height: 100, width: 100) 56 | .withRoundCorners(backgroundColor: Colors.grey) 57 | .withShadow() 58 | .alignAtCenter() 59 | .toCenter() 60 | .paddingOnly(left: 10) 61 | .paddingAll(20) 62 | .onTap( 63 | () async { 64 | final result = await navigateByRouteName(Routes.login); 65 | print(result); 66 | }, 67 | ) .withTooltip('just a tooltip') 68 | .onLongPress(() => print('long press')) 69 | 70 | ], 71 | ), 72 | ), 73 | ); 74 | } 75 | } 76 | 77 | class Login extends StatelessWidget { 78 | @override 79 | Widget build(BuildContext context) { 80 | return Container( 81 | color: Colors.grey, 82 | child: Center( 83 | child: MaterialButton( 84 | child: Text('Go Back'), 85 | onPressed: () => Navigator.pop(context), 86 | ), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tests 2 | description: A new Flutter application. 3 | publish_to: none 4 | 5 | version: 1.0.0+1 6 | homepage: https://github.com/droididan/dart_extentions 7 | environment: 8 | sdk: ">=2.7.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dart_extensions: 15 | path: ../ 16 | 17 | cupertino_icons: ^1.0.0 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | flutter: 24 | uses-material-design: true 25 | -------------------------------------------------------------------------------- /example/test/main.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | -------------------------------------------------------------------------------- /flutter_extentions.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lib/dart_extensions.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export 'package:dart_extensions/src/data_stractures/stack.dart'; 15 | export 'package:dart_extensions/src/date.dart'; 16 | export 'package:dart_extensions/src/files.dart'; 17 | export 'package:dart_extensions/src/flutter/align.dart'; 18 | export 'package:dart_extensions/src/flutter/icon.dart'; 19 | export 'package:dart_extensions/src/flutter/list.dart'; 20 | export 'package:dart_extensions/src/flutter/text.dart'; 21 | export 'package:dart_extensions/src/flutter/center.dart'; 22 | export 'package:dart_extensions/src/flutter/context.dart'; 23 | export 'package:dart_extensions/src/flutter/container.dart'; 24 | export 'package:dart_extensions/src/flutter/gesture_detector.dart'; 25 | export 'package:dart_extensions/src/flutter/navigation.dart'; 26 | export 'package:dart_extensions/src/flutter/padding.dart'; 27 | export 'package:dart_extensions/src/http.dart'; 28 | export 'package:dart_extensions/src/int.dart'; 29 | export 'package:dart_extensions/src/iterable.dart'; 30 | export 'package:dart_extensions/src/ranges.dart'; 31 | export 'package:dart_extensions/src/search_algorithms.dart'; 32 | export 'package:dart_extensions/src/sort_algorithms.dart'; 33 | export 'package:dart_extensions/src/string_ext.dart'; 34 | export 'package:dart_extensions/src/flutter/widgets.dart'; 35 | 36 | -------------------------------------------------------------------------------- /lib/emum.dart: -------------------------------------------------------------------------------- 1 | extension EnumExt on dynamic { 2 | static bool _isEnumItem(enumItem) { 3 | final splitEnum = enumItem.toString().split('.'); 4 | return splitEnum.length > 1 && splitEnum[0] == enumItem.runtimeType.toString(); 5 | } 6 | 7 | String convertToString({bool camelCase = false}) { 8 | assert(this != null); 9 | assert(_isEnumItem(this), '$this of type ${this.runtimeType.toString()} is not an enum item'); 10 | final temp = this.toString().split('.')[1]; 11 | return !camelCase ? temp : camelCaseToWords(temp); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/data_stractures/stack.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'dart:collection'; 15 | 16 | class StackX { 17 | final _list = ListQueue(); 18 | 19 | bool get isEmpty => _list.isEmpty; 20 | 21 | bool get isNotEmpty => _list.isNotEmpty; 22 | 23 | push(T element) => _list.addLast(element); 24 | 25 | T pop() { 26 | final T element = _list.last; 27 | _list.removeLast(); 28 | return element; 29 | } 30 | 31 | top() => _list.last; 32 | 33 | List addAll(Iterable elements) { 34 | _list.addAll(elements); 35 | return _list.toList(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/date.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | extension DateString on String { 15 | /// Parse string to [DateTime] 16 | DateTime? toDateTime() { 17 | try { 18 | return DateTime.tryParse(this); 19 | } on Exception catch (_) { 20 | return null; 21 | } 22 | } 23 | } 24 | 25 | extension DateInt on int { 26 | Duration toMilliseconds() => Duration(milliseconds: this); 27 | 28 | Duration toSeconds() => Duration(seconds: this); 29 | 30 | Duration toMinutes() => Duration(minutes: this); 31 | 32 | Duration toHours() => Duration(hours: this); 33 | 34 | Duration toDays() => Duration(days: this); 35 | } 36 | 37 | extension DateExtensions on DateTime { 38 | /// Adds this DateTime and Duration and returns the sum as a new DateTime object. 39 | DateTime operator +(Duration duration) => add(duration); 40 | 41 | /// Subtracts the Duration from this DateTime returns the difference as a new DateTime object. 42 | DateTime operator -(Duration duration) => subtract(duration); 43 | 44 | /// Returns true if [other] is in the same year as [this]. 45 | /// 46 | /// Does not account for timezones. 47 | bool isAtSameYearAs(DateTime other) => year == other.year; 48 | 49 | /// Returns true if [other] is in the same month as [this]. 50 | /// 51 | /// This means the exact month, including year. 52 | /// 53 | /// Does not account for timezones. 54 | bool isAtSameMonthAs(DateTime other) => 55 | isAtSameYearAs(other) && month == other.month; 56 | 57 | /// Returns true if [other] is on the same day as [this]. 58 | /// 59 | /// This means the exact day, including year and month. 60 | /// 61 | /// Does not account for timezones. 62 | bool isAtSameDayAs(DateTime other) => 63 | isAtSameMonthAs(other) && day == other.day; 64 | 65 | /// Returns true if [other] is at the same hour as [this]. 66 | /// 67 | /// This means the exact hour, including year, month and day. 68 | /// 69 | /// Does not account for timezones. 70 | bool isAtSameHourAs(DateTime other) => 71 | isAtSameDayAs(other) && hour == other.hour; 72 | 73 | /// Returns true if [other] is at the same minute as [this]. 74 | /// 75 | /// This means the exact minute, including year, month, day and hour. 76 | /// 77 | /// Does not account for timezones. 78 | bool disAtSameMinuteAs(DateTime other) => 79 | isAtSameHourAs(other) && minute == other.minute; 80 | 81 | /// Returns true if [other] is at the same minute as [this]. 82 | /// 83 | /// This means the exact minute, including year, month, day and hour. 84 | /// 85 | /// Does not account for timezones. 86 | bool isAtSameMinuteAs(DateTime other) => 87 | isAtSameHourAs(other) && minute == other.minute; 88 | 89 | /// Returns true if [other] is at the same second as [this]. 90 | /// 91 | /// This means the exact second, including year, month, day, hour and minute. 92 | /// 93 | /// Does not account for timezones. 94 | bool isAtSameSecondAs(DateTime other) => 95 | isAtSameMinuteAs(other) && second == other.second; 96 | 97 | /// Returns true if [other] is at the same millisecond as [this]. 98 | /// 99 | /// This means the exact millisecond, 100 | /// including year, month, day, hour, minute and second. 101 | /// 102 | /// Does not account for timezones. 103 | bool isAtSameMillisecondAs(DateTime other) => 104 | isAtSameSecondAs(other) && millisecond == other.millisecond; 105 | 106 | /// Returns true if [other] is at the same microsecond as [this]. 107 | /// 108 | /// This means the exact microsecond, 109 | /// including year, month, day, hour, minute, second and millisecond. 110 | /// 111 | /// Does not account for timezones. 112 | bool isAtSameMicrosecondAs(DateTime other) => 113 | isAtSameMillisecondAs(other) && microsecond == other.microsecond; 114 | 115 | bool get isYesterday { 116 | final nowDate = DateTime.now(); 117 | return year == nowDate.year && 118 | month == nowDate.month && 119 | day == nowDate.day - 1; 120 | } 121 | 122 | /// The list of days in a given month 123 | List get daysInMonth { 124 | var first = firstDayOfMonth; 125 | var daysBefore = first.weekday; 126 | var firstToDisplay = first.subtract(Duration(days: daysBefore)); 127 | var last = lastDayOfMonth; 128 | 129 | var daysAfter = 7 - last.weekday; 130 | 131 | // If the last day is sunday (7) the entire week must be rendered 132 | if (daysAfter == 0) { 133 | daysAfter = 7; 134 | } 135 | 136 | var lastToDisplay = last.add(Duration(days: daysAfter)); 137 | return daysInRange(firstToDisplay, lastToDisplay).toList(); 138 | } 139 | 140 | bool get isFirstDayOfMonth => isSameDay(firstDayOfMonth, this); 141 | 142 | bool get isLastDayOfMonth => isSameDay(lastDayOfMonth, this); 143 | 144 | DateTime get firstDayOfMonth => DateTime(this.year, this.month); 145 | 146 | DateTime get firstDayOfWeek { 147 | /// Handle Daylight Savings by setting hour to 12:00 Noon 148 | /// rather than the default of Midnight 149 | final day = DateTime.utc(this.year, this.month, this.day, 12); 150 | 151 | /// Weekday is on a 1-7 scale Monday - Sunday, 152 | /// This Calendar works from Sunday - Monday 153 | var decreaseNum = day.weekday % 7; 154 | return this.subtract(Duration(days: decreaseNum)); 155 | } 156 | 157 | DateTime get lastDayOfWeek { 158 | /// Handle Daylight Savings by setting hour to 12:00 Noon 159 | /// rather than the default of Midnight 160 | final day = DateTime.utc(this.year, this.month, this.day, 12); 161 | 162 | /// Weekday is on a 1-7 scale Monday - Sunday, 163 | /// This Calendar's Week starts on Sunday 164 | var increaseNum = day.weekday % 7; 165 | return day.add(Duration(days: 7 - increaseNum)); 166 | } 167 | 168 | /// The last day of a given month 169 | DateTime get lastDayOfMonth { 170 | var beginningNextMonth = (this.month < 12) 171 | ? DateTime(this.year, this.month + 1, 1) 172 | : DateTime(this.year + 1, 1, 1); 173 | return beginningNextMonth.subtract(Duration(days: 1)); 174 | } 175 | 176 | DateTime get previousMonth { 177 | var year = this.year; 178 | var month = this.month; 179 | if (month == 1) { 180 | year--; 181 | month = 12; 182 | } else { 183 | month--; 184 | } 185 | return DateTime(year, month); 186 | } 187 | 188 | DateTime get nextMonth { 189 | var year = this.year; 190 | var month = this.month; 191 | 192 | if (month == 12) { 193 | year++; 194 | month = 1; 195 | } else { 196 | month++; 197 | } 198 | return DateTime(year, month); 199 | } 200 | 201 | DateTime get previousWeek => this.subtract(Duration(days: 7)); 202 | 203 | DateTime get nextWeek => this.add(Duration(days: 7)); 204 | 205 | /// Returns a [DateTime] for each day the given range. 206 | /// 207 | /// [start] inclusive 208 | /// [end] exclusive 209 | static Iterable daysInRange(DateTime start, DateTime end) sync* { 210 | var i = start; 211 | var offset = start.timeZoneOffset; 212 | while (i.isBefore(end)) { 213 | yield i; 214 | i = i.add(Duration(days: 1)); 215 | var timeZoneDiff = i.timeZoneOffset - offset; 216 | if (timeZoneDiff.inSeconds != 0) { 217 | offset = i.timeZoneOffset; 218 | i = i.subtract(Duration(seconds: timeZoneDiff.inSeconds)); 219 | } 220 | } 221 | } 222 | 223 | static bool isSameWeek(DateTime a, DateTime b) { 224 | /// Handle Daylight Savings by setting hour to 12:00 Noon 225 | /// rather than the default of Midnight 226 | a = DateTime.utc(a.year, a.month, a.day); 227 | b = DateTime.utc(b.year, b.month, b.day); 228 | 229 | var diff = a.toUtc().difference(b.toUtc()).inDays; 230 | if (diff.abs() >= 7) { 231 | return false; 232 | } 233 | 234 | var min = a.isBefore(b) ? a : b; 235 | var max = a.isBefore(b) ? b : a; 236 | var result = max.weekday % 7 - min.weekday % 7 >= 0; 237 | return result; 238 | } 239 | 240 | /// Whether or not two times are on the same day. 241 | static bool isSameDay(DateTime a, DateTime b) => 242 | a.year == b.year && a.month == b.month && a.day == b.day; 243 | 244 | /// return true if the date is today 245 | bool isToday() { 246 | final now = DateTime.now(); 247 | final today = DateTime(now.year, now.month, now.day); 248 | final currentDate = DateTime(year, month, day); 249 | return today.isAtSameMomentAs(currentDate); 250 | } 251 | 252 | /// to add years to a [DateTime] add a positive number 253 | /// to remove years pass a negative number 254 | addOrRemoveYears(int years) { 255 | return DateTime(year + years, month, day, minute, second); 256 | } 257 | 258 | /// to add month to a [DateTime] add a positive number 259 | /// to remove years pass a negative number 260 | addOrRemoveMonth(int months) { 261 | return DateTime(year, month + months, day, minute, second); 262 | } 263 | 264 | /// to add days to a [DateTime] add a positive number 265 | /// to remove days pass a negative number 266 | addOrRemoveDay(int days) { 267 | return DateTime(year, month, day + days, minute, second); 268 | } 269 | 270 | /// to add min to a [DateTime] add a positive number 271 | /// to remove min pass a negative number 272 | addOrRemoveMinutes(int min) { 273 | return DateTime(year, month, day, minute + min, second); 274 | } 275 | 276 | /// to add sec to a [DateTime] add a positive number 277 | /// to remove sec pass a negative number 278 | addOrRemoveSeconds(int sec) { 279 | return DateTime(year, month, day, minute, second + sec); 280 | } 281 | 282 | /// Start time of Date times 283 | DateTime startOfDay() => DateTime(year, month, day); 284 | 285 | DateTime startOfMonth() => DateTime(year, month); 286 | 287 | DateTime startOfYear() => DateTime(year); 288 | 289 | /// next day 290 | DateTime tomorrow() => DateTime(year, month, day + 1); 291 | 292 | /// last day 293 | DateTime yesterday() => DateTime(year, month, day - 1); 294 | 295 | /// return the smaller date between 296 | DateTime min(DateTime that) => 297 | (millisecondsSinceEpoch < that.millisecondsSinceEpoch) ? this : that; 298 | 299 | DateTime max(DateTime that) => 300 | (millisecondsSinceEpoch > that.millisecondsSinceEpoch) ? this : that; 301 | 302 | bool get isLeapYear => 303 | (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); 304 | } 305 | -------------------------------------------------------------------------------- /lib/src/equality.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | typedef Comparer = bool Function(T left, T right); 15 | 16 | typedef Hasher = int Function(T value); 17 | 18 | typedef Sorter = int Function(T left, T right); 19 | 20 | class EqualityComparer { 21 | final Comparer compare; 22 | final Hasher hash; 23 | final Sorter sort; 24 | 25 | EqualityComparer({ 26 | Comparer? comparer, 27 | Hasher? hasher, 28 | Sorter? sorter, 29 | }) : compare = comparer ?? _getDefaultComparer(), 30 | hash = hasher ?? _getDefaultHasher(), 31 | sort = sorter ?? _getDefaultSorter(); 32 | 33 | static Comparer _getDefaultComparer() => 34 | (T left, T right) => left == right; 35 | 36 | static Hasher _getDefaultHasher() => (T value) => value.hashCode; 37 | 38 | static Sorter _getDefaultSorter() => (left, right) => 0; 39 | 40 | static EqualityComparer forType() => _registeredEqualityComparers[T] as EqualityComparer; 41 | 42 | static final Map _registeredEqualityComparers = { 43 | dynamic: EqualityComparer( 44 | comparer: (left, right) => left == right, 45 | hasher: (value) => value.hashCode, 46 | sorter: (left, right) => 0, 47 | ), 48 | num: EqualityComparer( 49 | comparer: (left, right) => left == right, 50 | hasher: (value) => value.hashCode, 51 | sorter: (left, right) => left.compareTo(right), 52 | ), 53 | int: EqualityComparer( 54 | comparer: (left, right) => left == right, 55 | hasher: (value) => value.hashCode, 56 | sorter: (left, right) => left.compareTo(right), 57 | ), 58 | double: EqualityComparer( 59 | comparer: (left, right) => left == right, 60 | hasher: (value) => value.hashCode, 61 | sorter: (left, right) => left.compareTo(right), 62 | ), 63 | String: EqualityComparer( 64 | comparer: (left, right) => left == right, 65 | hasher: (value) => value.hashCode, 66 | sorter: (left, right) => left.compareTo(right), 67 | ), 68 | Duration: EqualityComparer( 69 | comparer: (left, right) => left == right, 70 | hasher: (value) => value.hashCode, 71 | sorter: (left, right) => left.compareTo(right), 72 | ), 73 | BigInt: EqualityComparer( 74 | comparer: (left, right) => left == right, 75 | hasher: (value) => value.hashCode, 76 | sorter: (left, right) => left.compareTo(right), 77 | ), 78 | }; 79 | 80 | static bool registerEqualityComparer(EqualityComparer comparer, 81 | {bool overwrite = false}) { 82 | var typeExists = _registeredEqualityComparers.containsKey(T); 83 | if (!typeExists || overwrite) _registeredEqualityComparers[T] = comparer; 84 | return typeExists; 85 | } 86 | 87 | /// [EqualityComparer.registerEqualityComparer]. 88 | static EqualityComparer unregisterEqualityComparer() { 89 | return _registeredEqualityComparers.remove(T) as EqualityComparer; 90 | } 91 | } 92 | 93 | abstract class OrderedIterable extends Iterable { 94 | Iterable source; 95 | 96 | OrderedIterable(this.source); 97 | 98 | IterableSorter getIterableSorter(IterableSorter next); 99 | 100 | OrderedIterable createOrderedIterable( 101 | TNewKey Function(T) keySelector, 102 | EqualityComparer keyComparer, 103 | bool descending) { 104 | final result = InternalOrderedIterable( 105 | source, keySelector, keyComparer, descending); 106 | result.parent = this; 107 | return result; 108 | } 109 | } 110 | 111 | class InternalOrderedIterable extends OrderedIterable { 112 | OrderedIterable? parent; 113 | TKey Function(TValue) keySelector; 114 | EqualityComparer keyComparer; 115 | bool? descending; 116 | 117 | InternalOrderedIterable(Iterable source, this.keySelector, 118 | this.keyComparer, this.descending) 119 | : super(source) { 120 | this.source = source; 121 | } 122 | 123 | @override 124 | Iterator get iterator => iterate().iterator; 125 | 126 | Iterable iterate() sync* { 127 | final buffer = source.toList(); 128 | if (buffer.isNotEmpty) { 129 | final sorter = getIterableSorter(null); 130 | final map = sorter.sort(buffer, buffer.length); 131 | yield* OrderedBuffer(buffer, map); 132 | } 133 | } 134 | 135 | @override 136 | IterableSorter getIterableSorter(IterableSorter? next) { 137 | IterableSorter? sorter = InternalIterableSorter( 138 | keySelector, keyComparer, next,descending: descending); 139 | if (parent != null) sorter = parent!.getIterableSorter(sorter); 140 | return sorter; 141 | } 142 | } 143 | 144 | abstract class IterableSorter { 145 | void computeKeys(List elements, int count); 146 | 147 | int compareKeys(int idx1, int idx2); 148 | 149 | List sort(List elements, int count) { 150 | computeKeys(elements, count); 151 | final map = List.generate(count, (i) => i, growable: false); 152 | quickSort(map, 0, count - 1); 153 | return map; 154 | } 155 | 156 | void quickSort(List map, int left, int right) { 157 | do { 158 | var i = left; 159 | var j = right; 160 | final x = map[i + ((j - i) >> 1)]; 161 | do { 162 | while (i < map.length && compareKeys(x, map[i]) > 0) { 163 | i++; 164 | } 165 | while (j >= 0 && compareKeys(x, map[j]) < 0) { 166 | j--; 167 | } 168 | if (i > j) break; 169 | if (i < j) { 170 | final temp = map[i]; 171 | map[i] = map[j]; 172 | map[j] = temp; 173 | } 174 | i++; 175 | j--; 176 | } while (i <= j); 177 | if (j - left <= right - i) { 178 | if (left < j) quickSort(map, left, j); 179 | left = i; 180 | } else { 181 | if (i < right) quickSort(map, i, right); 182 | right = j; 183 | } 184 | } while (left < right); 185 | } 186 | } 187 | 188 | class InternalIterableSorter extends IterableSorter { 189 | late TKey Function(TValue) keySelector; 190 | EqualityComparer? comparer; 191 | bool? descending; 192 | IterableSorter? next; 193 | late List keys; 194 | 195 | InternalIterableSorter( 196 | this.keySelector, 197 | this.comparer, 198 | this.next, { 199 | this.descending = false, 200 | }) { 201 | comparer ??= EqualityComparer.forType(); 202 | } 203 | 204 | @override 205 | void computeKeys(List elements, int count) { 206 | keys = List.generate(count, (i) => keySelector(elements[i])); 207 | if (next != null) next!.computeKeys(elements, count); 208 | } 209 | 210 | @override 211 | int compareKeys(int index1, int index2) { 212 | final c = comparer?.sort(keys[index1], keys[index2]) ?? -1; 213 | if (c == 0) { 214 | if (next == null) return index1 - index2; 215 | return next!.compareKeys(index1, index2); 216 | } 217 | return descending! ? -c : c; 218 | } 219 | } 220 | 221 | class OrderedBuffer extends Iterable { 222 | List data; 223 | List orderedMap; 224 | 225 | OrderedBuffer(this.data, this.orderedMap); 226 | 227 | @override 228 | Iterator get iterator => iterate().iterator; 229 | 230 | Iterable iterate() sync* { 231 | for (var index in orderedMap) { 232 | yield data[index]; 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /lib/src/exceptions/range_exception.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /// Represents a range exception. 15 | class RException { 16 | RException(this.message); 17 | 18 | RException.steps() : message = 'The range must be more then 0'; 19 | 20 | final String? message; 21 | 22 | @override 23 | String toString() { 24 | return 'RException: ${message ?? ''}'; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/files.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | -------------------------------------------------------------------------------- /lib/src/flutter/align.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import 'package:flutter/material.dart'; 14 | 15 | extension AlignExtensions on Widget { 16 | Align alignAtBottomCenter({ 17 | Key? key, 18 | double? heightFactor, 19 | double? widthFactor, 20 | }) => 21 | Align( 22 | key: key, 23 | child: this, 24 | alignment: Alignment.bottomCenter, 25 | heightFactor: heightFactor, 26 | widthFactor: widthFactor, 27 | ); 28 | 29 | Align alignAtTopLeft({ 30 | Key? key, 31 | double? heightFactor, 32 | double? widthFactor, 33 | }) => 34 | Align( 35 | key: key, 36 | child: this, 37 | alignment: Alignment.topLeft, 38 | heightFactor: heightFactor, 39 | widthFactor: widthFactor, 40 | ); 41 | 42 | Align alignAtBottomLeft({ 43 | Key? key, 44 | double? heightFactor, 45 | double? widthFactor, 46 | }) => 47 | Align( 48 | key: key, 49 | child: this, 50 | alignment: Alignment.bottomLeft, 51 | heightFactor: heightFactor, 52 | widthFactor: widthFactor, 53 | ); 54 | 55 | Align alignAtBottomRight({ 56 | Key? key, 57 | double? heightFactor, 58 | double? widthFactor, 59 | }) => 60 | Align( 61 | key: key, 62 | child: this, 63 | alignment: Alignment.bottomRight, 64 | heightFactor: heightFactor, 65 | widthFactor: widthFactor, 66 | ); 67 | 68 | Align alignAtCenterLeft({ 69 | Key? key, 70 | double? heightFactor, 71 | double? widthFactor, 72 | }) => 73 | Align( 74 | key: key, 75 | child: this, 76 | alignment: Alignment.centerLeft, 77 | heightFactor: heightFactor, 78 | widthFactor: widthFactor, 79 | ); 80 | 81 | Align alignAtCenter({ 82 | Key? key, 83 | double? heightFactor, 84 | double? widthFactor, 85 | }) => 86 | Align( 87 | key: key, 88 | child: this, 89 | alignment: Alignment.center, 90 | heightFactor: heightFactor, 91 | widthFactor: widthFactor, 92 | ); 93 | 94 | Align alignAtCenterRight({ 95 | Key? key, 96 | double? heightFactor, 97 | double? widthFactor, 98 | }) => 99 | Align( 100 | key: key, 101 | child: this, 102 | alignment: Alignment.centerRight, 103 | heightFactor: heightFactor, 104 | widthFactor: widthFactor, 105 | ); 106 | 107 | Align alignAtLERP( 108 | Alignment a, 109 | Alignment b, 110 | double t, { 111 | Key? key, 112 | double? heightFactor, 113 | double? widthFactor, 114 | }) => 115 | Align( 116 | key: key, 117 | child: this, 118 | alignment: Alignment.lerp(a, b, t)!, 119 | heightFactor: heightFactor, 120 | widthFactor: widthFactor, 121 | ); 122 | 123 | Align alignXY( 124 | double x, 125 | double y, { 126 | Key? key, 127 | double? heightFactor, 128 | double? widthFactor, 129 | }) => 130 | Align( 131 | key: key, 132 | child: this, 133 | alignment: Alignment(x, y), 134 | heightFactor: heightFactor, 135 | widthFactor: widthFactor, 136 | ); 137 | 138 | Align alignAtTopCenter({ 139 | Key? key, 140 | double? heightFactor, 141 | double? widthFactor, 142 | }) => 143 | Align( 144 | key: key, 145 | child: this, 146 | alignment: Alignment.topCenter, 147 | heightFactor: heightFactor, 148 | widthFactor: widthFactor, 149 | ); 150 | 151 | Align alignAtTopRight({ 152 | Key? key, 153 | double? heightFactor, 154 | double? widthFactor, 155 | }) => 156 | Align( 157 | key: key, 158 | child: this, 159 | alignment: Alignment.topRight, 160 | heightFactor: heightFactor, 161 | widthFactor: widthFactor, 162 | ); 163 | } 164 | -------------------------------------------------------------------------------- /lib/src/flutter/center.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:flutter/material.dart'; 15 | 16 | extension CenterExtension on Widget { 17 | Center toCenter() { 18 | return Center( 19 | child: this, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/flutter/container.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import 'package:flutter/material.dart'; 14 | 15 | extension ContainerExtensions on Container { 16 | /// Add round corners to a Container 17 | /// if the Container already has a color use [backgroundColor] instead and remove the 18 | /// [Color] from the Container it self 19 | Container withRoundCorners({required Color backgroundColor}) => Container( 20 | decoration: BoxDecoration( 21 | color: backgroundColor, 22 | borderRadius: BorderRadius.all( 23 | Radius.circular(25), 24 | ), 25 | ), 26 | child: this, 27 | ); 28 | 29 | /// A shadow cast by a box 30 | /// 31 | /// [shadowColor] 32 | Container withShadow( 33 | {Color shadowColor = Colors.grey, 34 | double blurRadius = 20.0, 35 | double spreadRadius = 1.0, 36 | Offset offset = const Offset(10.0, 10.0)}) => 37 | Container( 38 | decoration: BoxDecoration( 39 | boxShadow: [ 40 | BoxShadow(color: shadowColor, blurRadius: blurRadius, spreadRadius: spreadRadius, offset: offset), 41 | ], 42 | ), 43 | child: this, 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/flutter/context.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ContextExtensions on BuildContext { 4 | 5 | // Returns the MediaQuery 6 | MediaQueryData get mq => MediaQuery.of(this); 7 | 8 | /// Returns if Orientation is landscape 9 | bool get isLandscape => mq.orientation == Orientation.landscape; 10 | 11 | /// Returns same as MediaQuery.of(context).size 12 | Size get sizePx => mq.size; 13 | 14 | /// Returns same as MediaQuery.of(context).size.width 15 | double get widthPx => sizePx.width; 16 | 17 | /// Returns same as MediaQuery.of(context).height 18 | double get heightPx => sizePx.height; 19 | 20 | } -------------------------------------------------------------------------------- /lib/src/flutter/duration.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import '../date.dart'; 3 | extension DurationExt on Duration { 4 | /// Utility to delay some callback (or code execution). 5 | /// 6 | /// Sample: 7 | /// ``` 8 | /// await 3.seconds.delay(() { 9 | /// .... 10 | /// } 11 | /// 12 | ///``` 13 | Future delay([FutureOr callback()?]) async => Future.delayed(this, callback); 14 | 15 | static const int daysPerWeek = 7; 16 | static const int nanosecondsPerMicrosecond = 1000; 17 | 18 | /// Returns the representation in weeks 19 | int get inWeeks => (inDays / daysPerWeek).ceil(); 20 | 21 | /// Adds the Duration to the current DateTime and returns a DateTime in the future 22 | DateTime get fromNow => DateTime.now() + this; 23 | 24 | /// Subtracts the Duration from the current DateTime and returns a DateTime in the past 25 | DateTime get ago => DateTime.now() - this; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/flutter/gesture_detector.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:dart_extensions/src/flutter/transforms/click_translate.dart'; 15 | import 'package:flutter/material.dart'; 16 | 17 | extension GestureDetectorExtensions on Widget { 18 | Widget get onTapAddJumpEffect => TranslateOnClick(child: this); 19 | 20 | Widget onDoubleTap(Function() function) => GestureDetector( 21 | onDoubleTap: function, 22 | child: this, 23 | ); 24 | 25 | Widget onTap(Function() function) => GestureDetector( 26 | onTap: function, 27 | child: this, 28 | ); 29 | 30 | Widget onLongPress(Function() function) => GestureDetector( 31 | onLongPress: function, 32 | child: this, 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/flutter/icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension IconExtensions on T { 4 | Icon copyWith({ 5 | Color? color, 6 | double? size, 7 | String? semanticLabel, 8 | TextDirection? textDirection, 9 | }) { 10 | return Icon( 11 | this.icon, 12 | color: color ?? this.color, 13 | size: size ?? this.size, 14 | semanticLabel: semanticLabel ?? this.semanticLabel, 15 | textDirection: textDirection ?? this.textDirection, 16 | ); 17 | } 18 | 19 | T iconSize(double size) => this.copyWith(size: size) as T; 20 | 21 | T iconColor(Color color) => this.copyWith(color: color) as T; 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/flutter/list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ListExtensions on List { 4 | Widget toRowWidget({ 5 | Key? key, 6 | MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 7 | MainAxisSize mainAxisSize = MainAxisSize.max, 8 | CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, 9 | TextDirection? textDirection, 10 | VerticalDirection verticalDirection = VerticalDirection.down, 11 | TextBaseline? textBaseline, 12 | List children = const [], 13 | }) => 14 | Row( 15 | key: key, 16 | mainAxisAlignment: mainAxisAlignment, 17 | mainAxisSize: mainAxisSize, 18 | crossAxisAlignment: crossAxisAlignment, 19 | textDirection: textDirection, 20 | verticalDirection: verticalDirection, 21 | textBaseline: textBaseline, 22 | children: this, 23 | ); 24 | 25 | Widget toStackWidget({ 26 | Key? key, 27 | AlignmentGeometry alignment = AlignmentDirectional.topStart, 28 | TextDirection? textDirection, 29 | StackFit fit = StackFit.loose, 30 | Clip clip = Clip.hardEdge, 31 | List children = const [], 32 | }) => 33 | Stack( 34 | key: key, 35 | alignment: alignment, 36 | textDirection: textDirection, 37 | fit: fit, 38 | clipBehavior: clip, 39 | children: this, 40 | ); 41 | 42 | Widget toColumnWidget({ 43 | Key? key, 44 | MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 45 | MainAxisSize mainAxisSize = MainAxisSize.max, 46 | CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, 47 | TextDirection? textDirection, 48 | VerticalDirection verticalDirection = VerticalDirection.down, 49 | TextBaseline? textBaseline, 50 | List children = const [], 51 | }) => 52 | Column( 53 | key: key, 54 | mainAxisAlignment: mainAxisAlignment, 55 | mainAxisSize: mainAxisSize, 56 | crossAxisAlignment: crossAxisAlignment, 57 | textDirection: textDirection, 58 | verticalDirection: verticalDirection, 59 | textBaseline: textBaseline, 60 | children: this, 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/flutter/navigation.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:flutter/material.dart'; 15 | 16 | extension NavigationStateExtensions on State { 17 | /// Navigate to another widget 18 | Future navigateTo({required Route route}) => Navigator.push(this.context, route); 19 | 20 | /// Navigate to another widget and replace remove the current one 21 | Future navigatePushReplacement({required Route route}) => Navigator.pushReplacement(this.context, route); 22 | 23 | 24 | /// Navigate to widget by the route name 25 | Future navigateByRouteName(String routeName, {Object? args}) => 26 | Navigator.pushNamed(context, routeName, arguments: args); 27 | } 28 | 29 | extension NavigationStatelessExtensions on StatelessWidget { 30 | /// Navigate to another widget 31 | Future navigateTo({required BuildContext context, required Route route}) => Navigator.push(context, route); 32 | 33 | /// Navigate to another widget and replace remove the current one 34 | Future navigatePushReplacement({required BuildContext context,required Route route}) => Navigator.pushReplacement(context, route); 35 | 36 | /// Navigate to widget by the route name 37 | Future navigateByRouteName({required BuildContext context, required String routeName, Object? args}) => 38 | Navigator.pushNamed(context, routeName, arguments: args); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/flutter/padding.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:flutter/material.dart'; 15 | 16 | extension PaddingExtensions on Widget { 17 | Padding paddingAll(double value, {Key? key}) { 18 | return Padding( 19 | key: key, 20 | padding: EdgeInsets.all(value), 21 | child: this, 22 | ); 23 | } 24 | 25 | Padding paddingLTRB( 26 | double left, 27 | double top, 28 | double right, 29 | double bottom, { 30 | Key? key, 31 | }) => 32 | Padding( 33 | key: key, 34 | padding: EdgeInsets.fromLTRB(left, top, right, bottom), 35 | child: this, 36 | ); 37 | 38 | Padding paddingSymmetric({Key? key, double v = 0.0, double h = 0.0}) => Padding( 39 | key: key, 40 | padding: EdgeInsets.symmetric( 41 | vertical: v, 42 | horizontal: h, 43 | ), 44 | child: this, 45 | ); 46 | 47 | Padding paddingOnly({Key? key, double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => 48 | Padding( 49 | key: key, 50 | padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), 51 | child: this, 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/flutter/responsive.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class ResponsiveTools { 7 | /// Device's BoxConstraints 8 | static late BoxConstraints boxConstraints; 9 | 10 | static late Orientation orientation; 11 | 12 | static late DeviceType deviceType; 13 | 14 | static late double h; 15 | 16 | static late double w; 17 | 18 | static void setAppSize(BoxConstraints constraints, Orientation currentOrientation) { 19 | boxConstraints = constraints; 20 | orientation = currentOrientation; 21 | 22 | // Sets screen width and height 23 | if (orientation == Orientation.portrait) { 24 | w = boxConstraints.maxWidth; 25 | h = boxConstraints.maxHeight; 26 | } else { 27 | w = boxConstraints.maxHeight; 28 | h = boxConstraints.maxWidth; 29 | } 30 | 31 | // Sets ScreenType 32 | if (kIsWeb) { 33 | deviceType = DeviceType.web; 34 | } else if (Platform.isAndroid || Platform.isIOS) { 35 | if ((orientation == Orientation.portrait && w < 600) || (orientation == Orientation.landscape && h < 600)) { 36 | deviceType = DeviceType.mobile; 37 | } else { 38 | deviceType = DeviceType.tablet; 39 | } 40 | } else if (Platform.isMacOS) { 41 | deviceType = DeviceType.mac; 42 | } else if (Platform.isWindows) { 43 | deviceType = DeviceType.windows; 44 | } else if (Platform.isLinux) { 45 | deviceType = DeviceType.linux; 46 | } else { 47 | deviceType = DeviceType.fuchsia; 48 | } 49 | } 50 | } 51 | 52 | enum DeviceType { mobile, tablet, web, mac, windows, linux, fuchsia } 53 | 54 | class ResponsiveApp extends StatelessWidget { 55 | const ResponsiveApp({Key? key, required this.builder}) : super(key: key); 56 | final ResponsiveBuild builder; 57 | 58 | @override 59 | Widget build(BuildContext context) { 60 | return LayoutBuilder(builder: (context, constraints) { 61 | return OrientationBuilder(builder: (context, orientation) { 62 | ResponsiveTools.setAppSize(constraints, orientation); 63 | return builder(context, orientation, ResponsiveTools.deviceType); 64 | }); 65 | }); 66 | } 67 | } 68 | 69 | typedef ResponsiveBuild = Widget Function( 70 | BuildContext context, 71 | Orientation orientation, 72 | DeviceType deviceType, 73 | ); 74 | 75 | extension SizerExt on num { 76 | double get hResponsive => this * ResponsiveTools.h / 100; 77 | double get wResponsive => this * ResponsiveTools.w / 100; 78 | double get textSizeResponsive => this * (ResponsiveTools.w / 3) / 100; 79 | } 80 | -------------------------------------------------------------------------------- /lib/src/flutter/sized_box.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | /// convert Sizedbox height/width to percents. 5 | extension SizedBoxExt on SizedBox { 6 | SizedBox toPercents(BuildContext context) { 7 | final size = MediaQuery.of(context).size; 8 | if (this.width != null) return SizedBox(width: (this.width ?? 0 / size.width) * 1000); 9 | if (this.height != null) return SizedBox(height: (this.height ?? 0 / size.height) * 1000); 10 | return SizedBox.shrink(); 11 | } 12 | } -------------------------------------------------------------------------------- /lib/src/flutter/sliver.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// [Widget] extensions to make it easier to work with [Sliver] widgets. 4 | extension WidgetSliver on Widget { 5 | Widget get sliver { 6 | return SliverToBoxAdapter( 7 | child: this, 8 | ); 9 | } 10 | 11 | /// Wraps the widget in a [SliverPadding] widget. 12 | Widget sliverPadding(EdgeInsetsGeometry padding) { 13 | return SliverPadding( 14 | padding: padding, 15 | sliver: this, 16 | ); 17 | } 18 | 19 | /// Wraps the widget in a [SliverVisibility] widget. 20 | Widget sliverVisibility(bool isVisible) { 21 | return isVisible ? this : SliverToBoxAdapter(); 22 | } 23 | 24 | /// Wraps the widget in a [SliverOpacity] widget. 25 | Widget sliverOpacity(double opacity) { 26 | return SliverOpacity( 27 | opacity: opacity, 28 | sliver: this, 29 | ); 30 | } 31 | 32 | /// Wraps the widget in a [SliverAppBar] widget. 33 | Widget sliverFittedBox({BoxFit fit = BoxFit.contain}) { 34 | return SliverToBoxAdapter( 35 | child: FittedBox( 36 | fit: fit, 37 | child: this, 38 | ), 39 | ); 40 | } 41 | 42 | /// Wraps the widget in a [SliverAppBar] widget. 43 | Widget sliverExpander() { 44 | return SliverFillRemaining( 45 | hasScrollBody: false, 46 | child: this, 47 | ); 48 | } 49 | 50 | /// Wraps the widget in a [SliverAppBar] widget. 51 | Widget sliverCenter() { 52 | return SliverFillRemaining( 53 | hasScrollBody: false, 54 | child: Center(child: this), 55 | ); 56 | } 57 | 58 | /// Wraps the widget in a [SliverAppBar] widget. 59 | Widget sliverAspectRatio(double aspectRatio) { 60 | return SliverToBoxAdapter( 61 | child: AspectRatio( 62 | aspectRatio: aspectRatio, 63 | child: this, 64 | ), 65 | ); 66 | } 67 | 68 | /// Wraps the widget in a [SliverAppBar] widget. 69 | Widget sliverClipRRect(BorderRadius borderRadius) { 70 | return SliverToBoxAdapter( 71 | child: ClipRRect( 72 | borderRadius: borderRadius, 73 | child: this, 74 | ), 75 | ); 76 | } 77 | 78 | /// Wraps the widget in a [SliverAppBar] widget. 79 | Widget sliverPhysicalModel({ 80 | required Color color, 81 | required double elevation, 82 | BoxShape shape = BoxShape.rectangle, 83 | Clip clipBehavior = Clip.none, 84 | }) { 85 | return SliverToBoxAdapter( 86 | child: PhysicalModel( 87 | color: color, 88 | elevation: elevation, 89 | shape: shape, 90 | clipBehavior: clipBehavior, 91 | child: this, 92 | ), 93 | ); 94 | } 95 | 96 | /// Wraps the widget in a [SliverAppBar] widget. 97 | Widget sliverFlexibleSpaceBar({String? title, bool centerTitle = true}) { 98 | return SliverAppBar( 99 | expandedHeight: 200.0, 100 | floating: false, 101 | pinned: true, 102 | flexibleSpace: FlexibleSpaceBar( 103 | centerTitle: centerTitle, 104 | title: title != null ? Text(title) : null, 105 | background: this, 106 | ), 107 | ); 108 | } 109 | 110 | /// Wraps the widget in a [SliverAppBar] widget. 111 | Widget sliverOffstage(bool offstage) { 112 | return SliverToBoxAdapter( 113 | child: Offstage( 114 | offstage: offstage, 115 | child: this, 116 | ), 117 | ); 118 | } 119 | 120 | /// Wraps the widget in a [SliverAppBar] widget. 121 | Widget sliverAnimatedSwitcher( 122 | {Duration duration = const Duration(milliseconds: 250)}) { 123 | return SliverToBoxAdapter( 124 | child: AnimatedSwitcher( 125 | duration: duration, 126 | child: this, 127 | ), 128 | ); 129 | } 130 | 131 | /// Wraps the widget in a [SliverAppBar] widget. 132 | Widget sliverFadeTransition({required Animation animation}) { 133 | return SliverToBoxAdapter( 134 | child: FadeTransition( 135 | opacity: animation, 136 | child: this, 137 | ), 138 | ); 139 | } 140 | 141 | /// Wraps the widget in a [SliverAppBar] widget. 142 | Widget sliverRotatedBox({int quarterTurns = 1}) { 143 | return SliverToBoxAdapter( 144 | child: RotatedBox( 145 | quarterTurns: quarterTurns, 146 | child: this, 147 | ), 148 | ); 149 | } 150 | 151 | /// Wraps the widget in a [SliverAppBar] widget. 152 | Widget sliverPositionedTransition( 153 | {required Animation animation}) { 154 | return SliverToBoxAdapter( 155 | child: PositionedTransition( 156 | rect: animation, 157 | child: this, 158 | ), 159 | ); 160 | } 161 | 162 | /// Wraps the widget in a [SliverAppBar] widget. 163 | Widget sliverScaleTransition({required Animation scale}) { 164 | return SliverToBoxAdapter( 165 | child: ScaleTransition( 166 | scale: scale, 167 | child: this, 168 | ), 169 | ); 170 | } 171 | 172 | /// Wraps the widget in a [SliverAppBar] widget. 173 | Widget sliverSizeTransition( 174 | {required Animation sizeFactor, Axis axis = Axis.vertical}) { 175 | return SliverToBoxAdapter( 176 | child: SizeTransition( 177 | sizeFactor: sizeFactor, 178 | axis: axis, 179 | child: this, 180 | ), 181 | ); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /lib/src/flutter/text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension StyledText on T { 4 | Text copyWith({ 5 | String? data, 6 | TextStyle? style, 7 | StrutStyle? strutStyle, 8 | TextAlign? textAlign, 9 | TextDirection? textDirection, 10 | Locale? locale, 11 | bool? softWrap, 12 | TextOverflow? overflow, 13 | double? textScaleFactor, 14 | int? maxLines, 15 | String? semanticsLabel, 16 | TextWidthBasis? textWidthBasis, 17 | }) => 18 | Text( 19 | data ?? this.data ?? "", 20 | style: style ?? this.style, 21 | strutStyle: strutStyle ?? this.strutStyle, 22 | textAlign: textAlign ?? this.textAlign, 23 | locale: locale ?? this.locale, 24 | maxLines: maxLines ?? this.maxLines, 25 | overflow: overflow ?? this.overflow, 26 | semanticsLabel: semanticsLabel ?? this.semanticsLabel, 27 | softWrap: softWrap ?? this.softWrap, 28 | textDirection: textDirection ?? this.textDirection, 29 | textScaler: TextScaler.linear(textScaleFactor ?? 0), 30 | textWidthBasis: textWidthBasis ?? this.textWidthBasis, 31 | ); 32 | 33 | T textStyle(TextStyle? style) => this.copyWith( 34 | style: (this.style ?? TextStyle()).copyWith( 35 | background: style?.background, 36 | backgroundColor: style?.backgroundColor, 37 | color: style?.color, 38 | debugLabel: style?.debugLabel, 39 | decoration: style?.decoration, 40 | decorationColor: style?.decorationColor, 41 | decorationStyle: style?.decorationStyle, 42 | decorationThickness: style?.decorationThickness, 43 | fontFamily: style?.fontFamily, 44 | fontFamilyFallback: style?.fontFamilyFallback, 45 | fontFeatures: style?.fontFeatures, 46 | fontSize: style?.fontSize, 47 | fontStyle: style?.fontStyle, 48 | fontWeight: style?.fontWeight, 49 | foreground: style?.foreground, 50 | height: style?.height, 51 | inherit: style?.inherit, 52 | letterSpacing: style?.letterSpacing, 53 | locale: style?.locale, 54 | shadows: style?.shadows, 55 | textBaseline: style?.textBaseline, 56 | wordSpacing: style?.wordSpacing, 57 | ), 58 | ) as T; 59 | 60 | T textScale(double scaleFactor) => 61 | this.copyWith(textScaleFactor: scaleFactor) as T; 62 | 63 | T bold() => this.copyWith( 64 | style: (this.style ?? TextStyle()).copyWith( 65 | fontWeight: FontWeight.bold, 66 | ), 67 | ) as T; 68 | 69 | T italic() => this.copyWith( 70 | style: (this.style ?? TextStyle()).copyWith( 71 | fontStyle: FontStyle.italic, 72 | ), 73 | ) as T; 74 | 75 | T fontWeight(FontWeight fontWeight) => this.copyWith( 76 | style: (this.style ?? TextStyle()).copyWith( 77 | fontWeight: fontWeight, 78 | ), 79 | ) as T; 80 | 81 | T fontSize(double size) => this.copyWith( 82 | style: (this.style ?? TextStyle()).copyWith( 83 | fontSize: size, 84 | ), 85 | ) as T; 86 | 87 | T fontFamily(String font) => this.copyWith( 88 | style: (this.style ?? TextStyle()).copyWith( 89 | fontFamily: font, 90 | ), 91 | ) as T; 92 | 93 | T letterSpacing(double space) => this.copyWith( 94 | style: (this.style ?? TextStyle()).copyWith( 95 | letterSpacing: space, 96 | ), 97 | ) as T; 98 | 99 | T wordSpacing(double space) => this.copyWith( 100 | style: (this.style ?? TextStyle()).copyWith( 101 | wordSpacing: space, 102 | ), 103 | ) as T; 104 | 105 | T textShadow({ 106 | Color color = const Color(0x34000000), 107 | double blurRadius = 0.0, 108 | Offset offset = Offset.zero, 109 | }) => 110 | this.copyWith( 111 | style: (this.style ?? TextStyle()).copyWith( 112 | shadows: [ 113 | Shadow( 114 | color: color, 115 | blurRadius: blurRadius, 116 | offset: offset, 117 | ), 118 | ], 119 | ), 120 | ) as T; 121 | 122 | T textColor(Color color) => this.copyWith( 123 | style: (this.style ?? TextStyle()).copyWith( 124 | color: color, 125 | ), 126 | ) as T; 127 | 128 | T textAlignment(TextAlign align) => this.copyWith(textAlign: align) as T; 129 | 130 | T textDirection(TextDirection direction) => 131 | this.copyWith(textDirection: direction) as T; 132 | 133 | T textBaseline(TextBaseline textBaseline) => this.copyWith( 134 | style: (this.style ?? TextStyle()).copyWith( 135 | textBaseline: textBaseline, 136 | ), 137 | ) as T; 138 | 139 | T textWidthBasis(TextWidthBasis textWidthBasis) => 140 | this.copyWith(textWidthBasis: textWidthBasis) as T; 141 | 142 | T withUnderLine() => this.copyWith( 143 | style: (this.style ?? TextStyle()) 144 | .copyWith(decoration: TextDecoration.underline)) as T; 145 | } 146 | -------------------------------------------------------------------------------- /lib/src/flutter/transforms.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:dart_extensions/src/flutter/transforms/click_translate.dart'; 15 | import 'package:flutter/material.dart'; 16 | 17 | extension TransformExtensions on Widget { 18 | Widget get pushEffectOnClick => TranslateOnClick(child: this); 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/flutter/transforms/click_translate.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import 'package:flutter/material.dart'; 14 | 15 | class TranslateOnClick extends StatefulWidget { 16 | final Widget child; 17 | 18 | const TranslateOnClick({Key? key,required this.child}) : super(key: key); 19 | 20 | @override 21 | _TranslateOnClickState createState() => _TranslateOnClickState(); 22 | } 23 | 24 | class _TranslateOnClickState extends State { 25 | final nonClickTransform = Matrix4.identity(); 26 | final clickTransform = Matrix4.identity()..translate(0, -10, 0); 27 | 28 | bool _clicking = false; 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return GestureDetector( 33 | onTapDown: (d) => _userClick(true), 34 | onTapUp: (d) => _userClick(false), 35 | child: AnimatedContainer( 36 | duration: Duration(milliseconds: 200), 37 | child: widget.child, 38 | transform: _clicking ? clickTransform : nonClickTransform, 39 | ), 40 | ); 41 | } 42 | 43 | void _userClick(bool click) { 44 | setState(() { 45 | _clicking = click; 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/flutter/widgets.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:flutter/material.dart'; 15 | 16 | extension WidgetsExtension on Widget { 17 | Widget withTooltip(String message, 18 | {Decoration? decoration, 19 | double? height, 20 | bool? preferBelow, 21 | EdgeInsetsGeometry? padding, 22 | TextStyle? textStyle, 23 | Duration? waitDuration, 24 | EdgeInsetsGeometry? margin}) => 25 | Tooltip( 26 | message: message, 27 | decoration: decoration, 28 | height: height, 29 | padding: padding, 30 | preferBelow: preferBelow, 31 | textStyle: textStyle, 32 | waitDuration: waitDuration, 33 | margin: margin, 34 | child: this, 35 | ); 36 | } -------------------------------------------------------------------------------- /lib/src/http.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'dart:convert' as convert; 15 | import '../src/string_ext.dart'; 16 | import 'package:http/http.dart' as http; 17 | 18 | const _defaultHeaders = {"Content-type": "application/json"}; 19 | 20 | extension HttpExtensions on String { 21 | /// Sends an HTTP GET request with the given headers to the given URL, which can 22 | /// be a [Uri] or a [String]. 23 | /// [endPoint] - end point of current url 24 | /// example: 25 | /// current string is www.mydomain.com 26 | /// endpoint param - user 27 | /// result request -> www.mydomain.com/user 28 | Future httpGet(String endPoint) async { 29 | if (this.isEmptyOrNull) return; 30 | 31 | try { 32 | final response = await http.get(Uri.http(this, endPoint)); 33 | return response.statusCode == 200 34 | ? convert.jsonDecode(response.body) 35 | : print('Request failed with status: ${response.statusCode}.'); 36 | } on Exception catch (e) { 37 | return Future.error(e); 38 | } 39 | } 40 | 41 | /// Sends an HTTP POST request with the given headers and body to the given URL, 42 | /// which can be a [Uri] or a [String]. 43 | /// [endPoint] - end point of current url 44 | /// example: 45 | /// current string is www.mydomain.com 46 | /// endpoint param - user 47 | /// result request -> www.mydomain.com/user 48 | Future httpPost(String endPoint, String json, [Map headers = _defaultHeaders]) async { 49 | if (this.isEmptyOrNull) return; 50 | 51 | try { 52 | final response = await http.post(Uri.http(this, endPoint), headers: headers, body: json); 53 | return response.statusCode == 200 54 | ? convert.jsonDecode(response.body) 55 | : print('Request failed with status: ${response.statusCode}.'); 56 | } on Exception catch (e) { 57 | return Future.error(e); 58 | } 59 | } 60 | 61 | /// Sends an HTTP PUT request with the given headers and body to the given URL, 62 | /// which can be a [Uri] or a [String]. 63 | /// [endPoint] - end point of current url 64 | /// example: 65 | /// current string is www.mydomain.com 66 | /// endpoint param - user 67 | /// result request -> www.mydomain.com/user 68 | Future httpPut(String endPoint, String json, [Map headers = _defaultHeaders]) async { 69 | if (this.isEmptyOrNull) return; 70 | 71 | try { 72 | final response = await http.put(Uri.http(this, endPoint), headers: headers, body: json); 73 | return response.statusCode == 200 74 | ? convert.jsonDecode(response.body) 75 | : print('Request failed with status: ${response.statusCode}.'); 76 | } on Exception catch (e) { 77 | return Future.error(e); 78 | } 79 | } 80 | 81 | /// Sends an HTTP DELETE request with the given headers to the given URL, which 82 | /// can be a [Uri] or a [String]. 83 | /// [endPoint] - end point of current url 84 | /// example: 85 | /// current string is www.mydomain.com 86 | /// endpoint param - user 87 | /// result request -> www.mydomain.com/user 88 | Future httpDelete(String endPoint, {Map? headers}) async { 89 | if (this.isEmptyOrNull) return; 90 | 91 | try { 92 | final response = await http.delete(Uri.http(this, endPoint), headers: headers); 93 | return response.statusCode == 200 94 | ? convert.jsonDecode(response.body) 95 | : print('Request failed with status: ${response.statusCode}.'); 96 | } on Exception catch (e) { 97 | return Future.error(e); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/src/int.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | extension IntExtensions on int { 14 | /// Return the min if this number is smaller then minimum 15 | /// Return the max if this number is bigger the the maximum 16 | /// Return this number if it's between the range 17 | int inRangeOf(int min, int max) { 18 | if (min.isNull || max.isNull) throw Exception('min or max cannot be null'); 19 | if (min > max) throw ArgumentError('min must be smaller the max'); 20 | 21 | if (this < min) return min; 22 | if (this > max) return max; 23 | return this; 24 | } 25 | 26 | // ignore: unnecessary_null_comparison 27 | bool get isNull => (this == null); 28 | 29 | /// Returns the absolute value 30 | get absolute => abs(); 31 | 32 | /// Returns number of digits in this number 33 | int get numberOfDigits => toString().length; 34 | 35 | /// Returns if the number is even 36 | bool get isEven => this % 2 == 0; 37 | 38 | /// Returns if the number is odd 39 | bool get isOdd => this % 2 != 0; 40 | 41 | /// Returns if the number is positive 42 | bool get isPositive => this > 0; 43 | 44 | /// Returns if the number is negative 45 | bool get isNegative => this < 0; 46 | 47 | /// Returns tenth of the number 48 | double get tenth => this / 10; 49 | 50 | /// Returns fourth of the number 51 | double get fourth => this / 4; 52 | 53 | /// Returns third of the number 54 | double get third => this / 3; 55 | 56 | /// Returns half of the number 57 | double get half => this / 2; 58 | 59 | /// Return this number time two 60 | int get doubled => this * 2; 61 | 62 | /// Return this number time three 63 | int get tripled => this * 3; 64 | 65 | /// Return this number time four 66 | int get quadrupled => this * 4; 67 | 68 | /// Return squared number 69 | int get squared => this * this; 70 | 71 | /// Convert this integer into boolean. 72 | /// 73 | /// Returns `true` if this integer is greater than *0*. 74 | bool get asBool => this > 0; 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/iterable.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import 'dart:collection'; 14 | import 'dart:math'; 15 | 16 | import 'package:quiver/iterables.dart'; 17 | 18 | import 'data_stractures/stack.dart'; 19 | import 'equality.dart'; 20 | 21 | typedef IndexedPredicate = bool Function(int index, T); 22 | 23 | extension CollectionsNullableExtensions on Iterable? { 24 | /// Returns this Iterable if it's not `null` and the empty list otherwise. 25 | Iterable orEmpty() => this ?? []; 26 | 27 | ///Returns `true` if this nullable iterable is either null or empty. 28 | bool get isEmptyOrNull => (this?.isEmpty ?? true); 29 | 30 | /// Returns `true` if at least one element matches the given [predicate]. 31 | bool any(bool predicate(T element)) { 32 | if (this.isEmptyOrNull) return false; 33 | for (final element in this.orEmpty()) if (predicate(element)) return true; 34 | return false; 35 | } 36 | 37 | /// Return a list concatenates the output of the current list and another [iterable] 38 | List concatWithSingleList(Iterable iterable) { 39 | if (isEmptyOrNull || iterable.isEmptyOrNull) return []; 40 | 41 | return [...this.orEmpty(), ...iterable]; 42 | } 43 | 44 | /// Return a list concatenates the output of the current list and multiple [iterables] 45 | List concatWithMultipleList(List> iterables) { 46 | if (isEmptyOrNull || iterables.isEmptyOrNull) return []; 47 | final list = iterables.toList(growable: false).expand((i) => i); 48 | return [...this.orEmpty(), ...list]; 49 | } 50 | 51 | /// Zip is used to combine multiple iterables into a single list that contains 52 | /// the combination of them two. 53 | zip(Iterable iterable) sync* { 54 | if (iterable.isEmptyOrNull) return; 55 | final iterables = List.empty() 56 | ..add(this.orEmpty()) 57 | ..add(iterable); 58 | 59 | final iterators = iterables.map((e) => e.iterator).toList(growable: false); 60 | while (iterators.every((e) => e.moveNext())) { 61 | yield iterators.map((e) => e.current).toList(growable: false); 62 | } 63 | } 64 | } 65 | 66 | extension CollectionsExtensions on Iterable { 67 | ///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. 68 | Iterable sortBy( 69 | TKey Function(T) keySelector, { 70 | required EqualityComparer keyComparer, 71 | }) { 72 | return InternalOrderedIterable(this, keySelector, keyComparer, false); 73 | } 74 | 75 | /// Convert iterable to set 76 | Set toMutableSet() => Set.from(this); 77 | 78 | /// Returns a set containing all elements that are contained 79 | /// by both this set and the specified collection. 80 | Set intersect(Iterable other) { 81 | final set = this.toMutableSet(); 82 | set.addAll(other); 83 | return set; 84 | } 85 | 86 | /// Groups the elements in values by the value returned by key. 87 | /// 88 | /// Returns a map from keys computed by key to a list of all values for which 89 | /// key returns that key. The values appear in the list in the same 90 | /// relative order as in values. 91 | Map> groupBy(K key(T e)) { 92 | var map = >{}; 93 | 94 | for (final element in this) { 95 | var list = map.putIfAbsent(key(element as T), () => []); 96 | list.add(element); 97 | } 98 | return map; 99 | } 100 | 101 | /// Returns a list containing only elements matching the given [predicate]. 102 | List filter(bool test(T element)) { 103 | final result = []; 104 | forEach((e) { 105 | if (e != null && test(e)) { 106 | result.add(e); 107 | } 108 | }); 109 | return result; 110 | } 111 | 112 | /// Returns a list containing all elements not matching the given [predicate] and will filter nulls as well. 113 | List filterNot(bool test(T element)) { 114 | final result = []; 115 | forEach((e) { 116 | if (e != null && !test(e)) { 117 | result.add(e); 118 | } 119 | }); 120 | return result; 121 | } 122 | 123 | // return the half size of a list 124 | int get halfLength => (this.length / 2).floor(); 125 | 126 | /// Returns a list containing first [n] elements. 127 | List takeOnly(int n) { 128 | if (n == 0) return []; 129 | 130 | var list = List.empty(); 131 | var thisList = this.toList(); 132 | final resultSize = this.length - n; 133 | if (resultSize <= 0) return []; 134 | if (resultSize == 1) return [this.last]; 135 | 136 | List.generate(n, (index) { 137 | list.add(thisList[index]); 138 | }); 139 | return list; 140 | } 141 | 142 | /// Returns a list containing all elements except first [n] elements. 143 | List drop(int n) { 144 | if (n == 0) return []; 145 | 146 | var list = List.empty(); 147 | var originalList = this.toList(); 148 | final resultSize = this.length - n; 149 | if (resultSize <= 0) return []; 150 | if (resultSize == 1) return [this.last]; 151 | 152 | originalList.removeRange(0, n); 153 | 154 | originalList.forEach((element) => list.add(element)); 155 | return list; 156 | } 157 | 158 | // Retuns map operation as a List 159 | List mapList(E f(T e)) => this.map(f).toList(); 160 | 161 | // Takes the first half of a list 162 | List firstHalf() => take(halfLength).toList(); 163 | 164 | // Takes the second half of a list 165 | List secondHalf() => drop(halfLength).toList(); 166 | 167 | /// returns a list with two swapped items 168 | /// [i] first item 169 | /// [j] second item 170 | List swap(int i, int j) { 171 | final list = this.toList(); 172 | final aux = list[i]; 173 | list[i] = list[j]; 174 | list[j] = aux; 175 | return list; 176 | } 177 | 178 | T getRandom() { 179 | Random generator = Random(); 180 | final index = generator.nextInt(this.length); 181 | return this.toList()[index]; 182 | } 183 | 184 | /// get the first element return null 185 | T? get firstOrNull => _elementAtOrNull(0); 186 | 187 | /// get the last element if the list is not empty or return null 188 | T? get lastOrNull => isNotEmpty ? last : null; 189 | 190 | T lastOrDefault(T defaultValue) => lastOrNull ?? defaultValue; 191 | 192 | T? firstOrNullWhere(bool predicate(T element)) { 193 | for (T element in this) { 194 | if (predicate(element)) return element; 195 | } 196 | return null; 197 | } 198 | 199 | /// get the first element or provider default 200 | /// example: 201 | /// var name = [danny, ronny, james].firstOrDefault["jack"]; // danny 202 | /// var name = [].firstOrDefault["jack"]; // jack 203 | T firstOrDefault(T defaultValue) => firstOrNull ?? defaultValue; 204 | 205 | /// Will retrun new [Iterable] with all elements that satisfy the predicate [predicate], 206 | Iterable whereIndexed(IndexedPredicate predicate) => 207 | _IndexedWhereIterable(this, predicate); 208 | 209 | /// 210 | /// Performs the given action on each element on iterable, providing sequential index with the element. 211 | /// [item] the element on the current iteration 212 | /// [index] the index of the current iteration 213 | /// 214 | /// example: 215 | /// ["a","b","c"].forEachIndexed((element, index) { 216 | /// print("$element, $index"); 217 | /// }); 218 | /// result: 219 | /// a, 0 220 | /// b, 1 221 | /// c, 2 222 | void forEachIndexed(void action(T element, int index)) { 223 | var index = 0; 224 | for (var element in this) { 225 | action(element, index++); 226 | } 227 | } 228 | 229 | /// Returns a new list with all elements sorted according to descending 230 | /// natural sort order. 231 | List sortedDescending() { 232 | var list = toList(); 233 | list.sort((a, b) => -(a as Comparable).compareTo(b)); 234 | return list; 235 | } 236 | 237 | /// Checks if all elements in the specified [collection] are contained in 238 | /// this collection. 239 | bool containsAll(Iterable collection) { 240 | for (var element in collection) { 241 | if (!contains(element)) return false; 242 | } 243 | return true; 244 | } 245 | 246 | /// Return a number of the existing elements by a specific predicate 247 | /// example: 248 | /// final aboveTwenty = [ 249 | /// User(33, "chicko"), 250 | /// User(45, "ronit"), 251 | /// User(19, "amsalam"), 252 | /// ].count((user) => user.age > 20); // 2 253 | int count([bool predicate(T element)?]) { 254 | var count = 0; 255 | if (predicate == null) { 256 | return length; 257 | } else { 258 | for (var current in this) { 259 | if (predicate(current)) { 260 | count++; 261 | } 262 | } 263 | } 264 | 265 | return count; 266 | } 267 | 268 | /// Returns `true` if all elements match the given predicate. 269 | /// Example: 270 | /// [5, 19, 2].all(isEven), isFalse) 271 | /// [6, 12, 2].all(isEven), isTrue) 272 | bool all(bool predicate(T pred)?) { 273 | for (var e in this) { 274 | if (!predicate!(e)) return false; 275 | } 276 | return true; 277 | } 278 | 279 | /// Returns a list containing only the elements from given collection having distinct keys. 280 | /// 281 | /// Basically it's just like distinct function but with a predicate 282 | /// example: 283 | /// [ 284 | /// User(22, "Sasha"), 285 | /// User(23, "Mika"), 286 | /// User(23, "Miryam"), 287 | /// User(30, "Josh"), 288 | /// User(36, "Ran"), 289 | /// ].distinctBy((u) => u.age).forEach((user) { 290 | /// print("${user.age} ${user.name}"); 291 | /// }); 292 | /// 293 | /// result: 294 | /// 22 Sasha 295 | /// 23 Mika 296 | /// 30 Josh 297 | /// 36 Ran 298 | List distinctBy(predicate(T selector)) { 299 | final set = HashSet(); 300 | final List list = []; 301 | toList().forEach((e) { 302 | final key = predicate(e); 303 | if (set.add(key)) { 304 | list.add(e); 305 | } 306 | }); 307 | 308 | return list; 309 | } 310 | 311 | // get an element at specific index or return null 312 | T? _elementAtOrNull(int index) { 313 | return _elementOrNull(index, (_) => null); 314 | } 315 | 316 | _elementOrNull(int index, T? defaultElement(int index)) { 317 | // if our index is smaller then 0 return the default 318 | if (index < 0) return defaultElement(index); 319 | 320 | var counter = 0; 321 | for (var element in this) { 322 | if (index == counter++) { 323 | return element; 324 | } 325 | } 326 | 327 | return defaultElement(index); 328 | } 329 | 330 | /// Returns a set containing all elements that are contained by this collection 331 | /// and not contained by the specified collection. 332 | /// The returned set preserves the element iteration order of the original collection. 333 | /// 334 | /// example: 335 | /// 336 | /// [1,2,3,4,5,6].subtract([4,5,6]) 337 | /// 338 | /// result: 339 | /// 1,2,3 340 | subtract(Iterable other) { 341 | final set = toSet(); 342 | set.removeAll(other); 343 | return set; 344 | } 345 | 346 | /// will convert iterable into a Stack data structure 347 | /// example: 348 | /// [1,2,3,4].toStack() 349 | /// stack.pop() 350 | /// stack.push(5) 351 | /// 352 | StackX toStack() { 353 | final stack = StackX(); 354 | stack.addAll(this); 355 | return stack; 356 | } 357 | 358 | /// Splits the Iterable into chunks of the specified size 359 | /// 360 | /// example: 361 | /// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].chunks(3)) 362 | /// result: 363 | /// ([1, 2, 3], [4, 5, 6], [7, 8, 9], [10]) 364 | Iterable> chunks(int size) => partition(this, size); 365 | 366 | /// Creates a Map instance in which the keys and values are computed from the iterable. 367 | Map associate(key(element), value(element)) => 368 | Map.fromIterable(this, key: key, value: value); 369 | 370 | /// Returns the first element matching the given [predicate], or `null` 371 | /// if element was not found. 372 | T? find(predicate(T selector)) { 373 | for (final element in this) { 374 | if (predicate(element)) { 375 | return element; 376 | } 377 | } 378 | 379 | return null; 380 | } 381 | } 382 | 383 | // A lazy [Iterable] skip elements do **NOT** match the predicate [_f]. 384 | class _IndexedWhereIterable extends Iterable { 385 | final Iterable _iterable; 386 | final IndexedPredicate _f; 387 | 388 | _IndexedWhereIterable(this._iterable, this._f); 389 | 390 | @override 391 | Iterator get iterator => _IndexedWhereIterator(_iterable.iterator, _f); 392 | } 393 | 394 | /// [Iterator] for [_IndexedWhereIterable] 395 | class _IndexedWhereIterator implements Iterator { 396 | final Iterator _iterator; 397 | final IndexedPredicate _f; 398 | int _index = 0; 399 | 400 | _IndexedWhereIterator(this._iterator, this._f); 401 | 402 | @override 403 | bool moveNext() { 404 | while (_iterator.moveNext()) { 405 | if (_f(_index++, _iterator.current)) { 406 | return true; 407 | } 408 | } 409 | return false; 410 | } 411 | 412 | @override 413 | E get current => _iterator.current; 414 | } 415 | -------------------------------------------------------------------------------- /lib/src/map.dart: -------------------------------------------------------------------------------- 1 | extension MapExt on Map { 2 | /// Flatten a nested Map into a single level map 3 | /// 4 | /// If you don't want to flatten arrays (with 0, 1,... indexes), 5 | /// use [safe] mode. 6 | /// 7 | /// To avoid circular reference issues or huge calculations, 8 | /// you can specify the [maxDepth] the function will traverse. 9 | Map flatJson({ 10 | String delimiter = ".", 11 | bool safe = false, 12 | int? maxDepth, 13 | }) { 14 | final result = {}; 15 | 16 | void step( 17 | Map obj, [ 18 | String? previousKey, 19 | int currentDepth = 1, 20 | ]) { 21 | obj.forEach((key, value) { 22 | final newKey = previousKey != null ? "$previousKey$delimiter$key" : key; 23 | 24 | if (maxDepth != null && currentDepth >= maxDepth) { 25 | result[newKey] = value; 26 | return; 27 | } 28 | if (value is Map) { 29 | return step(value, newKey, currentDepth + 1); 30 | } 31 | if (value is List && !safe) { 32 | return step( 33 | _listToMap(value as List), 34 | newKey, 35 | currentDepth + 1, 36 | ); 37 | } 38 | result[newKey] = value; 39 | }); 40 | } 41 | 42 | step(this); 43 | 44 | return result; 45 | } 46 | 47 | Map _listToMap(List list) => 48 | list.asMap().map((key, value) => MapEntry(key.toString(), value)); 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/model/user.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | class User { 15 | final int age; 16 | final String name; 17 | 18 | User(this.age,this.name); 19 | 20 | @override 21 | String toString() { 22 | return "$age, $name"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/ranges.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'exceptions/range_exception.dart'; 15 | 16 | extension RangeExtensions on int { 17 | /// Returns a sequence of integer, starting from [this], 18 | /// increments by [step] and ends at [end] 19 | Iterable until(int end, {int step = 1}) sync* { 20 | if (step == 0) { 21 | throw RException.steps(); 22 | } 23 | 24 | int currentNumber = this; 25 | 26 | if (step > 0) { 27 | while (currentNumber < end) { 28 | yield currentNumber; 29 | currentNumber += step; 30 | } 31 | } else { 32 | while (currentNumber > end) { 33 | yield currentNumber; 34 | currentNumber += step; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/search_algorithms.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | -------------------------------------------------------------------------------- /lib/src/sort_algorithms.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | extension SearchAlgorithmExtensions on List { 14 | List quickSort([int? left, int? right]) { 15 | var list = this; 16 | if ((left??0) >= (right??0)) return [-1]; 17 | int pivot = left!, i = left, j = right!, direction = -1, temp = 0; 18 | while (i < j) { 19 | if (direction == -1) { 20 | if (list[j] < list[pivot]) { 21 | temp = list[j]; 22 | list[j] = list[pivot]; 23 | list[pivot] = temp; 24 | pivot = j; 25 | direction = 1; 26 | } else 27 | j--; 28 | } 29 | if (direction == 1) { 30 | if (list[i] > list[pivot]) { 31 | temp = list[i]; 32 | list[i] = list[pivot]; 33 | list[pivot] = temp; 34 | pivot = i; 35 | direction = -1; 36 | } else 37 | i++; 38 | } 39 | } 40 | quickSort(left, i); 41 | quickSort(i + 1, right); 42 | 43 | return list; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/string_ext.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:flutter/material.dart'; 15 | import 'package:intl/intl.dart'; 16 | 17 | enum Gender { male, female } 18 | 19 | class Message { 20 | final String male, female, other; 21 | 22 | Message({required this.male, required this.female, required this.other}) 23 | : assert(male.isEmptyOrNull), 24 | assert(female.isEmptyOrNull), 25 | assert(other.isEmptyOrNull); 26 | } 27 | 28 | extension StringExtensions on String? { 29 | bool isNull(dynamic value) => value == null; 30 | 31 | /// Returns whether a dynamic value PROBABLY 32 | /// has the isEmpty getter/method by checking 33 | /// standard dart types that contains it. 34 | /// 35 | /// This is here to for the 'DRY' 36 | bool? _isEmpty(dynamic value) { 37 | if (value is String) { 38 | return value.toString().trim().isEmpty; 39 | } 40 | if (value is Iterable || value is Map) { 41 | return value.isEmpty as bool?; 42 | } 43 | return false; 44 | } 45 | 46 | 47 | 48 | /// Checks if string is a valid username. 49 | bool isUsername(String s) => hasMatch(s, r'^[a-zA-Z0-9][a-zA-Z0-9_.]+[a-zA-Z0-9]$'); 50 | 51 | /// Checks if string is Palindrom. 52 | bool isPalindrom(String string) { 53 | 54 | final cleanString = string.toLowerCase().replaceAll(RegExp(r"\s+"), '').replaceAll(RegExp(r"[^0-9a-zA-Z]+"), ""); 55 | 56 | for (var i = 0; i < cleanString.length; i++) { 57 | if (cleanString[i] != cleanString[cleanString.length - i - 1]) { 58 | return false; 59 | } 60 | } 61 | 62 | return true; 63 | } 64 | 65 | /// Checks if string is Currency. 66 | bool isCurrency(String s) => hasMatch(s, 67 | r'^(S?\$|\₩|Rp|\¥|\€|\₹|\₽|fr|R\$|R)?[ ]?[-]?([0-9]{1,3}[,.]([0-9]{3}[,.])*[0-9]{3}|[0-9]+)([,.][0-9]{1,2})?( ?(USD?|AUD|NZD|CAD|CHF|GBP|CNY|EUR|JPY|IDR|MXN|NOK|KRW|TRY|INR|RUB|BRL|ZAR|SGD|MYR))?$'); 68 | 69 | /// Checks if string is phone number. 70 | bool isPhoneNumber(String s) { 71 | if (s.length > 16 || s.length < 9) return false; 72 | return hasMatch(s, r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$'); 73 | } 74 | 75 | /// Checks if string is email. 76 | bool isEmail(String s) => hasMatch(s, 77 | r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); 78 | 79 | /// Checks if string is an html file. 80 | bool isHTML(String filePath) { 81 | return filePath.toLowerCase().endsWith(".html"); 82 | } 83 | 84 | /// Checks if string is an video file. 85 | bool isVideo(String filePath) { 86 | var ext = filePath.toLowerCase(); 87 | 88 | return ext.endsWith(".mp4") || 89 | ext.endsWith(".avi") || 90 | ext.endsWith(".wmv") || 91 | ext.endsWith(".rmvb") || 92 | ext.endsWith(".mpg") || 93 | ext.endsWith(".mpeg") || 94 | ext.endsWith(".3gp"); 95 | } 96 | 97 | /// Checks if string is an audio file. 98 | bool isAudio(String filePath) { 99 | final ext = filePath.toLowerCase(); 100 | 101 | return ext.endsWith(".mp3") || 102 | ext.endsWith(".wav") || 103 | ext.endsWith(".wma") || 104 | ext.endsWith(".amr") || 105 | ext.endsWith(".ogg"); 106 | } 107 | 108 | /// Checks if string is an image file. 109 | bool isImage(String filePath) { 110 | final ext = filePath.toLowerCase(); 111 | 112 | return ext.endsWith(".jpg") || 113 | ext.endsWith(".jpeg") || 114 | ext.endsWith(".png") || 115 | ext.endsWith(".gif") || 116 | ext.endsWith(".bmp"); 117 | } 118 | 119 | bool hasMatch(String? value, String pattern) { 120 | return (value == null) ? false : RegExp(pattern).hasMatch(value); 121 | } 122 | 123 | // Check if the string has any number in it, not accepting double, so don't 124 | // use "." 125 | isNumericOnly(String s) => hasMatch(s, r'^\d+$'); 126 | 127 | /// Checks if string consist only Alphabet. (No Whitespace) 128 | bool isAlphabetOnly(String s) => hasMatch(s, r'^[a-zA-Z]+$'); 129 | 130 | /// Checks if string contains at least one Capital Letter 131 | bool hasCapitalletter(String s) => hasMatch(s, r'[A-Z]'); 132 | 133 | /// Checks if data is null or blank (empty or only contains whitespace). 134 | bool? isBlank(dynamic value) { 135 | return _isEmpty(value); 136 | } 137 | 138 | /// Checks if string is boolean. 139 | bool isBool(String value) { 140 | if (isNull(value)) { 141 | return false; 142 | } 143 | 144 | return (value == 'true' || value == 'false'); 145 | } 146 | 147 | Size get getTextSize { 148 | final TextPainter textPainter = TextPainter( 149 | text: TextSpan(text: this), 150 | maxLines: 1, 151 | )..layout( 152 | minWidth: 0, 153 | maxWidth: double.infinity, 154 | ); 155 | return textPainter.size; 156 | } 157 | 158 | // Will add new line if the sentence is bigger the 2 words. 159 | /// [afterWords] will add new line after the selected word 160 | /// Example 161 | /// 'Hi, my name is'.wrapString(2) 162 | /// 163 | /// will print: 164 | /// Hi, my 165 | /// name is 166 | 167 | String wrapString(int afterWords) { 168 | final wordsArr = this?.split(' ') ?? []; 169 | 170 | if (wordsArr.length > 2) { 171 | final int middle = (this?.indexOf(wordsArr[afterWords]) ?? 0) - 1; 172 | final prefix = this?.substring(0, middle); 173 | final postfix = this?.substring(middle + 1); 174 | return '$prefix\n$postfix'; 175 | } 176 | 177 | return this ?? ''; 178 | } 179 | 180 | String generateMessageByGender({Gender? gender, Message? message}) => Intl.gender(gender.toString(), 181 | male: '$this ${message?.male}', female: '$this ${message?.female}', other: '$this ${message?.other}'); 182 | 183 | bool validateEmail() { 184 | if (this == null) return false; 185 | return RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(this!); 186 | } 187 | 188 | bool equalsIgnoreCase(String? other) => 189 | (this == null && other == null) || (this != null && other != null && this!.toLowerCase() == other.toLowerCase()); 190 | 191 | /// Return the string only if the delimiter exists in both ends, otherwise it will return the current string 192 | String? removeSurrounding(String delimiter) { 193 | if (this == null) return null; 194 | final prefix = delimiter; 195 | final suffix = delimiter; 196 | 197 | if ((this!.length >= prefix.length + suffix.length) && this!.startsWith(prefix) && this!.endsWith(suffix)) { 198 | return this!.substring(prefix.length, this!.length - suffix.length); 199 | } 200 | return this; 201 | } 202 | 203 | /// Return a bool if the string is null or empty 204 | bool get isEmptyOrNull => this == null || this!.isEmpty; 205 | 206 | /// Replace part of string after the first occurrence of given delimiter with the [replacement] string. 207 | /// If the string does not contain the delimiter, returns [defaultValue] which defaults to the original string. 208 | String? replaceAfter(String delimiter, String replacement, [String? defaultValue]) { 209 | if (this == null) return null; 210 | final index = this!.indexOf(delimiter); 211 | return (index == -1) 212 | ? defaultValue.isEmptyOrNull 213 | ? this 214 | : defaultValue 215 | : this!.replaceRange(index + 1, this!.length, replacement); 216 | } 217 | 218 | /// 219 | /// Replace part of string before the first occurrence of given delimiter with the [replacement] string. 220 | /// If the string does not contain the delimiter, returns [missingDelimiterValue?] which defaults to the original string. 221 | String? replaceBefore(String delimiter, String replacement, [String? defaultValue]) { 222 | if (this == null) return null; 223 | final index = this!.indexOf(delimiter); 224 | return (index == -1) 225 | ? defaultValue.isEmptyOrNull 226 | ? this 227 | : defaultValue 228 | : this!.replaceRange(0, index, replacement); 229 | } 230 | 231 | ///Returns `true` if at least one element matches the given [predicate]. 232 | /// the [predicate] should have only one character 233 | bool anyChar(bool predicate(String element)) => this.isEmptyOrNull ? false : this!.split('').any((s) => predicate(s)); 234 | 235 | /// Returns the string if it is not `null`, or the empty string otherwise 236 | String get orEmpty => this ?? ""; 237 | 238 | // if the string is empty perform an action 239 | String? ifEmpty(Function action) => this?.isEmpty == true ? action() : this; 240 | 241 | String get lastIndex { 242 | if (isEmptyOrNull) return ""; 243 | return this![this!.length - 1]; 244 | } 245 | 246 | /// prints to console this text if it's not empty or null 247 | void printThis() { 248 | if (isEmptyOrNull) return; 249 | print(toString()); 250 | } 251 | 252 | /// Parses the string as an double or returns `null` if it is not a number. 253 | double? toDoubleOrNull() => this == null ? null : double.tryParse(this!); 254 | 255 | /// Parses the string as an int or returns `null` if it is not a number. 256 | int? toIntOrNull() => this == null ? null : int.tryParse(this!); 257 | 258 | /// Returns a String without white space at all 259 | /// "hello world" // helloworld 260 | String? removeAllWhiteSpace() => this.isEmptyOrNull ? null : this!.replaceAll(RegExp(r"\s+\b|\b\s"), ""); 261 | 262 | /// Returns true if s is neither null, empty nor is solely made of whitespace characters. 263 | bool get isNotBlank => this != null && this!.trim().isNotEmpty; 264 | 265 | /// Returns a list of chars from a String 266 | List toCharArray() => isNotBlank ? this!.split('') : []; 267 | 268 | /// Returns a new string in which a specified string is inserted at a specified index position in this instance. 269 | String insert(int index, String str) => (List.from(this.toCharArray())..insert(index, str)).join(); 270 | 271 | /// Indicates whether a specified string is `null`, `empty`, or consists only of `white-space` characters. 272 | bool get isNullOrWhiteSpace { 273 | var length = (this?.split('') ?? []).where((x) => x == ' ').length; 274 | return length == (this?.length ?? 0) || this.isEmptyOrNull; 275 | } 276 | 277 | /// Shrink a string to be no more than [maxSize] in length, extending from the end. 278 | /// For example, in a string with 10 charachters, a [maxSize] of 3 would return the last 3 charachters. 279 | String? limitFromEnd(int maxSize) => (this?.length ?? 0) < maxSize ? this : this!.substring(this!.length - maxSize); 280 | 281 | /// Shrink a string to be no more than [maxSize] in length, extending from the start. 282 | /// For example, in a string with 10 charachters, a [maxSize] of 3 would return the first 3 charachters. 283 | String? limitFromStart(int maxSize) => (this?.length ?? 0) < maxSize ? this : this!.substring(0, maxSize); 284 | 285 | /// Convert this string into boolean. 286 | /// 287 | /// Returns `true` if this string is any of these values: `"true"`, `"yes"`, `"1"`, or if the string is a number and greater than 0, `false` if less than 1. This is also case insensitive. 288 | bool get asBool { 289 | var s = this?.trim().toLowerCase() ?? "false"; 290 | num n; 291 | try { 292 | n = num.parse(s); 293 | } catch (e) { 294 | n = -1; 295 | } 296 | return s == 'true' || s == 'yes' || n > 0; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | void checkNullError(Object o) { 15 | // ignore: unnecessary_null_comparison 16 | if (o == null) { 17 | throw NullIteratorError(); 18 | } 19 | } 20 | 21 | class NullIteratorError extends TypeError { 22 | @override 23 | String toString() { 24 | return '(NullIteratorError) Cannot call extension method on null.'; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dart_extensions 2 | description: Set of method-extensions for dart that makes using framework in a much easier and clean way also adds additional functionality. 3 | version: 2.2.4 4 | documentation: https://github.com/droididan/dart_extentions 5 | homepage: https://github.com/droididan/dart_extentions 6 | 7 | environment: 8 | sdk: ">=3.3.0 <4.0.0" 9 | 10 | dependencies: 11 | intl: ^0.19.0 12 | quiver: ^3.2.1 13 | http: ^1.2.1 14 | 15 | flutter: 16 | sdk: flutter 17 | 18 | dev_dependencies: 19 | test: 1.25.2 20 | -------------------------------------------------------------------------------- /test/algo_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:test/test.dart'; 15 | import 'package:dart_extensions/src/sort_algorithms.dart'; 16 | 17 | main() { 18 | group('algo tests', () { 19 | test('binarySearch', () { 20 | expect([9, 3, 5, 1, 2, 7, 8, 6, 10, 4, 3].quickSort(0, 10), 21 | [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10]); 22 | }); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /test/date_tests.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:test/test.dart'; 15 | import 'package:dart_extensions/src/date.dart'; 16 | 17 | main() { 18 | group('date time', () { 19 | test('toMilliseconds', () { 20 | expect(1.toMilliseconds(), Duration(milliseconds: 1)); 21 | }); 22 | 23 | test('toSeconds', () { 24 | expect(1.toSeconds(), Duration(seconds: 1)); 25 | }); 26 | 27 | test('toMinutes', () { 28 | expect(1.toMinutes(), Duration(minutes: 1)); 29 | }); 30 | 31 | test('toHours', () { 32 | expect(1.toHours(), Duration(hours: 1)); 33 | }); 34 | 35 | test('toDays', () { 36 | expect(1.toDays(), Duration(days: 1)); 37 | }); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /test/enum_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | import '../lib/emum.dart'; 3 | 4 | enum TestEnum { testValue1, testValue2, testValue3 } 5 | enum OtherEnumForTesting { helloImAnEnumValue } 6 | 7 | void main() { 8 | test('it should convert enums to string', () { 9 | expect(TestEnum.testValue1.convertToString(), 'testValue1'); 10 | expect(TestEnum.testValue2.convertToString(), 'testValue2'); 11 | expect(TestEnum.testValue3.convertToString(), 'testValue3'); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /test/int_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import 'package:test/test.dart'; 14 | import 'package:dart_extensions/src/int.dart'; 15 | 16 | main() { 17 | group('integers', () { 18 | 19 | test('inRangeOf', () { 20 | expect(1.inRangeOf(0, 3), 1); 21 | expect(2.inRangeOf(3, 4), 3); 22 | expect(5.inRangeOf(3, 4), 4); 23 | }); 24 | 25 | 26 | test('isEven', () { 27 | expect(2.isEven, true); 28 | expect(3.isEven, false); 29 | }); 30 | 31 | test('isOdd', () { 32 | expect(3.isOdd, true); 33 | expect(2.isOdd, false); 34 | }); 35 | 36 | test('isPositive', () { 37 | expect(0.isPositive, false); 38 | expect(1.isPositive, true); 39 | expect((-1).isPositive, false); 40 | }); 41 | 42 | test('isNegative', () { 43 | expect(0.isNegative, false); 44 | expect(1.isNegative, false); 45 | expect((-1).isNegative, true); 46 | }); 47 | 48 | test('tenth', () { 49 | expect(100.tenth, 10); 50 | }); 51 | 52 | test('fourth', () { 53 | expect(16.fourth, 4); 54 | }); 55 | 56 | test('third', () { 57 | expect(9.third, 3); 58 | }); 59 | 60 | test('half', () { 61 | expect(100.half, 50); 62 | }); 63 | 64 | test('doubled', () { 65 | expect(100.doubled, 200); 66 | }); 67 | 68 | test('tripled', () { 69 | expect(100.tripled, 300); 70 | }); 71 | 72 | test('quadrupled', () { 73 | expect(100.quadrupled, 400); 74 | }); 75 | 76 | test('squared', () { 77 | expect(2.squared, 4); 78 | }); 79 | 80 | test('asBool', () { 81 | expect(1.asBool, true); 82 | expect(0.asBool, false); 83 | expect(9382.asBool, true); 84 | expect((-2272).asBool, false); 85 | }); 86 | }); 87 | } -------------------------------------------------------------------------------- /test/iterable_tests.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import 'package:dart_extensions/src/iterable.dart'; 14 | import 'package:dart_extensions/src/model/user.dart'; 15 | import 'package:test/test.dart'; 16 | 17 | main() { 18 | final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; 19 | 20 | test('result of whereIndex', () { 21 | final result = users.whereIndexed((index, _) => index == 2); 22 | expect(result, [User(22, "Oded")]); 23 | }); 24 | 25 | test('if all elements match the predicate', () { 26 | final isEven = (x) => x % 2 == 0; 27 | expect([5, 19, 2].all(isEven), isFalse); 28 | expect([6, 12, 2].all(isEven), isTrue); 29 | expect([].all(isEven), isTrue); 30 | }); 31 | 32 | test('mapList', () { 33 | expect([3, 4, 5, 6, 7].mapList((item) => item.toDouble()), [3.0, 4.0, 5.0, 6.0, 7.0]); 34 | }); 35 | 36 | test('any', () { 37 | expect(users.any((u) => u.name == 'Oded'), true); 38 | expect(users.any((u) => u.name == 'Not Exists'), false); 39 | expect([].any((u) => u.name == 'Oded'), false); 40 | }); 41 | 42 | test("first or null", () { 43 | final list = [1, 45, 6, 9]; 44 | expect(null, list.firstWhere((element) => element == 7)); 45 | }); 46 | 47 | test('filter', () { 48 | [null, 1, 2].where((n) => n == 2); 49 | final listWithNull = [null, User(1, "r"), User(2, "t")]; 50 | final numberList = [1, 2, 3, 4, 5, 6]; 51 | 52 | expect(users.filter((u) => u.name == "Ronit"), [users[0], users[1]]); 53 | expect(numberList.filter((n) => n > 4), [5, 6]); 54 | expect(listWithNull.filter((u) => u?.name == "r"), [listWithNull[1]]); 55 | }); 56 | 57 | test('filterNot', () { 58 | final listWithNull = [null, User(1, "r"), User(2, "t")]; 59 | final numberList = [1, 2, 3, 4, 5, 6]; 60 | 61 | expect(listWithNull.filterNot((u) => u?.name == "t"), [listWithNull[1]]); 62 | expect(numberList.filterNot((n) => n > 4), [1, 2, 3, 4]); 63 | }); 64 | 65 | test('takeOnly', () { 66 | expect([1, 2, 3, 4].takeOnly(1), [1]); 67 | expect([1, 2, 3, 4].takeOnly(2), [1, 2]); 68 | expect([1, 2, 3, 4].takeOnly(10), []); 69 | }); 70 | 71 | test('drop', () { 72 | expect([1, 2, 3, 4].drop(1), [2, 3, 4]); 73 | expect([1, 2, 3, 4].drop(2), [3, 4]); 74 | expect([1, 2, 3, 4].drop(5), []); 75 | }); 76 | 77 | test('firstHalf', () { 78 | expect([1, 2, 3, 4].firstHalf(), [1, 2]); 79 | }); 80 | 81 | test('secondHalf', () { 82 | expect([1, 2, 3, 4].secondHalf(), [3, 4]); 83 | }); 84 | 85 | test('swap', () { 86 | expect([1, 2, 3, 4].swap(0, 1), [2, 1, 3, 4]); 87 | }); 88 | 89 | test('lastOrDefault', () { 90 | expect([1, 2, 3, 4].lastOrDefault(999), 4); 91 | expect([].lastOrDefault(999), 999); 92 | }); 93 | 94 | test('firstOrDefault', () { 95 | expect([1, 2, 3, 4].firstOrDefault(999), 1); 96 | expect([].firstOrDefault(999), 999); 97 | }); 98 | 99 | test('sortedDescending', () { 100 | expect([1, 2, 3, 4].sortedDescending(), [4, 3, 2, 1]); 101 | }); 102 | 103 | test('containsAll', () { 104 | expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); 105 | expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); 106 | expect([].containsAll([1, 2, 4]), false); 107 | }); 108 | 109 | test('containsAll', () { 110 | expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); 111 | expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); 112 | expect([].containsAll([1, 2, 4]), false); 113 | }); 114 | 115 | test('containsAll', () { 116 | expect(users.count((u) => u.age == 22), 2); 117 | expect(users.count((u) => u.name == "Ronit"), 2); 118 | expect(users.count((u) => u.name == "Bin"), 0); 119 | }); 120 | 121 | test('distinctBy', () { 122 | final expected = users.distinctBy((u) => u.age); 123 | expect(users.distinctBy((u) => u.age), expected); 124 | }); 125 | 126 | test('subtract', () { 127 | expect([1, 2, 3, 4].subtract([3, 4]), [1, 2]); 128 | expect([1, 2, 3, 4].subtract([1, 2, 3, 4]), []); 129 | expect([1, 2, 3, 4].subtract([1, 2]), [3, 4]); 130 | }); 131 | 132 | test('toStack', () { 133 | var stack = [1, 2, 3, 4].toStack(); 134 | expect(stack.pop(), 4); 135 | expect(stack.top(), 3); 136 | expect(stack.pop(), 3); 137 | expect(stack.top(), 2); 138 | }); 139 | 140 | test('concatWithSingleList', () { 141 | expect([1, 2, 3, 4].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); 142 | expect([1, 2, 3, 4].concatWithSingleList([]), []); 143 | expect([].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), []); 144 | }); 145 | 146 | test('concatWithMultipleList', () { 147 | final listOfLists = [ 148 | [5, 6, 7], 149 | [8, 9, 10] 150 | ]; 151 | expect([1, 2, 3, 4].concatWithMultipleList(listOfLists), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 152 | expect([1, 2, 3, 4].concatWithMultipleList([[], []]), [1, 2, 3, 4]); 153 | expect([].concatWithMultipleList(listOfLists), []); 154 | }); 155 | 156 | test('zip', () { 157 | final expectedResult = [ 158 | [1, 2], 159 | [3, 4], 160 | [5, 6] 161 | ]; 162 | expect([1, 3, 5].zip([2, 4, 6]), expectedResult); 163 | expect([1, 3, 5].zip([2, 4, 6, 7, 8]), expectedResult); 164 | }); 165 | 166 | test('associate', () { 167 | final users = [User(33, "Miki"), User(45, "Anna"), User(19, "Amit")]; 168 | expect(users.associate((k) => k.name, (e) => e.age), {'Miki': 33, 'Anna': 45, 'Amit': 19}); 169 | }); 170 | 171 | test('find', () { 172 | final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; 173 | expect(users.find((u) => u.name == "Ronit"), users.first); 174 | expect(users.find((u) => u.name == "Oded"), users[2]); 175 | expect(users.find((u) => u.name == "Not Exists Name"), null); 176 | expect(users.find((u) => u.age == 32), users.last); 177 | expect(users.find((u) => u.age == 31), null); 178 | }); 179 | 180 | test('gruopBy by age', () { 181 | final expected = { 182 | 22: [users[0], users[2]], 183 | 23: [users[1]], 184 | 32: [users[3]] 185 | }; 186 | 187 | expect(users.groupBy((User u) => u.age), expected); 188 | }); 189 | 190 | test('gourpBy by name', () { 191 | final expected = { 192 | 'Ronit': [users[0], users[1]], 193 | 'Oded': [users[2]], 194 | 'Shimi': [users[3]] 195 | }; 196 | expect(users.groupBy((User u) => u.name), expected); 197 | }); 198 | 199 | test('toMutableSet', () { 200 | expect([1, 1, 1, 1, 2, 3, 4].toMutableSet(), [1, 2, 3, 4]); 201 | expect(["a", "b", "a"].toMutableSet(), ["a", "b"]); 202 | }); 203 | 204 | test('intersect', () { 205 | expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); 206 | expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); 207 | }); 208 | } 209 | -------------------------------------------------------------------------------- /test/ranges_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | import 'package:dart_extensions/src/ranges.dart'; 14 | import 'package:test/test.dart'; 15 | 16 | main() { 17 | group('ranges', () { 18 | test('test positive range', () { 19 | expect(1.until(-10), []); 20 | }); 21 | 22 | test('test from positive to nagative range', () { 23 | expect(1.until(-3, step: -1), [1, 0, -1, -2]); 24 | }); 25 | 26 | test('test positive range with step 2', () { 27 | expect(1.until(10, step: 2), [1, 3, 5, 7, 9]); 28 | }); 29 | 30 | test('test negative range', () { 31 | expect((-10).until(0), [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1]); 32 | }); 33 | 34 | test('test positive range with step 2', () { 35 | expect((-10).until(0, step: 2), [-10, -8, -6, -4, -2]); 36 | }); 37 | 38 | test('for loop', () { 39 | final numbers = []; 40 | for (final num in 1.until(10)) { 41 | numbers.add(num); 42 | } 43 | expect(numbers, [1, 2, 3, 4, 5, 6, 7, 8, 9]); 44 | }); 45 | 46 | test('for loop step 2', () { 47 | final numbers = []; 48 | for (final num in 1.until(10, step: 2)) { 49 | numbers.add(num); 50 | } 51 | expect(numbers, [1, 3, 5, 7, 9]); 52 | }); 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /test/strings_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Idan Ayalon. All rights reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import 'package:test/test.dart'; 15 | import 'package:dart_extensions/src/string_ext.dart'; 16 | 17 | main() { 18 | group('strings', () { 19 | test('it should wrap a string', () { 20 | expect('one two three four'.wrapString(2), 'one two\nthree four'); 21 | expect('one two three four'.wrapString(3), 'one two three\nfour'); 22 | }); 23 | 24 | test('validateEmail', () { 25 | expect('name@domain.com'.validateEmail(), true); 26 | expect('name@domain'.validateEmail(), false); 27 | }); 28 | test('equalsIgnoreCase', () { 29 | expect('text'.equalsIgnoreCase('Text'), true); 30 | expect('text'.equalsIgnoreCase('another'), false); 31 | }); 32 | test('removeSurrounding', () { 33 | expect('-text-'.removeSurrounding('-'), 'text'); 34 | expect('-text-'.removeSurrounding('t'), '-text-'); 35 | }); 36 | test('isNullOrEmpty', () { 37 | expect(''.isEmptyOrNull, true); 38 | 39 | String? nullStr; 40 | expect(nullStr.isEmptyOrNull, true); 41 | expect('not empty'.isEmptyOrNull, false); 42 | }); 43 | 44 | test('replaceAfter', () { 45 | expect('name@'.replaceAfter('@', "domain.com"), "name@domain.com"); 46 | }); 47 | 48 | test('replaceBefore', () { 49 | expect('@domain.com'.replaceBefore('@', "name"), "name@domain.com"); 50 | }); 51 | 52 | test('anyChar', () { 53 | expect('test'.anyChar((s) => s == 't'), true); 54 | expect('test'.anyChar((s) => s == 'd'), false); 55 | }); 56 | 57 | test('toDoubleOrNull', () { 58 | expect('20.0'.toDoubleOrNull(), 20.0); 59 | expect('a20.0'.toDoubleOrNull(), null); 60 | }); 61 | 62 | test('removeAllWhiteSpace', () { 63 | expect('hello world'.removeAllWhiteSpace(), 'helloworld'); 64 | expect(' helloworld '.removeAllWhiteSpace(), 'helloworld'); 65 | }); 66 | 67 | test('isNotBlank', () { 68 | expect('hello world'.isNotBlank, true); 69 | expect('hello world '.isNotBlank, true); 70 | expect(''.isNotBlank, false); 71 | final String? test = null; 72 | expect(test.isNotBlank, false); 73 | }); 74 | 75 | test('toCharArray', () { 76 | expect("test".toCharArray(), ['t', 'e', 's', 't']); 77 | expect("".toCharArray(), []); 78 | }); 79 | 80 | test('insert', () { 81 | expect('test'.insert(1, 't'), 'ttest'); 82 | expect('123456890'.insert(6, '7'), '1234567890'); 83 | expect('dart cool'.insert(4, ' is'), 'dart is cool'); 84 | // how about multiline? 85 | expect('flutter\ncooler'.insert(7, '\nis'), 'flutter\nis\ncooler'); 86 | }); 87 | 88 | test('isNullOrWhiteSpace', () { 89 | expect('test'.isNullOrWhiteSpace, false); 90 | expect(' '.isNullOrWhiteSpace, true); 91 | expect(null.isNullOrWhiteSpace, true); 92 | expect(''.isNullOrWhiteSpace, true); 93 | expect(' test'.isNullOrWhiteSpace, false); 94 | expect('test '.isNullOrWhiteSpace, false); 95 | expect('te st'.isNullOrWhiteSpace, false); 96 | expect(' te st '.isNullOrWhiteSpace, false); 97 | }); 98 | 99 | test('asBool', () { 100 | // strings, it is case insensitive 101 | expect('true'.asBool, true); 102 | expect('True'.asBool, true); 103 | expect('false'.asBool, false); 104 | expect('False'.asBool, false); 105 | expect('yes'.asBool, true); 106 | expect('YES'.asBool, true); 107 | expect('no'.asBool, false); 108 | expect('NO'.asBool, false); 109 | 110 | // string numbers 111 | expect('232'.asBool, true); 112 | expect('1'.asBool, true); 113 | expect('0'.asBool, false); 114 | expect('-1'.asBool, false); 115 | }); 116 | 117 | test('limitFromEnd', () { 118 | const source = "01234"; 119 | expect(source.limitFromEnd(10), source); 120 | expect(source.limitFromEnd(2), "34"); 121 | }); 122 | test('limitFromStart', () { 123 | const source = "01234"; 124 | expect(source.limitFromStart(10), source); 125 | expect(source.limitFromStart(2), "01"); 126 | }); 127 | }); 128 | } 129 | --------------------------------------------------------------------------------