├── .gitattributes ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── dash-tools.png ├── documentations └── adaptive_ui_with_platform_type_detection.md ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── 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-night │ │ │ │ └── styles.xml │ │ │ │ └── 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 │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── 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 │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ ├── countries.dart │ └── main.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ ├── Configs │ │ │ ├── AppInfo.xcconfig │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements │ └── RunnerTests │ │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── lib ├── flutter_helper_utils.dart └── src │ ├── ai_docs.txt │ ├── extensions │ ├── extensions.dart │ ├── flutter_extensions │ │ ├── align.dart │ │ ├── carousel_controller.dart │ │ ├── colors │ │ │ ├── colors.dart │ │ │ ├── on_colors.dart │ │ │ └── on_string.dart │ │ ├── directionality.dart │ │ ├── flutter_extensions.dart │ │ ├── focus_scope_extensions.dart │ │ ├── future.dart │ │ ├── list_widget.dart │ │ ├── media_query_extension.dart │ │ ├── navigation.dart │ │ ├── on_numbers.dart │ │ ├── padding.dart │ │ ├── platform_extension.dart │ │ ├── scaffold_messenger_extension.dart │ │ ├── scroll_extensions.dart │ │ └── theme_extension.dart │ ├── num_extensions.dart │ └── string_extensions │ │ ├── general_string_extensions.dart │ │ └── string_extensions.dart │ ├── platform_env │ ├── platform_env.dart │ ├── platform_env_io.dart │ └── platform_env_web.dart │ ├── src.dart │ ├── value_notifier │ ├── extensions │ │ ├── extensions.dart │ │ ├── listenable_extensions.dart │ │ ├── notifier_extensions.dart │ │ ├── stream_to_notifier.dart │ │ └── value_notifier_extensions.dart │ ├── notifier_classes │ │ ├── bool_notifier.dart │ │ ├── color_notifier.dart │ │ ├── date_notifier.dart │ │ ├── double_notifier.dart │ │ ├── doubly_linked_list_notifier.dart │ │ ├── duration_notifier.dart │ │ ├── int_notifier.dart │ │ ├── list_notifier.dart │ │ ├── map_notifier.dart │ │ ├── notifier_classes.dart │ │ ├── num_notifier.dart │ │ ├── set_notifier.dart │ │ ├── string_notifier.dart │ │ ├── theme_mode_notifier.dart │ │ └── uri_notifier.dart │ ├── value_notifier.dart │ └── widgets │ │ ├── listenables_builder.dart │ │ └── widgets.dart │ └── widgets │ ├── adaptive_ui.dart │ ├── gradient_widget.dart │ ├── multi_tap_detector.dart │ ├── typed_list_view.dart │ └── widgets.dart ├── logo.svg ├── migration_guides.md ├── pubspec.yaml └── test └── string_to_colors_test.dart /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | -------------------------------------------------------------------------------- /.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: f1875d570e39de09040c8f79aa13cc56baab8db1 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Omar Khaled 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:very_good_analysis/analysis_options.7.0.0.yaml 2 | linter: 3 | rules: 4 | public_member_api_docs: false 5 | sort_pub_dependencies: false 6 | lines_longer_than_80_chars: false 7 | always_use_package_imports: false 8 | literal_only_boolean_expressions: false 9 | require_trailing_commas: false 10 | comment_references: false 11 | only_throw_errors: false 12 | unnecessary_library_directive: false 13 | avoid_positional_boolean_parameters: false 14 | avoid_catches_without_on_clauses: false 15 | document_ignores: false 16 | avoid_redundant_argument_values: false 17 | # avoid_dynamic_calls: false 18 | # use_build_context_synchronously: false 19 | -------------------------------------------------------------------------------- /dash-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/dash-tools.png -------------------------------------------------------------------------------- /documentations/adaptive_ui_with_platform_type_detection.md: -------------------------------------------------------------------------------- 1 | # Adaptive UI with Platform Type Detection 2 | 3 | This documentation is part of the [flutter_helper_utils](https://pub.dev/packages/flutter_helper_utils) package. 4 | 5 | ## Table of Contents 6 | 7 | - [Features](#features) 8 | - [Usage](#usage) 9 | - [Customizing Breakpoints](#customizing-breakpoints) 10 | - [Complete Example](#complete-example-with-customizations) 11 | 12 | ## Features 13 | 14 | - **Efficient:** Rebuild widgets only when the platform type changes, not on every size change. 15 | - **Native Dart:** Built with native Dart code, no 3rd party dependencies are used. 16 | - **Easy Access:** Use context extensions to easily get platform and orientation details. 17 | - **Customizable:** Define your own breakpoints and helper extensions. 18 | 19 | ## Usage 20 | 21 | ### 1. Wrap with PlatformTypeProvider 22 | 23 | Wrap the root of your application with `PlatformTypeProvider` to make platform and orientation information accessible throughout your widget tree. 24 | 25 | ```dart 26 | import 'package:flutter_helper_utils/flutter_helper_utils.dart'; 27 | 28 | runApp(PlatformTypeProvider( 29 | breakpoints: Breakpoint.defaults, // Use default breakpoints or provide your own 30 | child: MyApp() 31 | )); 32 | ``` 33 | 34 | ### 2. Build UI Using Context Extensions 35 | 36 | Leverage context extensions for a streamlined approach to building adaptive UIs: 37 | 38 | ```dart 39 | @override 40 | Widget build(BuildContext context) { 41 | final breakpoint = context.watchBreakpoint; // Automatically rebuilds when the breakpoint changes 42 | return breakpoint.isMobile ? const MobileLayout() : const TabletLayout(); 43 | } 44 | ``` 45 | 46 | **Other context extensions:** 47 | 48 | * `context.platformSizeInfo`: Provides `orientation`, `platform`, and `breakpoint` information, rebuilding the widget when any of these values change. 49 | * `context.deviceOrientation`: Offers the current orientation (rebuilds on change). 50 | 51 | ### 3. Build UI Using Layout Builder Widgets 52 | 53 | #### BreakpointLayoutBuilder 54 | 55 | Use this to build widgets based solely on the current breakpoint (e.g., mobile, tablet). 56 | 57 | ```dart 58 | BreakpointLayoutBuilder( 59 | builder: (context, breakpoint) { 60 | if (breakpoint.isMobile) { 61 | // Mobile layout 62 | } else if (breakpoint.isTablet) { 63 | // Tablet layout 64 | } else if (breakpoint.isDesktop) { 65 | // Desktop layout 66 | } // ... (add more conditions for other breakpoints) 67 | }, 68 | ); 69 | ``` 70 | 71 | #### PlatformInfoLayoutBuilder 72 | 73 | This widget provides both the platform type and orientation for building your UI: 74 | 75 | ```dart 76 | PlatformInfoLayoutBuilder( 77 | builder: (context, platformSizeInfo) { 78 | // Access platform, breakpoint, and orientation 79 | if (platformSizeInfo.breakpoint.isMobile) { 80 | // Mobile layout 81 | } // ... (add more conditions) 82 | }, 83 | ); 84 | ``` 85 | 86 | ## Customizing Breakpoints 87 | 88 | The `PlatformTypeProvider` widget lets you build responsive layouts by defining breakpoints for different screen sizes. It comes with default breakpoints for mobile, tablet, and desktop devices, but you can easily customize these or create your own to match your app's unique requirements. 89 | 90 | ### Default Breakpoints 91 | 92 | The package provides convenient constructors for default breakpoints: 93 | 94 | - `Breakpoint.mobile()`: Creates a mobile breakpoint with a default width of 600. 95 | - `Breakpoint.tablet()`: Creates a tablet breakpoint with a default width of 1200. 96 | - `Breakpoint.desktop()`: Creates a desktop breakpoint with a default width of 1800. 97 | 98 | You can use these directly or as a basis for customization: 99 | 100 | ```dart 101 | const smallerMobileBreakpoint = Breakpoint.mobile( 102 | name: 'mobileSmall', 103 | width: 360 104 | ); 105 | ``` 106 | 107 | ### Defining and Using Custom Breakpoints 108 | 109 | Create a `Breakpoint` instance with your desired size and a unique name: 110 | 111 | ```dart 112 | const watchBreakPoint = Breakpoint(size: Size(250, 250), name: 'watch'); 113 | ``` 114 | 115 | **To use your custom breakpoint**, pass it to the `PlatformTypeProvider`: 116 | 117 | ```dart 118 | PlatformTypeProvider( 119 | breakpoints: [ 120 | watchBreakPoint, // Your custom breakpoint 121 | ...Breakpoint.defaults, // Include default breakpoints if needed 122 | ], 123 | child: MyApp(), 124 | ), 125 | ``` 126 | 127 | Now, within your `BreakpointLayoutBuilder`, you can easily check for your custom breakpoint using the `match` method: 128 | 129 | ```dart 130 | BreakpointLayoutBuilder( 131 | builder: (context, breakpoint) { 132 | if (breakpoint.match('watch')) { 133 | // Render a watch-specific UI 134 | } else if (breakpoint.isMobile) { 135 | // Render a mobile UI 136 | } // ... and so on 137 | }, 138 | ); 139 | ``` 140 | 141 | **Important Note:** When defining your own breakpoints, consider screen dimensions and pixel densities for various watch models. 142 | 143 | ### Extending Breakpoint Functionality 144 | 145 | To make your breakpoint logic more readable and reusable, create extensions: 146 | 147 | ```dart 148 | extension CustomBreakpoint on Breakpoint { 149 | bool get isWatch => match('watch'); 150 | } 151 | 152 | if (breakpoint.isWatch) { 153 | // Watch-specific layout 154 | } 155 | ``` 156 | 157 | ### Complete Example with customizations 158 | 159 | ```dart 160 | import 'package:flutter/material.dart'; 161 | import 'package:flutter_helper_utils/flutter_helper_utils.dart'; 162 | 163 | const watchBreakPoint = Breakpoint(size: Size(250, 250), name: 'watch'); 164 | 165 | extension CustomBreakpointEx on Breakpoint { 166 | bool get isWatch => match('watch'); 167 | // if you prefer to match with size not name you can use: 168 | // bool get isWatch => this == watchBreakPoint; 169 | } 170 | 171 | void main() { 172 | runApp( 173 | PlatformTypeProvider( 174 | breakpoints: [ 175 | watchBreakPoint, 176 | ...Breakpoint.defaults, 177 | ], 178 | child: MyApp(), 179 | ), 180 | ); 181 | } 182 | 183 | class MyApp extends StatelessWidget { 184 | const MyApp({super.key}); 185 | 186 | @override 187 | Widget build(BuildContext context) { 188 | return MaterialApp( 189 | title: 'Adaptive UI', 190 | home: BreakpointLayoutBuilder( 191 | builder: (context, breakpoint) { 192 | if (breakpoint.isWatch) { 193 | // Render a watch-specific UI 194 | } else if (breakpoint.isMobile) { 195 | // Render a mobile UI 196 | } // ... and so on 197 | // the breakpoint understands operators like >, <=, ==, etc 198 | // e.g. breakpoint > watchBreakPoint 199 | }, 200 | ), 201 | ); 202 | } 203 | } 204 | ``` 205 | 206 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /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: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 17 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 18 | - platform: android 19 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 20 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 21 | - platform: ios 22 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 23 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 24 | - platform: linux 25 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 26 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 27 | - platform: macos 28 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 29 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 30 | - platform: web 31 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 32 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 33 | - platform: windows 34 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 35 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.example" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.example" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | tasks.register("clean", Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 21 | } 22 | } 23 | 24 | plugins { 25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 26 | id "com.android.application" version "7.3.0" apply false 27 | } 28 | 29 | include ":app" 30 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/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 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/countries.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_helper_utils/flutter_helper_utils.dart'; 5 | 6 | class Countries extends StatefulWidget { 7 | const Countries({super.key}); 8 | 9 | @override 10 | State createState() => _CountriesState(); 11 | } 12 | 13 | class _CountriesState extends State { 14 | final List countries = DHUCountry.generate(); 15 | List filteredCountries = []; 16 | String searchQuery = ''; 17 | Timer? _debounce; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | filteredCountries = countries; 23 | } 24 | 25 | // Debounce search to optimize performance 26 | void updateSearch(String query) { 27 | if (_debounce?.isActive ?? false) _debounce!.cancel(); 28 | _debounce = Timer(const Duration(milliseconds: 300), () { 29 | performSearch(query); 30 | }); 31 | } 32 | 33 | void performSearch(String query) { 34 | setState(() { 35 | searchQuery = query.toLowerCase().trim(); 36 | filteredCountries = CountrySearchService(countries).search(searchQuery); 37 | }); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Column( 43 | children: [ 44 | // Search widget 45 | Padding( 46 | padding: const EdgeInsets.all(8), 47 | child: TextField( 48 | decoration: const InputDecoration( 49 | labelText: 'Search Countries', 50 | border: OutlineInputBorder(), 51 | ), 52 | onChanged: updateSearch, 53 | ), 54 | ), 55 | if (filteredCountries.isEmpty) 56 | Padding( 57 | padding: const EdgeInsets.all(20), 58 | child: Text( 59 | 'No countries found for "$searchQuery".', 60 | style: const TextStyle(fontSize: 18), 61 | ), 62 | ) 63 | else 64 | SizedBox( 65 | height: 600, 66 | child: ListView.builder( 67 | itemCount: filteredCountries.length, 68 | itemBuilder: (context, i) { 69 | final country = filteredCountries[i]; 70 | final nativeCommonName = country.nativeNames.isNotEmpty 71 | ? '(${country.nativeNames.first.common})' 72 | : ''; 73 | 74 | return ListTile( 75 | leading: Text( 76 | country.flagEmoji, 77 | style: const TextStyle(fontSize: 50), 78 | ), 79 | title: buildHighlightedText( 80 | '${country.commonName} $nativeCommonName', searchQuery), 81 | subtitle: Column( 82 | crossAxisAlignment: CrossAxisAlignment.start, 83 | children: [ 84 | Text('Official: ${country.officialName}'), 85 | Text('Region: ${country.region}, ${country.subregion}'), 86 | Text('Capital: ${country.capital ?? 'N/A'}'), 87 | Text('ISO: ${country.iso2} / ${country.iso3}'), 88 | Text('Phone Code: ${country.phoneCode}'), 89 | Text('Area: ${country.area.toStringAsFixed(0)} km²'), 90 | if (country.currencies.isNotEmpty) 91 | Text( 92 | 'Currency: ${country.currencies.map((c) => c.name).join(', ')}'), 93 | if (country.languages.isNotEmpty) 94 | Text( 95 | 'Languages: ${country.languages.map((l) => l.name).join(', ')}'), 96 | ], 97 | ), 98 | ); 99 | }, 100 | ), 101 | ), 102 | ], 103 | ); 104 | } 105 | 106 | // Highlight matching text 107 | Widget buildHighlightedText(String text, String query) { 108 | final queryRegex = RegExp(RegExp.escape(query), caseSensitive: false); 109 | final matches = queryRegex.allMatches(text); 110 | if (matches.isEmpty) return Text(text); 111 | 112 | final spans = []; 113 | var lastIndex = 0; 114 | for (final match in matches) { 115 | if (match.start > lastIndex) { 116 | spans.add(TextSpan(text: text.substring(lastIndex, match.start))); 117 | } 118 | spans.add(TextSpan( 119 | text: text.substring(match.start, match.end), 120 | style: const TextStyle(backgroundColor: Colors.yellow), 121 | )); 122 | lastIndex = match.end; 123 | } 124 | if (lastIndex < text.length) { 125 | spans.add(TextSpan(text: text.substring(lastIndex))); 126 | } 127 | 128 | return RichText( 129 | text: 130 | TextSpan(children: spans, style: DefaultTextStyle.of(context).style), 131 | ); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.example") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | 90 | # Generated plugin build rules, which manage building the plugins and adding 91 | # them to the application. 92 | include(flutter/generated_plugins.cmake) 93 | 94 | 95 | # === Installation === 96 | # By default, "installing" just makes a relocatable bundle in the build 97 | # directory. 98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 101 | endif() 102 | 103 | # Start with a clean build bundle directory every time. 104 | install(CODE " 105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 106 | " COMPONENT Runtime) 107 | 108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 110 | 111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 112 | COMPONENT Runtime) 113 | 114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 115 | COMPONENT Runtime) 116 | 117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 118 | COMPONENT Runtime) 119 | 120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 121 | install(FILES "${bundled_library}" 122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 123 | COMPONENT Runtime) 124 | endforeach(bundled_library) 125 | 126 | # Copy the native assets provided by the build.dart from all packages. 127 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") 128 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 129 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 130 | COMPONENT Runtime) 131 | 132 | # Fully re-copy the assets directory on each build to avoid having stale files 133 | # from a previous install. 134 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 135 | install(CODE " 136 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 137 | " COMPONENT Runtime) 138 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 139 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 140 | 141 | # Install the AOT library on non-Debug builds only. 142 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 143 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 144 | COMPONENT Runtime) 145 | endif() 146 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "example"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | 9 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 10 | } 11 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | publish_to: 'none' 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: '>=3.0.6 <4.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | enefty_icons: ^1.0.7 14 | intl: ^0.19.0 15 | 16 | # internal packages 17 | flutter_helper_utils: 18 | path: ../ 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | 24 | flutter: 25 | uses-material-design: true 26 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | example 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(example LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omar-hanafy/flutter_helper_utils/946dffb197253a974c79e9a5f945f8d97120378d/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /lib/flutter_helper_utils.dart: -------------------------------------------------------------------------------- 1 | export 'src/src.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/extensions/extensions.dart: -------------------------------------------------------------------------------- 1 | export 'flutter_extensions/flutter_extensions.dart'; 2 | export 'string_extensions/string_extensions.dart'; 3 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/align.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension FHUAlignExtensions on Widget { 4 | Align alignAtBottomCenter({ 5 | Key? key, 6 | double? heightFactor, 7 | double? widthFactor, 8 | }) => 9 | Align( 10 | key: key, 11 | alignment: Alignment.bottomCenter, 12 | heightFactor: heightFactor, 13 | widthFactor: widthFactor, 14 | child: this, 15 | ); 16 | 17 | Align alignAtTopLeft({ 18 | Key? key, 19 | double? heightFactor, 20 | double? widthFactor, 21 | }) => 22 | Align( 23 | key: key, 24 | alignment: Alignment.topLeft, 25 | heightFactor: heightFactor, 26 | widthFactor: widthFactor, 27 | child: this, 28 | ); 29 | 30 | Align alignAtBottomLeft({ 31 | Key? key, 32 | double? heightFactor, 33 | double? widthFactor, 34 | }) => 35 | Align( 36 | key: key, 37 | alignment: Alignment.bottomLeft, 38 | heightFactor: heightFactor, 39 | widthFactor: widthFactor, 40 | child: this, 41 | ); 42 | 43 | Align alignAtBottomRight({ 44 | Key? key, 45 | double? heightFactor, 46 | double? widthFactor, 47 | }) => 48 | Align( 49 | key: key, 50 | alignment: Alignment.bottomRight, 51 | heightFactor: heightFactor, 52 | widthFactor: widthFactor, 53 | child: this, 54 | ); 55 | 56 | Align alignAtCenterLeft({ 57 | Key? key, 58 | double? heightFactor, 59 | double? widthFactor, 60 | }) => 61 | Align( 62 | key: key, 63 | alignment: Alignment.centerLeft, 64 | heightFactor: heightFactor, 65 | widthFactor: widthFactor, 66 | child: this, 67 | ); 68 | 69 | Align alignAtCenter({ 70 | Key? key, 71 | double? heightFactor, 72 | double? widthFactor, 73 | }) => 74 | Align( 75 | key: key, 76 | heightFactor: heightFactor, 77 | widthFactor: widthFactor, 78 | child: this, 79 | ); 80 | 81 | Align alignAtCenterRight({ 82 | Key? key, 83 | double? heightFactor, 84 | double? widthFactor, 85 | }) => 86 | Align( 87 | key: key, 88 | alignment: Alignment.centerRight, 89 | heightFactor: heightFactor, 90 | widthFactor: widthFactor, 91 | child: this, 92 | ); 93 | 94 | Align alignAtLERP( 95 | Alignment a, 96 | Alignment b, 97 | double t, { 98 | Key? key, 99 | double? heightFactor, 100 | double? widthFactor, 101 | }) => 102 | Align( 103 | key: key, 104 | alignment: Alignment.lerp(a, b, t)!, 105 | heightFactor: heightFactor, 106 | widthFactor: widthFactor, 107 | child: this, 108 | ); 109 | 110 | Align alignXY( 111 | double x, 112 | double y, { 113 | Key? key, 114 | double? heightFactor, 115 | double? widthFactor, 116 | }) => 117 | Align( 118 | key: key, 119 | alignment: Alignment(x, y), 120 | heightFactor: heightFactor, 121 | widthFactor: widthFactor, 122 | child: this, 123 | ); 124 | 125 | Align alignAtTopCenter({ 126 | Key? key, 127 | double? heightFactor, 128 | double? widthFactor, 129 | }) => 130 | Align( 131 | key: key, 132 | alignment: Alignment.topCenter, 133 | heightFactor: heightFactor, 134 | widthFactor: widthFactor, 135 | child: this, 136 | ); 137 | 138 | Align alignAtTopRight({ 139 | Key? key, 140 | double? heightFactor, 141 | double? widthFactor, 142 | }) => 143 | Align( 144 | key: key, 145 | alignment: Alignment.topRight, 146 | heightFactor: heightFactor, 147 | widthFactor: widthFactor, 148 | child: this, 149 | ); 150 | } 151 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/colors/colors.dart: -------------------------------------------------------------------------------- 1 | export 'on_colors.dart'; 2 | export 'on_string.dart'; 3 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/directionality.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: depend_on_referenced_packages 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart' as intl; 4 | 5 | /// DHUIntlTextDirectionExtensions 6 | extension DHUIntlTextDirectionExtensions on intl.TextDirection { 7 | TextDirection get toEnum => switch (this) { 8 | intl.TextDirection.RTL => TextDirection.rtl, 9 | _ => TextDirection.ltr, 10 | }; 11 | } 12 | 13 | /// A collection of utility extensions for working with text directionality and localization. 14 | /// 15 | /// These extensions provide convenient methods for handling RTL/LTR text direction 16 | /// and locale-specific functionality in Flutter applications. 17 | /// 18 | /// Example usage: 19 | /// ```dart 20 | /// // Check text direction 21 | /// if (context.isRTL) { 22 | /// // Handle RTL layout 23 | /// } 24 | /// 25 | /// // Get locale information 26 | /// print(context.languageCode); // e.g., 'en' 27 | /// ``` 28 | extension FHUTextDirection on TextDirection { 29 | /// Whether the text direction is right-to-left. 30 | bool get isRTL => this == TextDirection.rtl; 31 | 32 | /// Whether the text direction is left-to-right. 33 | bool get isLTR => this == TextDirection.ltr; 34 | 35 | /// Converts the text direction to a string representation. 36 | String get name => this == TextDirection.ltr ? 'ltr' : 'rtl'; 37 | 38 | /// Returns the opposite text direction. 39 | TextDirection get opposite => isRTL ? TextDirection.ltr : TextDirection.rtl; 40 | 41 | /// Returns a multiplier (-1 or 1) useful for RTL-aware calculations. 42 | /// 43 | /// Returns: 44 | /// * 1 for LTR 45 | /// * -1 for RTL 46 | double get multiplier => isRTL ? -1.0 : 1.0; 47 | } 48 | 49 | /// Extension methods for accessing text directionality from a [BuildContext]. 50 | extension FHUContextDirectionality on BuildContext { 51 | /// The current text direction from the ambient [Directionality]. 52 | TextDirection get directionality => Directionality.of(this); 53 | 54 | /// Whether the current text direction is left-to-right. 55 | bool get isLTR => directionality.isLTR; 56 | 57 | /// Whether the current text direction is right-to-left. 58 | bool get isRTL => directionality.isRTL; 59 | 60 | /// The current locale from [Localizations]. 61 | Locale get locale => Localizations.localeOf(this); 62 | 63 | /// A string representation of the current locale. 64 | /// 65 | /// Examples: 66 | /// * "en" for English 67 | /// * "en_US" for US English 68 | /// * "ar" for Arabic 69 | /// * "zh_Hant" for Traditional Chinese 70 | String get localeString { 71 | final currentLocale = locale; 72 | if (currentLocale.scriptCode != null) { 73 | return '${currentLocale.languageCode}_${currentLocale.scriptCode}'; 74 | } else if (currentLocale.countryCode != null) { 75 | return '${currentLocale.languageCode}_${currentLocale.countryCode}'; 76 | } 77 | return currentLocale.languageCode; 78 | } 79 | 80 | /// The language code of the current locale (e.g., "en" for English). 81 | String get languageCode => locale.languageCode; 82 | 83 | /// The country code of the current locale (e.g., "US" for United States). 84 | String? get countryCode => locale.countryCode; 85 | 86 | /// The script code of the current locale (e.g., "Hant" for Traditional Chinese). 87 | String? get scriptCode => locale.scriptCode; 88 | 89 | /// Returns true if the current locale matches the given language code. 90 | bool isLanguageCode(String languageCode) => this.languageCode == languageCode; 91 | 92 | /// Returns true if the current locale matches the given script code. 93 | bool isScriptCode(String scriptCode) => this.scriptCode == scriptCode; 94 | 95 | /// Returns true if the current locale matches the given country code. 96 | bool isCountryCode(String? countryCode) => this.countryCode == countryCode; 97 | 98 | /// Helper for RTL-aware logical start position. 99 | /// Returns 1.0 for RTL and 0.0 for LTR. 100 | double get logicalStart => isRTL ? 1.0 : 0.0; 101 | 102 | /// Helper for RTL-aware logical end position. 103 | /// Returns 0.0 for RTL and 1.0 for LTR. 104 | double get logicalEnd => isRTL ? 0.0 : 1.0; 105 | 106 | /// Returns an RTL-aware alignment for logical start position. 107 | AlignmentGeometry get logicalStartAlignment => 108 | isRTL ? AlignmentDirectional.centerEnd : AlignmentDirectional.centerStart; 109 | 110 | /// Returns an RTL-aware alignment for logical end position. 111 | AlignmentGeometry get logicalEndAlignment => 112 | isRTL ? AlignmentDirectional.centerStart : AlignmentDirectional.centerEnd; 113 | 114 | /// Creates RTL-aware directional padding. 115 | /// 116 | /// Example: 117 | /// ```dart 118 | /// Container( 119 | /// padding: context.logicalPadding(start: 16, end: 8), 120 | /// child: Text('Hello'), 121 | /// ) 122 | /// ``` 123 | EdgeInsetsGeometry logicalPadding({ 124 | double start = 0.0, 125 | double end = 0.0, 126 | double top = 0.0, 127 | double bottom = 0.0, 128 | }) => 129 | EdgeInsetsDirectional.fromSTEB(start, top, end, bottom); 130 | 131 | /// Creates RTL-aware directional margin. 132 | EdgeInsetsGeometry logicalMargin({ 133 | double start = 0.0, 134 | double end = 0.0, 135 | double top = 0.0, 136 | double bottom = 0.0, 137 | }) => 138 | EdgeInsetsDirectional.fromSTEB(start, top, end, bottom); 139 | 140 | /// Common language checks 141 | bool get isArabic => languageCode == 'ar'; 142 | 143 | bool get isEnglish => languageCode == 'en'; 144 | 145 | bool get isPersian => languageCode == 'fa'; 146 | 147 | bool get isHebrew => languageCode == 'he'; 148 | 149 | bool get isUrdu => languageCode == 'ur'; 150 | 151 | /// Returns true if the current language is typically written RTL. 152 | bool get isRTLLanguage => isArabic || isPersian || isHebrew || isUrdu; 153 | 154 | /// Returns a direction-aware translation offset. 155 | /// 156 | /// Useful for animations and positioning that need to respect text direction. 157 | Offset directionAwareOffset(double x, double y) => 158 | Offset(x * directionality.multiplier, y); 159 | 160 | /// Returns a size adjusted for text direction. 161 | /// 162 | /// Useful when you need to flip dimensions based on text direction. 163 | Size directionAwareSize(double width, double height) => 164 | isRTL ? Size(height, width) : Size(width, height); 165 | } 166 | 167 | /// Extension for RTL-aware list operations. 168 | extension FHUDirectionalIterable on Iterable { 169 | /// Returns the iterable in logical order based on the given text direction. 170 | /// 171 | /// For RTL, the iterable is reversed. 172 | Iterable inDirection(TextDirection direction) => 173 | direction.isRTL ? toList().reversed : this; 174 | 175 | /// Returns the iterable in logical order based on the current context's direction. 176 | List inContextDirection(BuildContext context) => 177 | inDirection(context.directionality).toList(); 178 | } 179 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/flutter_extensions.dart: -------------------------------------------------------------------------------- 1 | export 'align.dart'; 2 | export 'carousel_controller.dart'; 3 | export 'colors/colors.dart'; 4 | export 'directionality.dart'; 5 | export 'focus_scope_extensions.dart'; 6 | export 'future.dart'; 7 | export 'list_widget.dart'; 8 | export 'media_query_extension.dart'; 9 | export 'navigation.dart'; 10 | export 'on_numbers.dart'; 11 | export 'padding.dart'; 12 | export 'platform_extension.dart'; 13 | export 'scaffold_messenger_extension.dart'; 14 | export 'scroll_extensions.dart'; 15 | export 'theme_extension.dart'; 16 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/focus_scope_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension FHUFocusScopeExtension on BuildContext { 4 | /// Returns the [FocusNode.nearestScope] of the [Focus] or [FocusScope] that 5 | /// most tightly encloses the given [context]. 6 | /// 7 | /// If this node doesn't have a [Focus] or [FocusScope] widget ancestor, then 8 | /// the [FocusManager.rootScope] is returned. 9 | FocusScopeNode get focusScope => FocusScope.of(this); 10 | 11 | /// Removes the focus on this node by moving the primary focus to another node. 12 | /// 13 | /// This method removes focus from a node that has the primary focus, cancels 14 | /// any outstanding requests to focus it, while setting the primary focus to 15 | /// another node according to the `disposition`. 16 | /// 17 | /// It is safe to call regardless of whether this node has ever requested 18 | /// focus or not. If this node doesn't have focus or primary focus, nothing 19 | /// happens. 20 | void unFocus() => focusScope.unfocus(); 21 | 22 | /// is commonly used to hide keyboard on onTap/onPress call. Usage could be 23 | /// `onTap: () => context.requestFocus` or `onTap: context.requestFocusCall`. 24 | void requestFocus() => focusScope.requestFocus(FocusNode()); 25 | 26 | GestureTapCallback get requestFocusCall => 27 | () => focusScope.requestFocus(FocusNode()); 28 | 29 | /// Whether this node has input focus. 30 | /// 31 | /// A [FocusNode] has focus when it is an ancestor of a node that returns true 32 | /// from [hasPrimaryFocus], or it has the primary focus itself. 33 | bool get hasFocus => focusScope.hasFocus; 34 | 35 | /// Returns true if this node currently has the application-wide input focus. 36 | /// 37 | /// A [FocusNode] has the primary focus when the node is focused in its 38 | /// nearest ancestor [FocusScopeNode] and [hasFocus] is true for all its 39 | /// ancestor nodes, but none of its descendants. 40 | /// 41 | /// This is different from [hasFocus] in that [hasFocus] is true if the node 42 | /// is anywhere in the focus chain, but here the node has to be at the end of 43 | /// the chain to return true. 44 | bool get hasPrimaryFocus => focusScope.hasPrimaryFocus; 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/future.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | /// Extension on [Future] that provides useful utilities and shortcuts for productivity. 6 | extension FHUFuture on Future { 7 | /// Builds a [FutureBuilder] widget for this [Future]. 8 | /// 9 | /// The [builder] function defines how the UI should update based on the [AsyncSnapshot]. 10 | /// [initialData] is the initial data used to create the snapshot before the [Future] completes. 11 | Widget builder( 12 | AsyncWidgetBuilder builder, { 13 | T? initialData, 14 | }) => 15 | FutureBuilder( 16 | future: this, 17 | builder: builder, 18 | initialData: initialData, 19 | ); 20 | 21 | /// Transforms the result of the [Future] using the provided [transformer] function. 22 | /// 23 | /// Returns a new [Future] of the transformed result. 24 | Future map(R Function(T value) transformer) { 25 | return then((value) => transformer(value)); 26 | } 27 | 28 | /// Builds a widget based on the snapshot state of this [Future] using provided callbacks. 29 | /// 30 | /// - [onSuccess] is called when the [Future] completes successfully with data. 31 | /// - [onError] is called if the [Future] completes with an error. 32 | /// - [onLoading] is displayed while the [Future] is in progress. 33 | Widget buildWidget({ 34 | required Widget Function(BuildContext context, T data) onSuccess, 35 | required Widget Function(BuildContext context, Object? error) onError, 36 | Widget Function(BuildContext context)? onLoading, 37 | }) { 38 | return builder( 39 | (context, snapshot) => snapshot.buildWidget( 40 | onSuccess: (data) => onSuccess.call(context, data), 41 | onError: (error) => onError.call(context, error), 42 | onLoading: onLoading == null ? null : () => onLoading.call(context)), 43 | ); 44 | } 45 | } 46 | 47 | /// Extension on [ConnectionState] providing utility properties for checking the connection state. 48 | extension FHUConnectionState on ConnectionState { 49 | /// Returns true if the connection state is [ConnectionState.none]. 50 | bool get isNone => this == ConnectionState.none; 51 | 52 | /// Returns true if the connection state is [ConnectionState.waiting]. 53 | bool get isWaiting => this == ConnectionState.waiting; 54 | 55 | /// Returns true if the connection state is [ConnectionState.active]. 56 | bool get isActive => this == ConnectionState.active; 57 | 58 | /// Returns true if the connection state is [ConnectionState.done]. 59 | bool get isDone => this == ConnectionState.done; 60 | } 61 | 62 | /// Extension on [AsyncSnapshot] that provides convenient utilities for checking snapshot states. 63 | extension FHUAsyncSnapshot on AsyncSnapshot { 64 | /// Returns true if the snapshot has no connection state. 65 | bool get isNone => connectionState.isNone; 66 | 67 | /// Returns true if the snapshot is currently waiting for data. 68 | bool get isWaiting => connectionState.isWaiting; 69 | 70 | /// Returns true if the snapshot is active (data is actively streaming). 71 | bool get isActive => connectionState.isActive; 72 | 73 | /// Returns true if the snapshot is done receiving data. 74 | bool get isDone => connectionState.isDone; 75 | 76 | /// Returns true if the snapshot has completed successfully with data, either active or done. 77 | bool get isSuccess => hasData && (isDone || isActive); 78 | 79 | /// Returns true if the snapshot has an error and is done receiving data. 80 | bool get hasErrorAndDone => hasError && isDone; 81 | 82 | /// Returns true if the snapshot is either waiting or active. 83 | bool get isWaitingOrActive => isWaiting || isActive; 84 | 85 | /// Provides access to data with a default value if data is not available. 86 | /// 87 | /// - [defaultValue] is returned if the snapshot does not contain data. 88 | T dataOr(T defaultValue) => hasData ? data! : defaultValue; 89 | 90 | /// Transforms the snapshot's data if available using the provided [transform] function. 91 | /// 92 | /// Returns a transformed result of type [R] if data is available, or null otherwise. 93 | R? mapData(R Function(T data) transform) { 94 | return hasData ? transform(data as T) : null; 95 | } 96 | 97 | /// Handles different snapshot states and returns a result based on the current state. 98 | /// 99 | /// - [none]: Called if the snapshot has no connection. 100 | /// - [waiting]: Called while the snapshot is waiting for data. 101 | /// - [active]: Called when the snapshot is active with data. 102 | /// - [done]: Called when the snapshot is done and has data. 103 | /// - [error]: Called if the snapshot has encountered an error. 104 | /// - [orElse]: Optional fallback for unhandled states. 105 | R dataWhen({ 106 | required R Function() none, 107 | required R Function() waiting, 108 | required R Function(T data) active, 109 | required R Function(T data) done, 110 | required R Function(Object error) error, 111 | R Function()? orElse, 112 | }) { 113 | if (hasError) return error(this.error!); 114 | 115 | if (isSuccess) { 116 | if (isActive) return active(data as T); 117 | if (isDone) return done(data as T); 118 | } 119 | 120 | if (isWaiting) return waiting(); 121 | if (isNone) return none(); 122 | if (orElse != null) return orElse(); 123 | 124 | return none(); 125 | } 126 | 127 | /// Builds a widget based on the snapshot state using provided callbacks. 128 | /// 129 | /// - [onSuccess]: Called when the snapshot has data, in either active or done state. 130 | /// - [onError]: Called if the snapshot has an error. 131 | /// - [onLoading]: Optional, displayed while waiting for data; defaults to a [CircularProgressIndicator] if not provided. 132 | Widget buildWidget({ 133 | required Widget Function(T data) onSuccess, 134 | required Widget Function(Object? error) onError, 135 | Widget Function()? onLoading, 136 | }) { 137 | if (hasError) return onError(error); 138 | if (hasData && (isActive || isDone)) onSuccess.call(data as T); 139 | return onLoading?.call() ?? 140 | const Center(child: CircularProgressIndicator()); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/list_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | extension FHUListExtensions on List { 5 | Widget toRow({ 6 | Key? key, 7 | MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 8 | MainAxisSize mainAxisSize = MainAxisSize.max, 9 | CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, 10 | TextDirection? textDirection, 11 | VerticalDirection verticalDirection = VerticalDirection.down, 12 | TextBaseline? textBaseline, 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 toStack({ 26 | Key? key, 27 | AlignmentGeometry alignment = AlignmentDirectional.topStart, 28 | TextDirection? textDirection, 29 | StackFit fit = StackFit.loose, 30 | Clip clip = Clip.hardEdge, 31 | }) => 32 | Stack( 33 | key: key, 34 | alignment: alignment, 35 | textDirection: textDirection, 36 | fit: fit, 37 | clipBehavior: clip, 38 | children: this, 39 | ); 40 | 41 | Widget toColumn({ 42 | Key? key, 43 | MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 44 | MainAxisSize mainAxisSize = MainAxisSize.max, 45 | CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, 46 | TextDirection? textDirection, 47 | VerticalDirection verticalDirection = VerticalDirection.down, 48 | TextBaseline? textBaseline, 49 | }) => 50 | Column( 51 | key: key, 52 | mainAxisAlignment: mainAxisAlignment, 53 | mainAxisSize: mainAxisSize, 54 | crossAxisAlignment: crossAxisAlignment, 55 | textDirection: textDirection, 56 | verticalDirection: verticalDirection, 57 | textBaseline: textBaseline, 58 | children: this, 59 | ); 60 | 61 | Widget toList({ 62 | Key? key, 63 | Axis scrollDirection = Axis.vertical, 64 | bool reverse = false, 65 | ScrollController? controller, 66 | bool? primary, 67 | ScrollPhysics? physics, 68 | bool shrinkWrap = false, 69 | EdgeInsetsGeometry? padding, 70 | double? itemExtent, 71 | Widget? prototypeItem, 72 | double? cacheExtent, 73 | DragStartBehavior dragStartBehavior = DragStartBehavior.start, 74 | ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = 75 | ScrollViewKeyboardDismissBehavior.manual, 76 | String? restorationId, 77 | Clip clipBehavior = Clip.hardEdge, 78 | bool addAutomaticKeepAlives = true, 79 | bool addRepaintBoundaries = true, 80 | bool addSemanticIndexes = true, 81 | int? semanticChildCount, 82 | }) => 83 | ListView( 84 | key: key, 85 | scrollDirection: scrollDirection, 86 | reverse: reverse, 87 | controller: controller, 88 | primary: primary, 89 | physics: physics, 90 | shrinkWrap: shrinkWrap, 91 | padding: padding, 92 | itemExtent: itemExtent, 93 | prototypeItem: prototypeItem, 94 | cacheExtent: cacheExtent, 95 | dragStartBehavior: dragStartBehavior, 96 | keyboardDismissBehavior: keyboardDismissBehavior, 97 | restorationId: restorationId, 98 | clipBehavior: clipBehavior, 99 | addAutomaticKeepAlives: addAutomaticKeepAlives, 100 | addRepaintBoundaries: addRepaintBoundaries, 101 | addSemanticIndexes: addSemanticIndexes, 102 | semanticChildCount: semanticChildCount, 103 | children: this, 104 | ); 105 | 106 | Widget toListView({ 107 | required NullableIndexedWidgetBuilder itemBuilder, 108 | Key? key, 109 | Axis scrollDirection = Axis.vertical, 110 | bool reverse = false, 111 | ScrollController? controller, 112 | bool? primary, 113 | ScrollPhysics? physics, 114 | bool shrinkWrap = false, 115 | EdgeInsetsGeometry? padding, 116 | double? itemExtent, 117 | Widget? prototypeItem, 118 | double? cacheExtent, 119 | DragStartBehavior dragStartBehavior = DragStartBehavior.start, 120 | ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = 121 | ScrollViewKeyboardDismissBehavior.manual, 122 | String? restorationId, 123 | Clip clipBehavior = Clip.hardEdge, 124 | bool addAutomaticKeepAlives = true, 125 | bool addRepaintBoundaries = true, 126 | bool addSemanticIndexes = true, 127 | int? semanticChildCount, 128 | ChildIndexGetter? findChildIndexCallback, 129 | int? itemCount, 130 | }) => 131 | ListView.builder( 132 | key: key, 133 | scrollDirection: scrollDirection, 134 | reverse: reverse, 135 | controller: controller, 136 | primary: primary, 137 | physics: physics, 138 | shrinkWrap: shrinkWrap, 139 | padding: padding, 140 | itemExtent: itemExtent, 141 | prototypeItem: prototypeItem, 142 | cacheExtent: cacheExtent, 143 | dragStartBehavior: dragStartBehavior, 144 | keyboardDismissBehavior: keyboardDismissBehavior, 145 | restorationId: restorationId, 146 | clipBehavior: clipBehavior, 147 | addAutomaticKeepAlives: addAutomaticKeepAlives, 148 | addRepaintBoundaries: addRepaintBoundaries, 149 | addSemanticIndexes: addSemanticIndexes, 150 | semanticChildCount: semanticChildCount, 151 | findChildIndexCallback: findChildIndexCallback, 152 | itemCount: itemCount ?? length, 153 | itemBuilder: itemBuilder, 154 | ); 155 | } 156 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/padding.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension FHUPaddingExtensions on Widget { 4 | Padding paddingAll(double value, {Key? key}) { 5 | return Padding( 6 | key: key, 7 | padding: EdgeInsets.all(value), 8 | child: this, 9 | ); 10 | } 11 | 12 | Padding paddingLTRB( 13 | double left, 14 | double top, 15 | double right, 16 | double bottom, { 17 | Key? key, 18 | }) => 19 | Padding( 20 | key: key, 21 | padding: EdgeInsets.fromLTRB(left, top, right, bottom), 22 | child: this, 23 | ); 24 | 25 | Padding paddingSymmetric({ 26 | Key? key, 27 | double v = 0.0, 28 | double h = 0.0, 29 | }) => 30 | Padding( 31 | key: key, 32 | padding: EdgeInsets.symmetric( 33 | vertical: v, 34 | horizontal: h, 35 | ), 36 | child: this, 37 | ); 38 | 39 | Padding paddingOnly({ 40 | Key? key, 41 | double left = 0.0, 42 | double right = 0.0, 43 | double top = 0.0, 44 | double bottom = 0.0, 45 | }) => 46 | Padding( 47 | key: key, 48 | padding: 49 | EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), 50 | child: this, 51 | ); 52 | } 53 | 54 | EdgeInsets edgeInsetsFlexible({ 55 | double? all, 56 | double? horizontal, 57 | double? vertical, 58 | double? top, 59 | double? bottom, 60 | double? left, 61 | double? right, 62 | }) { 63 | // Priority: 64 | // 1. 'all' 65 | // 2. Symmetrical values (if applicable) 66 | // 3. Individual values (with fallbacks to 0 if not provided) 67 | 68 | if (all != null) { 69 | return EdgeInsets.all(all); 70 | } 71 | 72 | if (horizontal != null || vertical != null) { 73 | return EdgeInsets.symmetric( 74 | horizontal: horizontal ?? 0.0, 75 | vertical: vertical ?? 0.0, 76 | ); 77 | } 78 | 79 | // If no symmetrical values, use individual ones 80 | return EdgeInsets.fromLTRB( 81 | left ?? 0.0, 82 | top ?? 0.0, 83 | right ?? 0.0, 84 | bottom ?? 0.0, 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/extensions/flutter_extensions/scaffold_messenger_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension FHUScaffoldMessengerExtension on BuildContext { 4 | /// The state from the closest instance of this class that encloses the given 5 | /// context. 6 | ScaffoldMessengerState get scaffoldMessenger => ScaffoldMessenger.of(this); 7 | 8 | /// Shows a [MaterialBanner] across all registered [Scaffold]s. Scaffolds register 9 | /// to receive material banners from their closest [ScaffoldMessenger] ancestor. 10 | /// If there are several registered scaffolds the material banner is shown 11 | /// simultaneously on all of them. 12 | ScaffoldFeatureController 13 | showMaterialBanner(MaterialBanner materialBanner) => 14 | scaffoldMessenger.showMaterialBanner(materialBanner); 15 | 16 | /// Shows a [SnackBar] across all registered [Scaffold]s. Scaffolds register 17 | /// to receive snack bars from their closest [ScaffoldMessenger] ancestor. 18 | /// If there are several registered scaffolds the snack bar is shown 19 | /// simultaneously on all of them. 20 | ScaffoldFeatureController showSnackBar( 21 | SnackBar snackBar, 22 | ) => 23 | scaffoldMessenger.showSnackBar(snackBar); 24 | 25 | /// Removes the current [MaterialBanner] by running its normal exit animation. 26 | /// 27 | /// The closed completer is called after the animation is complete. 28 | void hideCurrentMaterialBanner() => 29 | scaffoldMessenger.hideCurrentMaterialBanner(); 30 | 31 | /// Removes the current [SnackBar] by running its normal exit animation. 32 | /// 33 | /// The closed completer is called after the animation is complete. 34 | void hideCurrentSnackBar() => scaffoldMessenger.hideCurrentSnackBar(); 35 | 36 | /// Removes the current [MaterialBanner] (if any) immediately from registered 37 | /// [Scaffold]s. 38 | /// 39 | /// The removed material banner does not run its normal exit animation. If there are 40 | /// any queued material banners, they begin their entrance animation immediately. 41 | void removeCurrentMaterialBanner() => 42 | scaffoldMessenger.removeCurrentMaterialBanner(); 43 | 44 | /// Removes the current [SnackBar] (if any) immediately from registered 45 | /// [Scaffold]s. 46 | /// 47 | /// The removed snack bar does not run its normal exit animation. If there are 48 | /// any queued snack bars, they begin their entrance animation immediately. 49 | void removeCurrentSnackBar() => scaffoldMessenger.removeCurrentSnackBar(); 50 | 51 | /// Removes all the [MaterialBanner]s currently in queue by clearing the queue 52 | /// and running normal exit animation on the current [MaterialBanner]. 53 | void clearMaterialBanners() => scaffoldMessenger.clearMaterialBanners(); 54 | 55 | /// Removes all the snackBars currently in queue by clearing the queue 56 | /// and running normal exit animation on the current snackBar. 57 | void clearSnackBars() => scaffoldMessenger.clearSnackBars(); 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/extensions/num_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | extension FHUIntExtensions on int { 4 | /// Construct a color from the lower 32 bits of an [int]. 5 | /// 6 | /// The bits are interpreted as follows: 7 | /// 8 | /// * Bits 24-31 are the alpha value. 9 | /// * Bits 16-23 are the red value. 10 | /// * Bits 8-15 are the green value. 11 | /// * Bits 0-7 are the blue value. 12 | /// 13 | /// In other words, if AA is the alpha value in hex, RR the red value in hex, 14 | /// GG the green value in hex, and BB the blue value in hex, a color can be 15 | /// expressed as `const Color(0xAARRGGBB)`. 16 | /// 17 | /// For example, to get a fully opaque orange, you would use `const 18 | /// Color(0xFFFF9000)` (`FF` for the alpha, `FF` for the red, `90` for the 19 | /// green, and `00` for the blue). 20 | Color get color => Color(this); 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/extensions/string_extensions/general_string_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension FHUNullSafeStringExtensions on String? { 4 | // all string extensions could be found in the https:// 5 | // String get withoutWhiteSpaces => isEmptyOrNull ? '' : this!.replaceAll(RegExp(r'\s+\b|\b\s'), ''); 6 | Size get textSize { 7 | final textPainter = TextPainter( 8 | text: TextSpan(text: this), 9 | maxLines: 1, 10 | )..layout(); 11 | return textPainter.size; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/extensions/string_extensions/string_extensions.dart: -------------------------------------------------------------------------------- 1 | export 'general_string_extensions.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/platform_env/platform_env.dart: -------------------------------------------------------------------------------- 1 | export 'platform_env_io.dart' if (dart.library.html) 'platform_env_web.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/platform_env/platform_env_io.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | /// A platform-independent class providing information about the operating system, 6 | /// with web compatibility considerations. 7 | abstract class PlatformEnv { 8 | /// Returns the `TargetPlatform` corresponding to the current operating system. 9 | static TargetPlatform get targetPlatform { 10 | switch (operatingSystem) { 11 | case 'android': 12 | return TargetPlatform.android; 13 | case 'fuchsia': 14 | return TargetPlatform.fuchsia; 15 | case 'ios': 16 | return TargetPlatform.iOS; 17 | case 'linux': 18 | return TargetPlatform.linux; 19 | case 'macos': 20 | return TargetPlatform.macOS; 21 | case 'windows': 22 | return TargetPlatform.windows; 23 | default: 24 | return defaultTargetPlatform; 25 | } 26 | } 27 | 28 | /// Returns `true` if the current platform is Linux. 29 | static bool get isLinux => Platform.isLinux; 30 | 31 | /// Returns `true` if the current platform is macOS. 32 | static bool get isMacOS => Platform.isMacOS; 33 | 34 | /// Returns `true` if the current platform is Windows. 35 | static bool get isWindows => Platform.isWindows; 36 | 37 | /// Returns `true` if the current platform is Android. 38 | static bool get isAndroid => Platform.isAndroid; 39 | 40 | /// Returns `true` if the current platform is iOS. 41 | static bool get isIOS => Platform.isIOS; 42 | 43 | /// Returns `true` if the current platform is not web. 44 | static bool get isWeb => false; 45 | 46 | /// Returns `true` if the current platform is Fuchsia. 47 | static bool get isFuchsia => Platform.isFuchsia; 48 | 49 | /// Returns the name of the operating system. 50 | static String get operatingSystem => Platform.operatingSystem; 51 | 52 | /// Returns the version of the operating system. 53 | static String get operatingSystemVersion => Platform.operatingSystemVersion; 54 | 55 | /// Returns the number of processors available to the Dart VM. 56 | static int get numberOfProcessors => Platform.numberOfProcessors; 57 | 58 | /// Returns the path separator used by the operating system. 59 | static String get pathSeparator => Platform.pathSeparator; 60 | 61 | /// Returns the local hostname for the system. 62 | static String get localHostname => Platform.localHostname; 63 | 64 | /// Returns the version of the current Dart runtime. 65 | static String get version => Platform.version; 66 | 67 | /// Returns the name of the current locale. 68 | static String get localeName => Platform.localeName; 69 | 70 | /// Returns the environment for this process as a map. 71 | static Map get environment => Platform.environment; 72 | 73 | /// Returns the path of the executable used to run the script in this isolate. 74 | static String get executable => Platform.executable; 75 | 76 | /// Returns the path of the executable used to run the script in this isolate after it has been resolved by the OS. 77 | static String get resolvedExecutable => Platform.resolvedExecutable; 78 | 79 | /// Returns the absolute URI of the script being run in this isolate. 80 | static Uri get script => Platform.script; 81 | 82 | /// Returns the flags passed to the executable used to run the script in this isolate. 83 | static List get executableArguments => Platform.executableArguments; 84 | 85 | /// Returns the `--packages` flag passed to the executable used to run the script in this isolate, or `null` if no such flag was provided. 86 | static String? get packageConfig => Platform.packageConfig; 87 | 88 | /// Returns the system's default line terminator. 89 | static String get lineTerminator => Platform.isWindows ? '\r\n' : '\n'; 90 | 91 | static Map report() => { 92 | 'targetPlatform': targetPlatform.name, 93 | 'isLinux': '$isLinux', 94 | 'isMacOS': '$isMacOS', 95 | 'isWindows': '$isWindows', 96 | 'isAndroid': '$isAndroid', 97 | 'isIOS': '$isIOS', 98 | 'isWeb': '$isWeb', 99 | 'isFuchsia': '$isFuchsia', 100 | 'operatingSystem': operatingSystem, 101 | 'operatingSystemVersion': operatingSystemVersion, 102 | 'numberOfProcessors': '$numberOfProcessors', 103 | 'pathSeparator': pathSeparator, 104 | 'localHostname': localHostname, 105 | 'version': version, 106 | 'localeName': localeName, 107 | ...environment, 108 | 'executable': executable, 109 | 'resolvedExecutable': resolvedExecutable, 110 | 'script': '$script', 111 | 'executableArguments': '$executableArguments', 112 | 'packageConfig': '$packageConfig', 113 | 'lineTerminator': lineTerminator, 114 | }; 115 | } 116 | -------------------------------------------------------------------------------- /lib/src/platform_env/platform_env_web.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | /// A platform-independent class providing information about the operating system, 4 | /// with web compatibility considerations. 5 | abstract class PlatformEnv { 6 | /// Returns the `TargetPlatform` corresponding to the current operating system. 7 | static TargetPlatform get targetPlatform => defaultTargetPlatform; 8 | 9 | /// Returns `false` since the current platform is not Linux. 10 | static bool get isLinux => false; 11 | 12 | /// Returns `false` since the current platform is not macOS. 13 | static bool get isMacOS => false; 14 | 15 | /// Returns `false` since the current platform is not Windows. 16 | static bool get isWindows => false; 17 | 18 | /// Returns `false` since the current platform is not Android. 19 | static bool get isAndroid => false; 20 | 21 | /// Returns `false` since the current platform is not iOS. 22 | static bool get isIOS => false; 23 | 24 | /// Returns `true` since the current platform is web. 25 | static bool get isWeb => true; 26 | 27 | /// Returns `false` since the current platform is not Fuchsia. 28 | static bool get isFuchsia => false; 29 | 30 | /// Returns the string 'web' as the operating system. 31 | static String get operatingSystem => 'web'; 32 | 33 | /// Returns 'N/A' as the operating system version on web platforms. 34 | static String get operatingSystemVersion => 'N/A'; 35 | 36 | /// Returns `0` since the number of processors is not available on web platforms. 37 | static int get numberOfProcessors => 0; 38 | 39 | /// Returns `/` as the path separator on web platforms. 40 | static String get pathSeparator => '/'; 41 | 42 | /// Returns 'localhost' as the local hostname on web platforms. 43 | static String get localHostname => 'localhost'; 44 | 45 | /// Returns 'N/A' as the version of the current Dart runtime on web platforms. 46 | static String get version => 'N/A'; 47 | 48 | /// Returns 'en' as the default locale name on web platforms. 49 | static String get localeName => 'en'; 50 | 51 | /// Returns an empty map since environment variables are not available on web platforms. 52 | static Map get environment => {}; 53 | 54 | /// Returns 'N/A' as the executable path on web platforms. 55 | static String get executable => 'N/A'; 56 | 57 | /// Returns 'N/A' as the resolved executable path on web platforms. 58 | static String get resolvedExecutable => 'N/A'; 59 | 60 | /// Returns an empty URI as the script URI on web platforms. 61 | static Uri get script => Uri(); 62 | 63 | /// Returns an empty list since no executable arguments are available on web platforms. 64 | static List get executableArguments => []; 65 | 66 | /// Returns `null` since the `--packages` flag is not applicable on web platforms. 67 | static String? get packageConfig => null; 68 | 69 | /// Returns `'\n'` as the system's default line terminator on web platforms. 70 | static String get lineTerminator => '\n'; 71 | 72 | static Map report() => { 73 | 'targetPlatform': targetPlatform.name, 74 | 'operatingSystem': operatingSystem, 75 | 'operatingSystemVersion': operatingSystemVersion, 76 | 'numberOfProcessors': '$numberOfProcessors', 77 | 'pathSeparator': pathSeparator, 78 | 'localHostname': localHostname, 79 | 'version': version, 80 | 'localeName': localeName, 81 | ...environment, 82 | 'executable': executable, 83 | 'resolvedExecutable': resolvedExecutable, 84 | 'script': '$script', 85 | 'executableArguments': '$executableArguments', 86 | 'packageConfig': '$packageConfig', 87 | 'lineTerminator': lineTerminator, 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /lib/src/src.dart: -------------------------------------------------------------------------------- 1 | export 'package:dart_helper_utils/dart_helper_utils.dart'; 2 | 3 | export 'extensions/extensions.dart'; 4 | export 'platform_env/platform_env.dart'; 5 | export 'value_notifier/value_notifier.dart'; 6 | export 'widgets/widgets.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/value_notifier/extensions/extensions.dart: -------------------------------------------------------------------------------- 1 | export 'listenable_extensions.dart'; 2 | export 'notifier_extensions.dart'; 3 | export 'stream_to_notifier.dart'; 4 | export 'value_notifier_extensions.dart'; 5 | -------------------------------------------------------------------------------- /lib/src/value_notifier/extensions/listenable_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_helper_utils/flutter_helper_utils.dart'; 4 | 5 | /// Extension on [Listenable] to integrate with [ListenableBuilder]. 6 | /// 7 | /// Provides a convenient method to create a [ListenableBuilder] that listens to the [Listenable]. 8 | /// This extension simplifies reacting to changes in the [Listenable] within the UI. 9 | extension FHUListenableExtension on Listenable { 10 | /// Creates a [ListenableBuilder] widget that listens to this [Listenable]. 11 | /// 12 | /// The [builder] function is called whenever this [Listenable] changes, allowing the UI to react to these changes. 13 | /// 14 | /// Example: 15 | /// ```dart 16 | /// final myNotifier = ValueNotifier(0); 17 | /// myNotifier.builder( 18 | /// (context) => Text('Value is ${myNotifier.value}'), 19 | /// ); 20 | /// // The Text widget will update whenever [myNotifier] changes. 21 | /// ``` 22 | Widget builder(Widget Function(BuildContext context) builder) { 23 | return ListenableBuilder( 24 | listenable: this, 25 | builder: (context, child) => builder(context), 26 | ); 27 | } 28 | } 29 | 30 | /// Extension on a list of [Listenable] objects to integrate with [ListenablesBuilder]. 31 | /// 32 | /// Facilitates the creation of a [ListenablesBuilder] widget that listens to multiple [Listenable] instances. 33 | /// Ideal for scenarios where the UI needs to respond to changes in multiple listenable sources. 34 | extension FHUListenablesExtension on List { 35 | /// Creates a [ListenablesBuilder] widget that listens to this list of [Listenable] objects. 36 | /// 37 | /// The [builder] function is called whenever any of the [Listenable] objects in this list change, 38 | /// allowing the UI to react to these changes. Optional parameters [buildWhen] and [threshold] 39 | /// can be used to control when the builder is called. 40 | /// 41 | /// Example: 42 | /// ```dart 43 | /// final textController = TextEditingController(); 44 | /// final scrollController = ScrollController(); 45 | /// final myNotifier = ValueNotifier(0); 46 | /// final myListeners = [textController, scrollController, myNotifier]; 47 | /// myListeners.builder( 48 | /// (context) => Text('Value is ${myNotifier.value}'), 49 | /// ); 50 | /// // The Text widget will update whenever any of myListeners change. 51 | /// ``` 52 | Widget builder( 53 | Widget Function(BuildContext context) builder, { 54 | bool Function()? buildWhen, 55 | Duration? threshold, 56 | }) { 57 | return ListenablesBuilder( 58 | listenables: this, 59 | buildWhen: buildWhen, 60 | threshold: threshold, 61 | builder: builder, 62 | ); 63 | } 64 | } 65 | 66 | /// Extension on [ValueListenable] to seamlessly create a [ValueListenableBuilder] widget. 67 | /// 68 | /// Simplifies the instantiation of [ValueListenableBuilder], allowing for direct UI updates in response to [ValueListenable] changes. 69 | /// Enhances code readability and efficiency by reducing boilerplate. 70 | extension FHUValueListenableExtension on ValueListenable { 71 | /// Creates a [ValueListenableBuilder] widget that listens to this [ValueListenable]. 72 | /// 73 | /// The [builder] function is called with the current value of the [ValueListenable] whenever it changes, 74 | /// allowing the UI to react to these changes. 75 | /// 76 | /// Example: 77 | /// ```dart 78 | /// final myNotifier = ValueNotifier(0); 79 | /// myNotifier.builder( 80 | /// (value) => Text('Value is $value'), 81 | /// ); 82 | /// // The Text widget will update whenever [myNotifier] changes. 83 | /// ``` 84 | Widget builder( 85 | Widget Function(T value) builder, { 86 | Key? key, 87 | }) { 88 | return ValueListenableBuilder( 89 | valueListenable: this, 90 | key: key, 91 | builder: (_, value, __) => builder(value), 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/value_notifier/extensions/notifier_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_helper_utils/dart_helper_utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_helper_utils/src/value_notifier/notifier_classes/notifier_classes.dart'; 4 | 5 | /// Extension on any non-nullable [Object]. 6 | /// 7 | /// Allows you to directly create a [ValueNotifier] instance from the object. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// // Creates ValueNotifier(User()) where User is your custom type/class. 12 | /// final userNotifier = User().notifier; 13 | /// ``` 14 | extension FHUValueNotifierExtension on T { 15 | /// Creates a [ValueNotifier] for the current instance. 16 | ValueNotifier get notifier => ValueNotifier(this); 17 | } 18 | 19 | /// Extension on `bool` to provide easy access to creating a [ValueNotifier] for boolean values. 20 | extension FHUBoolNotifierEx on bool { 21 | /// Creates a [BoolNotifier] instance from the current `bool` value. 22 | BoolNotifier get notifier => BoolNotifier(this); 23 | } 24 | 25 | /// Extension on `num` to provide easy access to creating a [ValueNotifier] for numeric types. 26 | extension FHUNumNotifierEx on num { 27 | /// Creates a [NumNotifier] instance from the current `num` value. 28 | NumNotifier get notifier => NumNotifier(this); 29 | } 30 | 31 | /// Extension on `double` to provide easy access to creating a [ValueNotifier] for double values. 32 | extension FHUDoubleNotifierEx on double { 33 | /// Creates a [DoubleNotifier] instance from the current `double` value. 34 | DoubleNotifier get notifier => DoubleNotifier(this); 35 | } 36 | 37 | /// Extension on `int` to provide easy access to creating a [ValueNotifier] for integer values. 38 | extension FHUIntNotifierEx on int { 39 | /// Creates an [IntNotifier] instance from the current `int` value. 40 | IntNotifier get notifier => IntNotifier(this); 41 | } 42 | 43 | /// Extension on `DateTime` to provide easy access to creating a [ValueNotifier] for `DateTime` objects. 44 | extension FHUDateTimeNotifierEx on DateTime { 45 | /// Creates a [DateTimeNotifier] instance from the current `DateTime` value. 46 | DateTimeNotifier get notifier => DateTimeNotifier(this); 47 | } 48 | 49 | /// Extension on `String` to provide easy access to creating a [ValueNotifier] for `String` values. 50 | extension FHUStringNotifierEx on String { 51 | /// Creates a [StringNotifier] instance from the current `String` value. 52 | StringNotifier get notifier => StringNotifier(this); 53 | } 54 | 55 | /// Extension on `Color` to provide easy access to creating a [ValueNotifier] for `Color` objects. 56 | extension FHUColorNotifierEx on Color { 57 | /// Creates a [ColorNotifier] instance from the current `Color` value. 58 | ColorNotifier get notifier => ColorNotifier(this); 59 | } 60 | 61 | /// Extension on `Uri` to provide easy access to creating a [ValueNotifier] for `Uri` objects. 62 | extension FHUUriNotifierEx on Uri { 63 | /// Creates a [UriNotifier] instance from the current `Uri` value. 64 | UriNotifier get notifier => UriNotifier(this); 65 | } 66 | 67 | /// Extension on `ThemeMode` to provide easy access to creating a [ValueNotifier] for `ThemeMode`. 68 | extension FHUThemeModeNotifierEx on ThemeMode { 69 | /// Creates a [ThemeModeNotifier] instance from the current `ThemeMode` value. 70 | ThemeModeNotifier get notifier => ThemeModeNotifier(this); 71 | } 72 | 73 | /// Extension on `Brightness` to provide easy access to creating a [ValueNotifier] for `Brightness`. 74 | extension FHUBrightnessNotifierEx on Brightness { 75 | /// Creates a [BrightnessNotifier] instance from the current `Brightness` value. 76 | BrightnessNotifier get notifier => BrightnessNotifier(this); 77 | } 78 | 79 | /// Extension on `Iterable` to provide easy access to creating various [ValueNotifier] instances. 80 | /// 81 | /// - [ListNotifier] for lists. 82 | /// - [SetNotifier] for sets. 83 | /// - [DoublyLinkedListNotifier] for doubly linked lists. 84 | extension FHUIterableNotifierEx on Iterable { 85 | /// Creates a [ListNotifier] instance from the current `Iterable`. 86 | ListNotifier get listNotifier => ListNotifier.from(this); 87 | 88 | /// Creates a [SetNotifier] instance from the current `Iterable`. 89 | SetNotifier get setNotifier => SetNotifier.from(this); 90 | 91 | /// Creates a [DoublyLinkedListNotifier] instance from the current `Iterable`. 92 | DoublyLinkedListNotifier get doublyLinkedListNotifier => 93 | DoublyLinkedListNotifier.from(this); 94 | } 95 | 96 | /// Extension on `List` to provide easy access to creating a [ValueNotifier] for lists. 97 | extension FHUListNotifierEx on List { 98 | /// Creates a [ListNotifier] instance from the current `List`. 99 | ListNotifier get notifier => ListNotifier(this); 100 | } 101 | 102 | /// Extension on `DoublyLinkedList` to provide easy access to creating a [ValueNotifier] for doubly linked lists. 103 | /// 104 | /// [DoublyLinkedList] is a part of the `dart:collection` library. 105 | extension FHUDoublyLinkedListNotifierEx on DoublyLinkedList { 106 | /// Creates a [DoublyLinkedListNotifier] instance from the current `DoublyLinkedList`. 107 | DoublyLinkedListNotifier get notifier => DoublyLinkedListNotifier(this); 108 | } 109 | 110 | /// Extension on `Set` to provide easy access to creating a [ValueNotifier] for sets. 111 | extension FHUSetNotifierEx on Set { 112 | /// Creates a [SetNotifier] instance from the current `Set`. 113 | SetNotifier get notifier => SetNotifier(this); 114 | } 115 | 116 | /// Extension on `Map` to provide easy access to creating a [ValueNotifier] for maps. 117 | /// 118 | /// Allows creating a [MapNotifier] instance from a `Map`. 119 | extension FHUMapNotifierEx on Map { 120 | /// Creates a [MapNotifier] instance from the current `Map`. 121 | MapNotifier get notifier => MapNotifier(this); 122 | } 123 | -------------------------------------------------------------------------------- /lib/src/value_notifier/extensions/stream_to_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | /// Extension on `Stream` to convert any stream into a `ValueNotifier`. 6 | /// 7 | /// This extension provides a convenient way to bridge the reactive world of streams 8 | /// with the `ValueNotifier` pattern used for state management in Flutter applications. By converting 9 | /// a stream into a `ValueNotifier`, you can easily integrate asynchronous stream data into your 10 | /// Flutter widgets with the reactive and efficient update mechanism that `ValueNotifier` provides. 11 | extension FHUStreamToValueNotifier on Stream { 12 | /// Converts the current stream into a `ValueListenable` which is effectively a `ValueNotifier`. 13 | /// 14 | /// The conversion process involves listening to the stream and updating the `ValueNotifier`'s value 15 | /// each time the stream emits a new item. This allows Flutter widgets to reactively rebuild whenever 16 | /// the `ValueNotifier`'s value changes, based on the latest data emitted by the stream. 17 | /// 18 | /// Parameters: 19 | /// - `initialValue`: The initial value to be used for the `ValueNotifier` before any data is received from the stream. 20 | /// - `onDone`: An optional callback that gets called when the stream is done. The last value received 21 | /// from the stream is passed to this callback. 22 | /// - `onError`: An optional error handler for stream errors. If not provided, a default error handler 23 | /// that logs the error is used. 24 | /// 25 | /// Returns a `ValueListenable` which is a `ValueNotifier` that updates its value based on the stream's emissions. 26 | ValueListenable toValueNotifier( 27 | T initialValue, { 28 | void Function(T)? onDone, 29 | void Function(Object, StackTrace)? onError, 30 | }) { 31 | final notifier = ValueNotifier(initialValue); 32 | listen( 33 | (value) => notifier.value = value, 34 | onError: onError ?? _defaultOnError, 35 | onDone: () => onDone?.call(notifier.value), 36 | ); 37 | return notifier; 38 | } 39 | 40 | /// Default error handler that logs any errors coming from the stream. 41 | /// 42 | /// This function is used as the error handler for the stream listener if no custom 43 | /// `onError` function is provided when calling `toValueNotifier`. 44 | /// 45 | /// Parameters: 46 | /// - `error`: The error object emitted by the stream. 47 | /// - `stackTrace`: The stack trace associated with the error. 48 | void _defaultOnError(Object error, StackTrace stackTrace) => log( 49 | 'Error on stream $toString()', 50 | // Log the error with stream identification 51 | error: error, 52 | stackTrace: stackTrace, 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/value_notifier/extensions/value_notifier_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | extension FHUValueNotifierExtensions on ValueNotifier { 6 | /// Equivalent to the getter [value] but in shorter syntax. 7 | T get v => value; 8 | 9 | set v(T newValue) => value = newValue; 10 | } 11 | 12 | extension FHUValueListenableExtensions on ValueListenable { 13 | /// Registers a callback to be invoked whenever the `ValueNotifier`'s value changes. 14 | VoidCallback onChange(void Function(T value) action) { 15 | void listener() => action(value); 16 | addListener(listener); 17 | return () => removeListener(listener); 18 | } 19 | 20 | /// Registers a debounced callback which is invoked only after the notifier's value 21 | /// is stable for the specified [duration]. 22 | VoidCallback debounce(Duration duration, void Function(T value) action) { 23 | Timer? debounceTimer; 24 | 25 | void listener() { 26 | debounceTimer?.cancel(); 27 | debounceTimer = Timer(duration, () => action(value)); 28 | } 29 | 30 | addListener(listener); 31 | 32 | return () => { 33 | debounceTimer?.cancel(), 34 | removeListener(listener), 35 | }; 36 | } 37 | 38 | /// Converts the [ValueNotifier] into a [Stream]. This stream emits values whenever the 39 | /// [value] changes. The use of [distinct] ensures that consecutive duplicate values are 40 | /// filtered out, thus the stream only emits when the value actually changes. 41 | Stream get stream => 42 | Stream.periodic(Duration.zero, (_) => value).distinct(); 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/value_notifier/notifier_classes/bool_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_helper_utils/flutter_helper_utils.dart'; 3 | 4 | /// allows to quickly create a ValueNotifier of type bool. 5 | class BoolNotifier extends ValueNotifier { 6 | BoolNotifier(super.initial); 7 | 8 | @override 9 | void notifyListeners() { 10 | try { 11 | super.notifyListeners(); 12 | } catch (_) {} 13 | } 14 | 15 | void refresh() => notifyListeners(); 16 | 17 | /// similar to value setter but this one force trigger the notifyListeners() 18 | /// event if newValue == value. 19 | void update(bool newValue) { 20 | value = newValue; 21 | refresh(); 22 | } 23 | } 24 | 25 | /// BoolValueNotifierExtension 26 | /// 27 | /// Extension on `ValueNotifier` providing additional boolean-specific functionalities. 28 | /// This extension simplifies toggling and other boolean operations directly on 29 | /// the [ValueNotifier] without the need to perform actions in the value itself. 30 | /// 31 | /// Example: 32 | /// ```dart 33 | /// final boolValueNotifier = true.notifier; 34 | /// boolValueNotifier.toggle(); // Toggles the boolean value. 35 | /// ``` 36 | extension FHUBoolValueNotifierExtension on ValueNotifier { 37 | /// toggle the value of the [ValueNotifier] 38 | void toggle() => value = !value; 39 | 40 | /// toggle the value of the [ValueNotifier] and run the provided function. 41 | void toggleWithCallback(VoidCallback callback) { 42 | toggle(); 43 | callback(); 44 | } 45 | 46 | /// toggle after a specific time. 47 | Future delayedToggle(Duration delay) async { 48 | await delay.delayed(); 49 | toggle(); 50 | } 51 | 52 | /// toggle the value of the ValueNotifier based on a condition. 53 | void conditionalToggle({required bool condition}) { 54 | if (condition) toggle(); 55 | } 56 | 57 | /// make the value of the [ValueNotifier] true 58 | void setTrue() => value = true; 59 | 60 | /// make the value of the [ValueNotifier] false 61 | void setFalse() => value = false; 62 | 63 | /// The logical conjunction ("and") of this and [other]. 64 | /// 65 | /// Returns `true` if both this and [other] are `true`, and `false` otherwise. 66 | bool operator &(bool other) => value & other; 67 | 68 | /// The logical disjunction ("inclusive or") of this and [other]. 69 | /// 70 | /// Returns `true` if either this or [other] is `true`, and `false` otherwise.) 71 | bool operator |(bool other) => value | other; 72 | 73 | /// The logical exclusive disjunction ("exclusive or") of this and [other]. 74 | /// 75 | /// Returns whether this and [other] are neither both `true` nor both `false`. 76 | bool operator ^(bool other) => value ^ other; 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/value_notifier/notifier_classes/color_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter_helper_utils/flutter_helper_utils.dart'; 4 | 5 | /// allows to quickly create a ValueNotifier of type Color. 6 | class ColorNotifier extends ValueNotifier { 7 | ColorNotifier(super.initial); 8 | 9 | @override 10 | void notifyListeners() { 11 | try { 12 | super.notifyListeners(); 13 | } catch (_) {} 14 | } 15 | 16 | void refresh() => notifyListeners(); 17 | 18 | /// similar to value setter but this one force trigger the notifyListeners() 19 | /// event if newValue == value. 20 | void update(Color newValue) { 21 | value = newValue; 22 | refresh(); 23 | } 24 | } 25 | 26 | /// Extension: ColorValueNotifierExtension 27 | /// 28 | /// Description: 29 | /// Adds convenience methods to `ValueNotifier` for managing color state changes 30 | /// 31 | /// Example: 32 | /// ```dart 33 | /// final colorValueNotifier = Colors.blue.notifier; 34 | /// colorValueNotifier.withOpacity(0.5); 35 | /// ``` 36 | extension FHUColorValueNotifierExtension on ValueListenable { 37 | /// The alpha channel of this color in an 8 bit value. 38 | /// 39 | /// A value of 0 means this color is fully transparent. A value of 255 means 40 | /// this color is fully opaque. 41 | double get alpha => value.a; 42 | 43 | /// **Deprecated:** Use [alpha] instead. 44 | /// 45 | /// The alpha channel of this color as a double. 46 | /// 47 | /// A value of 0.0 means this color is fully transparent. A value of 1.0 means 48 | /// this color is fully opaque. 49 | @Deprecated('Use alpha instead. This will be removed in future versions.') 50 | double get opacity => value.a; 51 | 52 | /// The red channel of this color in an 8 bit value. 53 | double get red => value.r; 54 | 55 | /// The green channel of this color in an 8 bit value. 56 | double get green => value.g; 57 | 58 | /// The blue channel of this color in an 8 bit value. 59 | double get blue => value.b; 60 | 61 | /// Returns a new color that matches this color with the alpha channel 62 | /// replaced with `a` (which ranges from 0 to 255). 63 | /// 64 | /// Out of range values will have unexpected effects. 65 | Color withAlpha(int a) => value.withAlpha(a); 66 | 67 | /// Returns a new color that matches this color with the alpha channel 68 | /// replaced with the given `opacity` (which ranges from 0.0 to 1.0). 69 | /// 70 | /// Out of range values will have unexpected effects. 71 | Color withOpacity(double opacity) => value.addOpacity(opacity); 72 | 73 | /// Returns a new color that matches this color with the red channel replaced 74 | /// with `r` (which ranges from 0 to 255). 75 | /// 76 | /// Out of range values will have unexpected effects. 77 | Color withRed(int r) => value.withRed(r); 78 | 79 | /// Returns a new color that matches this color with the green channel 80 | /// replaced with `g` (which ranges from 0 to 255). 81 | /// 82 | /// Out of range values will have unexpected effects. 83 | Color withGreen(int g) => value.withGreen(g); 84 | 85 | /// Returns a new color that matches this color with the blue channel replaced 86 | /// with `b` (which ranges from 0 to 255). 87 | /// 88 | /// Out of range values will have unexpected effects. 89 | Color withBlue(int b) => value.withBlue(b); 90 | 91 | /// Returns a brightness value between 0 for darkest and 1 for lightest. 92 | /// 93 | /// Represents the relative luminance of the color. This value is computationally 94 | /// expensive to calculate. 95 | /// 96 | /// See . 97 | double computeLuminance() => value.computeLuminance(); 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/value_notifier/notifier_classes/duration_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// create a [ValueNotifier] of type [Duration], which reacts just like normal [Duration], 4 | /// but with notifier capabilities. 5 | class DurationNotifier extends ValueNotifier implements Duration { 6 | DurationNotifier(super.initial); 7 | 8 | @override 9 | void notifyListeners() { 10 | try { 11 | super.notifyListeners(); 12 | } catch (_) {} 13 | } 14 | 15 | void refresh() => notifyListeners(); 16 | 17 | /// similar to value setter but this one force trigger the notifyListeners() 18 | /// event if newValue == value. 19 | void update(Duration newValue) { 20 | value = newValue; 21 | refresh(); 22 | } 23 | 24 | /// documentation available in the original overridden method. 25 | @override 26 | Duration operator *(num factor) => value * factor; 27 | 28 | /// documentation available in the original overridden method. 29 | @override 30 | Duration operator +(Duration other) => value + other; 31 | 32 | /// documentation available in the original overridden method. 33 | @override 34 | Duration operator -() => -value; 35 | 36 | /// documentation available in the original overridden method. 37 | @override 38 | Duration operator -(Duration other) => value - other; 39 | 40 | /// documentation available in the original overridden method. 41 | @override 42 | bool operator <(Duration other) => value < other; 43 | 44 | /// documentation available in the original overridden method. 45 | @override 46 | bool operator <=(Duration other) => value <= other; 47 | 48 | /// documentation available in the original overridden method. 49 | @override 50 | bool operator >(Duration other) => value > other; 51 | 52 | /// documentation available in the original overridden method. 53 | @override 54 | bool operator >=(Duration other) => value >= other; 55 | 56 | /// documentation available in the original overridden method. 57 | @override 58 | Duration operator ~/(int quotient) => value ~/ quotient; 59 | 60 | /// documentation available in the original overridden method. 61 | @override 62 | Duration abs() => value.abs(); 63 | 64 | /// documentation available in the original overridden method. 65 | @override 66 | int compareTo(Duration other) => value.compareTo(other); 67 | 68 | /// documentation available in the original overridden method. 69 | @override 70 | int get inDays => value.inDays; 71 | 72 | /// documentation available in the original overridden method. 73 | @override 74 | int get inHours => value.inHours; 75 | 76 | /// documentation available in the original overridden method. 77 | @override 78 | int get inMicroseconds => value.inMicroseconds; 79 | 80 | /// documentation available in the original overridden method. 81 | @override 82 | int get inMilliseconds => value.inMilliseconds; 83 | 84 | /// documentation available in the original overridden method. 85 | @override 86 | int get inMinutes => value.inMinutes; 87 | 88 | /// documentation available in the original overridden method. 89 | @override 90 | int get inSeconds => value.inSeconds; 91 | 92 | /// documentation available in the original overridden method. 93 | @override 94 | bool get isNegative => value.isNegative; 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/value_notifier/notifier_classes/notifier_classes.dart: -------------------------------------------------------------------------------- 1 | export 'bool_notifier.dart'; 2 | export 'color_notifier.dart'; 3 | export 'date_notifier.dart'; 4 | export 'double_notifier.dart'; 5 | export 'doubly_linked_list_notifier.dart'; 6 | export 'duration_notifier.dart'; 7 | export 'int_notifier.dart'; 8 | export 'list_notifier.dart'; 9 | export 'map_notifier.dart'; 10 | export 'num_notifier.dart'; 11 | export 'set_notifier.dart'; 12 | export 'string_notifier.dart'; 13 | export 'theme_mode_notifier.dart'; 14 | export 'uri_notifier.dart'; 15 | -------------------------------------------------------------------------------- /lib/src/value_notifier/notifier_classes/theme_mode_notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_helper_utils/flutter_helper_utils.dart'; 3 | 4 | class ThemeModeNotifier extends ValueNotifier { 5 | ThemeModeNotifier(super.value); 6 | 7 | @override 8 | void notifyListeners() { 9 | try { 10 | super.notifyListeners(); 11 | } catch (_) {} 12 | } 13 | 14 | void refresh() => notifyListeners(); 15 | 16 | /// similar to value setter but this one force trigger the notifyListeners() 17 | /// event if newValue == value. 18 | void update(ThemeMode newValue) { 19 | value = newValue; 20 | refresh(); 21 | } 22 | } 23 | 24 | extension ThemeModeNotifierEx on ValueNotifier { 25 | bool get isDark => value.isDark; 26 | 27 | bool get isLight => value.isLight; 28 | 29 | bool get isSystem => value.isSystem; 30 | 31 | void setDark() => value = ThemeMode.dark; 32 | 33 | void setLight() => value = ThemeMode.light; 34 | 35 | void setSystem() => value = ThemeMode.system; 36 | } 37 | 38 | class BrightnessNotifier extends ValueNotifier { 39 | BrightnessNotifier(super.value); 40 | 41 | @override 42 | void notifyListeners() { 43 | try { 44 | super.notifyListeners(); 45 | } catch (_) {} 46 | } 47 | 48 | void refresh() => notifyListeners(); 49 | 50 | /// similar to value setter but this one force trigger the notifyListeners() 51 | /// event if newValue == value. 52 | void update(Brightness newValue) { 53 | value = newValue; 54 | refresh(); 55 | } 56 | } 57 | 58 | extension BrightnessNotifierEx on ValueNotifier { 59 | bool get isDark => value.isDark; 60 | 61 | bool get isLight => value.isLight; 62 | 63 | void setDart() => value = Brightness.dark; 64 | 65 | void setLight() => value = Brightness.light; 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/value_notifier/value_notifier.dart: -------------------------------------------------------------------------------- 1 | export 'extensions/extensions.dart'; 2 | export 'notifier_classes/notifier_classes.dart'; 3 | export 'widgets/widgets.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/value_notifier/widgets/listenables_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// [ListenablesBuilder] 4 | /// 5 | /// A widget that rebuilds itself when any of the provided [Listenable] objects notify their listeners. 6 | /// This allows for responsive and dynamic UI updates based on changes in multiple [Listenable] sources. 7 | /// 8 | /// The `ListenablesBuilder` widget is designed to listen to a collection of [Listenable] objects, 9 | /// such as [ValueNotifier], [ChangeNotifier], or custom [Listenable] instances. When any of the 10 | /// specified `listenables` notify their listeners of changes, the widget will trigger a rebuild, 11 | /// ensuring that the UI reflects the latest state. 12 | /// 13 | /// This widget offers flexibility in its configuration: 14 | /// - You can provide a list of [Listenable] objects to listen to. 15 | /// - You can define a custom `builder` function to specify the UI. 16 | /// - You can optionally provide a `buildWhen` callback to control when the widget should rebuild, 17 | /// optimizing performance by preventing unnecessary rebuilds. 18 | /// - You can also specify a `threshold` duration to limit the frequency of rebuilds during rapid 19 | /// state changes, helping to avoid performance issues and UI flickering. 20 | /// **Example:** 21 | /// ```dart 22 | /// ListenablesBuilder( 23 | /// listenables: [myValueNotifier1, myChangeNotifier], 24 | /// builder: (context) => Text('Value 1: ${myValueNotifier1.value}, Value 2: ${myChangeNotifier.someProperty}'), 25 | /// ); 26 | /// ``` 27 | class ListenablesBuilder extends StatefulWidget { 28 | const ListenablesBuilder({ 29 | required this.listenables, 30 | required this.builder, 31 | this.buildWhen, 32 | this.threshold, 33 | super.key, 34 | }); 35 | 36 | /// A list of [Listenable] objects that this widget listens to. 37 | /// The widget will rebuild when any of the `listenables` notify their listeners. 38 | final List listenables; 39 | 40 | /// A function that returns the widget to be built. 41 | /// This function is called whenever the widget needs to rebuild. 42 | final Widget Function(BuildContext context) builder; 43 | 44 | /// An optional function that determines whether the widget should rebuild 45 | /// when any of the `listenables` notify their listeners. 46 | /// If it returns `true`, the [builder] function is called to rebuild the widget. 47 | /// If `false`, the widget is not rebuilt. If null, the widget rebuilds on every notification. 48 | final bool Function()? buildWhen; 49 | 50 | /// An optional [Duration] that sets a minimum interval between rebuilds 51 | /// to limit the frequency of rebuilds during rapid state changes. 52 | final Duration? threshold; 53 | 54 | @override 55 | State createState() => _ListenablesBuilderState(); 56 | } 57 | 58 | class _ListenablesBuilderState extends State { 59 | DateTime? _lastBuildTime; 60 | final Set _activeListenables = {}; // Track active listeners 61 | 62 | @override 63 | void initState() { 64 | super.initState(); 65 | _addListeners(); 66 | } 67 | 68 | void _addListeners() { 69 | for (final listenable in widget.listenables) { 70 | if (listenable != null && !_activeListenables.contains(listenable)) { 71 | listenable.addListener(_listener); 72 | _activeListenables.add(listenable); 73 | } 74 | } 75 | } 76 | 77 | void _removeListeners() { 78 | for (final listenable in _activeListenables) { 79 | listenable.removeListener(_listener); 80 | } 81 | _activeListenables.clear(); 82 | } 83 | 84 | void _listener() { 85 | if (widget.buildWhen?.call() ?? true) { 86 | final now = DateTime.now(); 87 | if (widget.threshold == null || 88 | _lastBuildTime == null || 89 | now.difference(_lastBuildTime!) > widget.threshold!) { 90 | setState(() { 91 | _lastBuildTime = now; 92 | }); 93 | } 94 | } 95 | } 96 | 97 | @override 98 | void didUpdateWidget(ListenablesBuilder oldWidget) { 99 | super.didUpdateWidget(oldWidget); 100 | if (oldWidget.listenables != widget.listenables) { 101 | _removeListeners(); 102 | _addListeners(); 103 | } 104 | } 105 | 106 | @override 107 | void dispose() { 108 | _removeListeners(); 109 | super.dispose(); 110 | } 111 | 112 | @override 113 | Widget build(BuildContext context) => widget.builder(context); 114 | } 115 | -------------------------------------------------------------------------------- /lib/src/value_notifier/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'listenables_builder.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/widgets/gradient_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// A [GradientWidget] applies a gradient effect to its child widget 4 | /// and offers extensive customization options for blending, alignment, 5 | /// and opacity. 6 | /// 7 | /// This widget uses a [ShaderMask] to overlay the provided [gradient] on top 8 | /// of the [child] widget. Additionally, it provides options to control how the 9 | /// gradient interacts with the child, including blend mode, child alignment, and opacity. 10 | /// 11 | /// Example usage: 12 | /// ```dart 13 | /// GradientWidget( 14 | /// gradient: LinearGradient( 15 | /// colors: [Colors.blue, Colors.purple, Colors.pink], 16 | /// begin: AlignmentDirectional.topStart, 17 | /// end: AlignmentDirectional.bottomEnd, 18 | /// ), 19 | /// blendMode: BlendMode.srcIn, 20 | /// opacity: 0.8, 21 | /// child: Text( 22 | /// 'Gradient Text!', 23 | /// style: TextStyle(fontSize: 40, color: Colors.white), 24 | /// ), 25 | /// ) 26 | /// ``` 27 | /// 28 | /// The gradient can be customized with any [Gradient], and the child can 29 | /// be any widget, not limited to just text. 30 | class GradientWidget extends StatelessWidget { 31 | /// Creates a [GradientWidget] with customizable gradient, blend mode, opacity, 32 | /// and alignments for both the gradient and the child. 33 | const GradientWidget({ 34 | required this.child, 35 | required this.gradient, 36 | this.blendMode = BlendMode.srcIn, 37 | @Deprecated( 38 | "Use the gradient's own positioning properties instead (like begin/end for LinearGradient)") 39 | this.gradientAlignment = Alignment.topLeft, 40 | this.opacity = 1.0, 41 | this.childAlignment = AlignmentDirectional.center, 42 | super.key, 43 | }) : assert(opacity >= 0.0 && opacity <= 1.0, 44 | 'Opacity must be between 0.0 and 1.0'); 45 | 46 | /// The widget that will have the gradient applied to it. 47 | final Widget child; 48 | 49 | /// The gradient to apply on the child widget. 50 | /// Use the gradient's own positioning properties (like begin/end for LinearGradient) 51 | /// to control its placement. 52 | final Gradient gradient; 53 | 54 | /// The blend mode to determine how the gradient interacts with the 55 | /// child widget's existing colors. Defaults to [BlendMode.srcIn]. 56 | final BlendMode blendMode; 57 | 58 | /// The alignment of the gradient relative to the child widget's bounds. 59 | /// This controls how the gradient is positioned. Defaults to [Alignment.topLeft]. 60 | /// 61 | /// @deprecated Use the gradient's own positioning properties instead 62 | /// (like begin/end for LinearGradient). 63 | @Deprecated( 64 | "Use the gradient's own positioning properties instead (like begin/end for LinearGradient)") 65 | final Alignment gradientAlignment; 66 | 67 | /// The opacity of the gradient, ranging from 0.0 (fully transparent) 68 | /// to 1.0 (fully opaque). Defaults to 1.0. 69 | final double opacity; 70 | 71 | /// The alignment of the child within the widget. Useful if the child does not 72 | /// fill the entire available space. Defaults to [AlignmentDirectional.center]. 73 | final AlignmentDirectional childAlignment; 74 | 75 | @override 76 | Widget build(BuildContext context) { 77 | return Opacity( 78 | opacity: opacity, 79 | child: ShaderMask( 80 | blendMode: blendMode, 81 | shaderCallback: gradient.createShader, 82 | child: Align( 83 | alignment: childAlignment, 84 | child: child, 85 | ), 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/src/widgets/multi_tap_detector.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/gestures.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | /// A widget that detects multiple taps on its child within a specified duration. 7 | /// 8 | /// The [onTap] callback is triggered when the specified number of taps ([tapCount]) 9 | /// occurs within the given [duration]. 10 | /// 11 | /// This widget can detect gestures where the user taps multiple times on the child 12 | /// widget within a configurable time interval. It handles both rapid tapping 13 | /// and accidental taps where the user pauses or performs other gestures. 14 | class MultiTapDetector extends StatefulWidget { 15 | const MultiTapDetector({ 16 | required this.child, 17 | required this.onTap, 18 | this.tapCount = 3, 19 | this.duration = const Duration(milliseconds: 500), 20 | this.onTapProgress, 21 | super.key, 22 | }) : assert(tapCount > 1, 'tapCount must be greater than 1'); 23 | 24 | /// The widget to which the multi-tap detection will be applied. 25 | final Widget child; 26 | 27 | /// The callback function to be executed when the multi-tap gesture is detected. 28 | final VoidCallback onTap; 29 | 30 | /// Optional callback that reports the progress of tapping sequence. 31 | /// The integer parameter represents the current tap count. 32 | final void Function(int currentCount)? onTapProgress; 33 | 34 | /// The number of taps required to trigger the [onTap] callback. 35 | final int tapCount; 36 | 37 | /// The maximum duration allowed between consecutive taps within a multi-tap sequence. 38 | final Duration duration; 39 | 40 | @override 41 | State createState() => _MultiTapDetectorState(); 42 | } 43 | 44 | class _MultiTapDetectorState extends State { 45 | Timer? _resetTimer; 46 | 47 | @override 48 | void dispose() { 49 | _resetTimer?.cancel(); 50 | super.dispose(); 51 | } 52 | 53 | void _startResetTimer() { 54 | _resetTimer?.cancel(); 55 | _resetTimer = Timer(widget.duration, () { 56 | // Reset timer expired without completing the sequence 57 | if (widget.onTapProgress != null) { 58 | widget.onTapProgress?.call(0); 59 | } 60 | }); 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return RawGestureDetector( 66 | gestures: { 67 | SerialTapGestureRecognizer: 68 | GestureRecognizerFactoryWithHandlers( 69 | () => SerialTapGestureRecognizer( 70 | supportedDevices: null, // Support all devices 71 | ), 72 | (SerialTapGestureRecognizer instance) { 73 | instance 74 | ..onSerialTapDown = (SerialTapDownDetails details) { 75 | _startResetTimer(); 76 | 77 | // Report progress if callback is provided 78 | if (widget.onTapProgress != null) { 79 | widget.onTapProgress?.call(details.count); 80 | } 81 | } 82 | ..onSerialTapUp = (SerialTapUpDetails details) { 83 | if (details.count == widget.tapCount) { 84 | _resetTimer?.cancel(); 85 | widget.onTap(); 86 | 87 | // Reset progress 88 | if (widget.onTapProgress != null) { 89 | widget.onTapProgress?.call(0); 90 | } 91 | } 92 | } 93 | ..onSerialTapCancel = (SerialTapCancelDetails details) { 94 | _resetTimer?.cancel(); 95 | 96 | // Reset progress 97 | if (widget.onTapProgress != null) { 98 | widget.onTapProgress?.call(0); 99 | } 100 | }; 101 | }, 102 | ), 103 | }, 104 | child: widget.child, 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/src/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'adaptive_ui.dart'; 2 | export 'gradient_widget.dart'; 3 | export 'multi_tap_detector.dart'; 4 | export 'typed_list_view.dart'; 5 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Group 2 4 | 5 | 6 | 7 | 8 | Flutter Helper Utils 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_helper_utils 2 | description: The Flutter Helper Utils Package offers various extensions and helper methods that can make development more efficient. 3 | repository: https://github.com/omar-hanafy/flutter_helper_utils 4 | version: 8.0.2 5 | topics: 6 | - utilities 7 | - helpers 8 | - extensions 9 | - productivity 10 | - adaptive 11 | 12 | environment: 13 | sdk: '>=3.4.0 <4.0.0' 14 | flutter: ">=3.29.0 <4.0.0" 15 | 16 | dependencies: 17 | flutter: 18 | sdk: flutter 19 | dart_helper_utils: ">=4.1.2 <6.0.0" 20 | intl: ">=0.19.0 <1.0.0" 21 | 22 | dev_dependencies: 23 | flutter_test: 24 | sdk: flutter 25 | very_good_analysis: ^7.0.0 26 | --------------------------------------------------------------------------------