├── res └── values │ └── strings_en.arb ├── example ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── flutter_export_environment.sh │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── AppDelegate.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── 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-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── AppDelegate.m │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ └── project.pbxproj │ └── .gitignore ├── .gitignore ├── .metadata ├── .idea │ ├── runConfigurations │ │ └── main_dart.xml │ ├── libraries │ │ ├── Flutter_for_Android.xml │ │ └── Dart_SDK.xml │ ├── modules.xml │ └── workspace.xml ├── README.md ├── example.iml ├── test │ └── widget_test.dart ├── example_android.iml ├── pubspec.yaml ├── lib │ └── main.dart └── pubspec.lock ├── test └── hijri_picker_test.dart ├── .gitignore ├── .idea ├── dictionaries │ └── ahmedaljoaid.xml ├── misc.xml ├── vcs.xml ├── libraries │ ├── Flutter_Plugins.xml │ ├── Dart_SDK.xml │ └── Dart_Packages.xml └── modules.xml ├── CHANGELOG.md ├── lib ├── src │ └── hijri_calendar_builders.dart └── hijri_picker.dart ├── hijri_picker.iml ├── LICENSE ├── pubspec.yaml ├── README.md └── pubspec.lock /res/values/strings_en.arb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /test/hijri_picker_test.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | void main() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | web/ 7 | build/ 8 | 9 | .flutter-plugins 10 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmedoid/hijri_picker/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | ios/.generated/ 9 | ios/Flutter/Generated.xcconfig 10 | ios/Runner/GeneratedPluginRegistrant.* 11 | .idea/* 12 | -------------------------------------------------------------------------------- /.idea/dictionaries/ahmedaljoaid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hijri 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/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/ahmedoid/hijri_picker/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmedoid/hijri_picker/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmedoid/hijri_picker/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmedoid/hijri_picker/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmedoid/hijri_picker/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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: f9bb4289e9fd861d70ae78bcc3a042ef1b35cc9d 8 | channel: beta 9 | -------------------------------------------------------------------------------- /example/.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /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/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 7 | [GeneratedPluginRegistrant registerWithRegistry:self]; 8 | // Override point for customization after application launch. 9 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 10 | } 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | ## 3.1.0 3 | 4 | - Added custom builders for days and weekdays in `HijriMonthPicker()` 5 | 6 | ## 3.0.0 7 | 8 | - Migrated to null safety 9 | 10 | ## 2.0.0 11 | 12 | - Update hijri package to last 13 | 14 | ## 1.0.3 15 | 16 | - Update flutter to last 17 | - Add example for a widget usages 18 | 19 | ## 1.0.1 20 | 21 | - Edit text size for header 22 | - Update Hijri Package to 1.0.1 23 | 24 | ## 1.0.0 25 | 26 | - Support Local 27 | - Update Hijri Package 28 | 29 | ## 0.0.1 30 | 31 | - Initial version 32 | -------------------------------------------------------------------------------- /lib/src/hijri_calendar_builders.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hijri/hijri_calendar.dart'; 3 | 4 | class HijriCalendarBuilders { 5 | const HijriCalendarBuilders({ 6 | this.weekdayBuilder, 7 | this.dayBuilder, 8 | }); 9 | 10 | /// Weekdays builder (Sat, Sun, ..) 11 | final Widget Function(BuildContext context, String day)? weekdayBuilder; 12 | 13 | /// Days builder (1, 2, ..) 14 | final Widget Function( 15 | BuildContext context, HijriCalendar day, bool isSelected)? dayBuilder; 16 | } 17 | -------------------------------------------------------------------------------- /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/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=W:\Development\Configurations\flutter\versions\2.0.2" 4 | export "FLUTTER_APPLICATION_PATH=W:\Development\Workspaces\Github\Flutter\Forks\hijri_picker\example" 5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true" 6 | export "FLUTTER_TARGET=lib\main.dart" 7 | export "FLUTTER_BUILD_DIR=build" 8 | export "SYMROOT=${SOURCE_ROOT}/../build\ios" 9 | export "FLUTTER_BUILD_NAME=1.0.0" 10 | export "FLUTTER_BUILD_NUMBER=1" 11 | export "DART_OBFUSCATION=false" 12 | export "TRACK_WIDGET_CREATION=false" 13 | export "TREE_SHAKE_ICONS=false" 14 | export "PACKAGE_CONFIG=.packages" 15 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | 2 | Hijri Date Picker 3 | - 4 | Hijri calender to pick umm alqura dates support max & min dates 5 | 6 | 7 | Simple Usage 8 | - 9 | ```dart in html 10 | final HijriCalendar picked = await showHijriDatePicker( 11 | context: context, 12 | initialDate: selectedDate, 13 | lastDate: new HijriCalendar() 14 | ..hYear = 1442 15 | ..hMonth = 9 16 | ..hDay = 25, 17 | firstDate: new HijriCalendar() 18 | ..hYear = 1438 19 | ..hMonth = 12 20 | ..hDay = 25, 21 | initialDatePickerMode: DatePickerMode.day, 22 | ); 23 | ``` 24 | About 25 | - 26 | 27 | screenshots 28 | - 29 |

30 | 31 | 32 |

33 | 34 | by 35 | - 36 | Ahmed Aljoaid 👨🏻‍💻 37 | -------------------------------------------------------------------------------- /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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /hijri_picker.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:example/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Ahmed Aljuaid 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /example/example_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hijri_picker 2 | description: Hijri calender to pick umm alqura dates support max & min dates. 3 | version: 4.0.0 4 | homepage: "https://github.com/ahmedoid/hijri_picker" 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | hijri: ^3.0.0 13 | 14 | dev_dependencies: 15 | flutter_driver: 16 | sdk: flutter 17 | 18 | 19 | # For information on the generic Dart part of this file, see the 20 | # following page: https://www.dartlang.org/tools/pub/pubspec 21 | 22 | # The following section is specific to Flutter. 23 | flutter: 24 | 25 | # To add assets to your package, add an assets section, like this: 26 | # assets: 27 | # - images/a_dot_burr.jpeg 28 | # - images/a_dot_ham.jpeg 29 | # 30 | # For details regarding assets in packages, see 31 | # https://flutter.io/assets-and-images/#from-packages 32 | # 33 | # An image asset can refer to one or more resolution-specific "variants", see 34 | # https://flutter.io/assets-and-images/#resolution-aware. 35 | 36 | # To add custom fonts to your package, add a fonts section here, 37 | # in this "flutter" section. Each entry in this list should have a 38 | # "family" key with the font family name, and a "fonts" key with a 39 | # list giving the asset and other descriptors for the font. For 40 | # example: 41 | # fonts: 42 | # - family: Schyler 43 | # fonts: 44 | # - asset: fonts/Schyler-Regular.ttf 45 | # - asset: fonts/Schyler-Italic.ttf 46 | # style: italic 47 | # - family: Trajan Pro 48 | # fonts: 49 | # - asset: fonts/TrajanPro.ttf 50 | # - asset: fonts/TrajanPro_Bold.ttf 51 | # weight: 700 52 | # 53 | # For details regarding fonts in packages, see 54 | # https://flutter.io/custom-fonts/#from-packages 55 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | environment: 5 | sdk: '>=3.0.0 <4.0.0' 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | flutter_localizations: 11 | sdk: flutter 12 | cupertino_icons: ^1.0.5 13 | 14 | # The following adds the Cupertino Icons font to your application. 15 | # Use with the CupertinoIcons class for iOS style icons. 16 | hijri_picker: 17 | path : ../ 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | 24 | # For information on the generic Dart part of this file, see the 25 | # following page: https://www.dartlang.org/tools/pub/pubspec 26 | 27 | # The following section is specific to Flutter. 28 | flutter: 29 | 30 | # The following line ensures that the Material Icons font is 31 | # included with your application, so that you can use the icons in 32 | # the material Icons class. 33 | uses-material-design: true 34 | 35 | # To add assets to your application, add an assets section, like this: 36 | # assets: 37 | # - images/a_dot_burr.jpeg 38 | # - images/a_dot_ham.jpeg 39 | 40 | # An image asset can refer to one or more resolution-specific "variants", see 41 | # https://flutter.io/assets-and-images/#resolution-aware. 42 | 43 | # For details regarding adding assets from package dependencies, see 44 | # https://flutter.io/assets-and-images/#from-packages 45 | 46 | # To add custom fonts to your application, add a fonts section here, 47 | # in this "flutter" section. Each entry in this list should have a 48 | # "family" key with the font family name, and a "fonts" key with a 49 | # list giving the asset and other descriptors for the font. For 50 | # example: 51 | # fonts: 52 | # - family: Schyler 53 | # fonts: 54 | # - asset: fonts/Schyler-Regular.ttf 55 | # - asset: fonts/Schyler-Italic.ttf 56 | # style: italic 57 | # - family: Trajan Pro 58 | # fonts: 59 | # - asset: fonts/TrajanPro.ttf 60 | # - asset: fonts/TrajanPro_Bold.ttf 61 | # weight: 700 62 | # 63 | # For details regarding fonts from package dependencies, 64 | # see https://flutter.io/custom-fonts/#from-packages 65 | -------------------------------------------------------------------------------- /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/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_localizations/flutter_localizations.dart'; 5 | import 'package:hijri/hijri_calendar.dart'; 6 | import 'package:hijri_picker/hijri_picker.dart'; 7 | 8 | void main() => runApp(MyApp()); 9 | 10 | class MyApp extends StatelessWidget { 11 | // This widget is the root of your application. 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | title: 'Flutter Demo', 16 | localizationsDelegates: [ 17 | GlobalMaterialLocalizations.delegate, 18 | GlobalWidgetsLocalizations.delegate, 19 | ], 20 | supportedLocales: [ 21 | // const Locale('en', 'USA'), 22 | const Locale('ar', 'SA'), 23 | ], 24 | debugShowCheckedModeBanner: false, 25 | theme: ThemeData( 26 | primaryColor: Colors.brown, 27 | brightness: Brightness.light 28 | ), 29 | home: MyHomePage(title: "Umm Alqura Calendar")); 30 | } 31 | } 32 | 33 | class MyHomePage extends StatefulWidget { 34 | MyHomePage({Key? key, required this.title}) : super(key: key); 35 | 36 | final String title; 37 | 38 | @override 39 | _MyHomePageState createState() => _MyHomePageState(); 40 | } 41 | 42 | class _MyHomePageState extends State { 43 | var selectedDate = HijriCalendar.now(); 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | HijriCalendar.setLocal(Localizations.localeOf(context).languageCode); 48 | return Scaffold( 49 | appBar: AppBar( 50 | title: Text(widget.title), 51 | ), 52 | body: Padding( 53 | padding: const EdgeInsets.all(8.0), 54 | child: Center( 55 | child: Column( 56 | mainAxisAlignment: MainAxisAlignment.center, 57 | // crossAxisAlignment: CrossAxisAlignment.stretch, 58 | children: [ 59 | Text( 60 | '${selectedDate.toString()}', 61 | style: Theme.of(context).textTheme.headlineSmall, 62 | ), 63 | Text( 64 | '${selectedDate.fullDate()}', 65 | style: Theme.of(context).textTheme.headlineSmall, 66 | ), 67 | ], 68 | ), 69 | ), 70 | ), 71 | floatingActionButton: FloatingActionButton( 72 | onPressed: () => _selectDate(context), 73 | tooltip: 'Pick Date', 74 | child: Icon(Icons.event), 75 | ), 76 | ); 77 | } 78 | 79 | Future _selectDate(BuildContext context) async { 80 | final HijriCalendar? picked = await showHijriDatePicker( 81 | context: context, 82 | initialDate: selectedDate, 83 | lastDate: HijriCalendar() 84 | ..hYear = 1445 85 | ..hMonth = 9 86 | ..hDay = 25, 87 | firstDate: HijriCalendar() 88 | ..hYear = 1438 89 | ..hMonth = 12 90 | ..hDay = 25, 91 | initialDatePickerMode: DatePickerMode.day, 92 | ); 93 | if (picked != null) 94 | setState(() { 95 | selectedDate = picked; 96 | }); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/pub/v/hijri_picker?style=flat)](https://pub.dev/packages/hijri_picker) 2 | ![](https://img.shields.io/github/license/ahmedoid/hijri_picker?style=flat) 3 | 4 | Hijri Date Picker 5 | - 6 | Flutter Widget & Picker to select Hijri (Isalmic Calendar) date 7 | 8 | ## Screenshots صور 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Simple Usage طريقة الاستخدام 24 | - 25 | Add local to `MaterialApp` 26 | ```dart in html 27 | localizationsDelegates: [ 28 | GlobalMaterialLocalizations.delegate, 29 | GlobalWidgetsLocalizations.delegate, 30 | ], 31 | supportedLocales: [ 32 | const Locale('ar', 'SA'), 33 | ], 34 | ``` 35 | [Internationalizing Flutter apps](https://flutter.dev/docs/development/accessibility-and-localization/internationalization) 36 | 37 | 38 | 39 | ```dart in html 40 | final HijriCalendar? picked = await showHijriDatePicker( 41 | context: context, 42 | initialDate: selectedDate, 43 | lastDate: HijriCalendar() 44 | ..hYear = 1442 45 | ..hMonth = 9 46 | ..hDay = 25, 47 | firstDate: HijriCalendar() 48 | ..hYear = 1438 49 | ..hMonth = 12 50 | ..hDay = 25, 51 | initialDatePickerMode: DatePickerMode.day, 52 | ); 53 | ``` 54 | 55 | ## As Widget استخدام كـ 56 | ```dart in html 57 | HijriMonthPicker( 58 | lastDate: HijriCalendar() 59 | ..hYear = 1445 60 | ..hMonth = 9 61 | ..hDay = 25, 62 | firstDate: HijriCalendar() 63 | ..hYear = 1438 64 | ..hMonth = 12 65 | ..hDay = 25, 66 | onChanged: (HijriCalendar value) { 67 | setState(() { 68 | selectedDate = selectedDate; 69 | }); 70 | }, 71 | selectedDate: selectedDate, 72 | ) 73 | ``` 74 | 75 | 76 | by 77 | - 78 | Ahmed Aljoaid 79 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.2.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | cupertino_icons: 47 | dependency: "direct main" 48 | description: 49 | name: cupertino_icons 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "0.1.3" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_localizations: 66 | dependency: "direct main" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | flutter_test: 71 | dependency: "direct dev" 72 | description: flutter 73 | source: sdk 74 | version: "0.0.0" 75 | hijri: 76 | dependency: transitive 77 | description: 78 | name: hijri 79 | url: "https://pub.dartlang.org" 80 | source: hosted 81 | version: "3.0.0" 82 | hijri_picker: 83 | dependency: "direct main" 84 | description: 85 | path: ".." 86 | relative: true 87 | source: path 88 | version: "3.1.0" 89 | intl: 90 | dependency: transitive 91 | description: 92 | name: intl 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "0.17.0" 96 | matcher: 97 | dependency: transitive 98 | description: 99 | name: matcher 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "0.12.11" 103 | material_color_utilities: 104 | dependency: transitive 105 | description: 106 | name: material_color_utilities 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "0.1.3" 110 | meta: 111 | dependency: transitive 112 | description: 113 | name: meta 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.7.0" 117 | path: 118 | dependency: transitive 119 | description: 120 | name: path 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.8.0" 124 | sky_engine: 125 | dependency: transitive 126 | description: flutter 127 | source: sdk 128 | version: "0.0.99" 129 | source_span: 130 | dependency: transitive 131 | description: 132 | name: source_span 133 | url: "https://pub.dartlang.org" 134 | source: hosted 135 | version: "1.8.1" 136 | stack_trace: 137 | dependency: transitive 138 | description: 139 | name: stack_trace 140 | url: "https://pub.dartlang.org" 141 | source: hosted 142 | version: "1.10.0" 143 | stream_channel: 144 | dependency: transitive 145 | description: 146 | name: stream_channel 147 | url: "https://pub.dartlang.org" 148 | source: hosted 149 | version: "2.1.0" 150 | string_scanner: 151 | dependency: transitive 152 | description: 153 | name: string_scanner 154 | url: "https://pub.dartlang.org" 155 | source: hosted 156 | version: "1.1.0" 157 | term_glyph: 158 | dependency: transitive 159 | description: 160 | name: term_glyph 161 | url: "https://pub.dartlang.org" 162 | source: hosted 163 | version: "1.2.0" 164 | test_api: 165 | dependency: transitive 166 | description: 167 | name: test_api 168 | url: "https://pub.dartlang.org" 169 | source: hosted 170 | version: "0.4.8" 171 | typed_data: 172 | dependency: transitive 173 | description: 174 | name: typed_data 175 | url: "https://pub.dartlang.org" 176 | source: hosted 177 | version: "1.3.0" 178 | vector_math: 179 | dependency: transitive 180 | description: 181 | name: vector_math 182 | url: "https://pub.dartlang.org" 183 | source: hosted 184 | version: "2.1.1" 185 | sdks: 186 | dart: ">=2.14.0 <3.0.0" 187 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "3.1.6" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.8.2" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.1.0" 25 | characters: 26 | dependency: transitive 27 | description: 28 | name: characters 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.2.0" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.3.1" 39 | clock: 40 | dependency: transitive 41 | description: 42 | name: clock 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | collection: 47 | dependency: transitive 48 | description: 49 | name: collection 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.15.0" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "3.0.1" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.2.0" 67 | file: 68 | dependency: transitive 69 | description: 70 | name: file 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "6.1.2" 74 | flutter: 75 | dependency: "direct main" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_driver: 80 | dependency: "direct dev" 81 | description: flutter 82 | source: sdk 83 | version: "0.0.0" 84 | flutter_test: 85 | dependency: transitive 86 | description: flutter 87 | source: sdk 88 | version: "0.0.0" 89 | fuchsia_remote_debug_protocol: 90 | dependency: transitive 91 | description: flutter 92 | source: sdk 93 | version: "0.0.0" 94 | hijri: 95 | dependency: "direct main" 96 | description: 97 | name: hijri 98 | url: "https://pub.dartlang.org" 99 | source: hosted 100 | version: "3.0.0" 101 | matcher: 102 | dependency: transitive 103 | description: 104 | name: matcher 105 | url: "https://pub.dartlang.org" 106 | source: hosted 107 | version: "0.12.11" 108 | material_color_utilities: 109 | dependency: transitive 110 | description: 111 | name: material_color_utilities 112 | url: "https://pub.dartlang.org" 113 | source: hosted 114 | version: "0.1.3" 115 | meta: 116 | dependency: transitive 117 | description: 118 | name: meta 119 | url: "https://pub.dartlang.org" 120 | source: hosted 121 | version: "1.7.0" 122 | path: 123 | dependency: transitive 124 | description: 125 | name: path 126 | url: "https://pub.dartlang.org" 127 | source: hosted 128 | version: "1.8.0" 129 | platform: 130 | dependency: transitive 131 | description: 132 | name: platform 133 | url: "https://pub.dartlang.org" 134 | source: hosted 135 | version: "3.1.0" 136 | process: 137 | dependency: transitive 138 | description: 139 | name: process 140 | url: "https://pub.dartlang.org" 141 | source: hosted 142 | version: "4.2.4" 143 | sky_engine: 144 | dependency: transitive 145 | description: flutter 146 | source: sdk 147 | version: "0.0.99" 148 | source_span: 149 | dependency: transitive 150 | description: 151 | name: source_span 152 | url: "https://pub.dartlang.org" 153 | source: hosted 154 | version: "1.8.1" 155 | stack_trace: 156 | dependency: transitive 157 | description: 158 | name: stack_trace 159 | url: "https://pub.dartlang.org" 160 | source: hosted 161 | version: "1.10.0" 162 | stream_channel: 163 | dependency: transitive 164 | description: 165 | name: stream_channel 166 | url: "https://pub.dartlang.org" 167 | source: hosted 168 | version: "2.1.0" 169 | string_scanner: 170 | dependency: transitive 171 | description: 172 | name: string_scanner 173 | url: "https://pub.dartlang.org" 174 | source: hosted 175 | version: "1.1.0" 176 | sync_http: 177 | dependency: transitive 178 | description: 179 | name: sync_http 180 | url: "https://pub.dartlang.org" 181 | source: hosted 182 | version: "0.3.0" 183 | term_glyph: 184 | dependency: transitive 185 | description: 186 | name: term_glyph 187 | url: "https://pub.dartlang.org" 188 | source: hosted 189 | version: "1.2.0" 190 | test_api: 191 | dependency: transitive 192 | description: 193 | name: test_api 194 | url: "https://pub.dartlang.org" 195 | source: hosted 196 | version: "0.4.8" 197 | typed_data: 198 | dependency: transitive 199 | description: 200 | name: typed_data 201 | url: "https://pub.dartlang.org" 202 | source: hosted 203 | version: "1.3.0" 204 | vector_math: 205 | dependency: transitive 206 | description: 207 | name: vector_math 208 | url: "https://pub.dartlang.org" 209 | source: hosted 210 | version: "2.1.1" 211 | vm_service: 212 | dependency: transitive 213 | description: 214 | name: vm_service 215 | url: "https://pub.dartlang.org" 216 | source: hosted 217 | version: "7.5.0" 218 | webdriver: 219 | dependency: transitive 220 | description: 221 | name: webdriver 222 | url: "https://pub.dartlang.org" 223 | source: hosted 224 | version: "3.0.0" 225 | sdks: 226 | dart: ">=2.14.0 <3.0.0" 227 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_Packages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 13 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; 14 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 15 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 16 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 17 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 18 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = ""; 26 | dstSubfolderSpec = 10; 27 | files = ( 28 | ); 29 | name = "Embed Frameworks"; 30 | runOnlyForDeploymentPostprocessing = 0; 31 | }; 32 | /* End PBXCopyFilesBuildPhase section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 38 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 39 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 40 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 41 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 42 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 43 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | 9740EEB11CF90186004384FC /* Flutter */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 66 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 67 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 68 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 69 | ); 70 | name = Flutter; 71 | sourceTree = ""; 72 | }; 73 | 97C146E51CF9000F007C117D = { 74 | isa = PBXGroup; 75 | children = ( 76 | 9740EEB11CF90186004384FC /* Flutter */, 77 | 97C146F01CF9000F007C117D /* Runner */, 78 | 97C146EF1CF9000F007C117D /* Products */, 79 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | 97C146EF1CF9000F007C117D /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 97C146EE1CF9000F007C117D /* Runner.app */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | 97C146F01CF9000F007C117D /* Runner */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 95 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 96 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 97 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 98 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 99 | 97C147021CF9000F007C117D /* Info.plist */, 100 | 97C146F11CF9000F007C117D /* Supporting Files */, 101 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 102 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 103 | ); 104 | path = Runner; 105 | sourceTree = ""; 106 | }; 107 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 97C146F21CF9000F007C117D /* main.m */, 111 | ); 112 | name = "Supporting Files"; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | 97C146ED1CF9000F007C117D /* Runner */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 121 | buildPhases = ( 122 | 9740EEB61CF901F6004384FC /* Run Script */, 123 | 97C146EA1CF9000F007C117D /* Sources */, 124 | 97C146EB1CF9000F007C117D /* Frameworks */, 125 | 97C146EC1CF9000F007C117D /* Resources */, 126 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 127 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 128 | ); 129 | buildRules = ( 130 | ); 131 | dependencies = ( 132 | ); 133 | name = Runner; 134 | productName = Runner; 135 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 136 | productType = "com.apple.product-type.application"; 137 | }; 138 | /* End PBXNativeTarget section */ 139 | 140 | /* Begin PBXProject section */ 141 | 97C146E61CF9000F007C117D /* Project object */ = { 142 | isa = PBXProject; 143 | attributes = { 144 | LastUpgradeCheck = 0910; 145 | ORGANIZATIONNAME = "The Chromium Authors"; 146 | TargetAttributes = { 147 | 97C146ED1CF9000F007C117D = { 148 | CreatedOnToolsVersion = 7.3.1; 149 | }; 150 | }; 151 | }; 152 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 153 | compatibilityVersion = "Xcode 3.2"; 154 | developmentRegion = English; 155 | hasScannedForEncodings = 0; 156 | knownRegions = ( 157 | en, 158 | Base, 159 | ); 160 | mainGroup = 97C146E51CF9000F007C117D; 161 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 162 | projectDirPath = ""; 163 | projectRoot = ""; 164 | targets = ( 165 | 97C146ED1CF9000F007C117D /* Runner */, 166 | ); 167 | }; 168 | /* End PBXProject section */ 169 | 170 | /* Begin PBXResourcesBuildPhase section */ 171 | 97C146EC1CF9000F007C117D /* Resources */ = { 172 | isa = PBXResourcesBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 176 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 177 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 178 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 179 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 180 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXShellScriptBuildPhase section */ 187 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 188 | isa = PBXShellScriptBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | ); 192 | inputPaths = ( 193 | ); 194 | name = "Thin Binary"; 195 | outputPaths = ( 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | shellPath = /bin/sh; 199 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 200 | }; 201 | 9740EEB61CF901F6004384FC /* Run Script */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Run Script"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 214 | }; 215 | /* End PBXShellScriptBuildPhase section */ 216 | 217 | /* Begin PBXSourcesBuildPhase section */ 218 | 97C146EA1CF9000F007C117D /* Sources */ = { 219 | isa = PBXSourcesBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 223 | 97C146F31CF9000F007C117D /* main.m in Sources */, 224 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | /* End PBXSourcesBuildPhase section */ 229 | 230 | /* Begin PBXVariantGroup section */ 231 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | 97C146FB1CF9000F007C117D /* Base */, 235 | ); 236 | name = Main.storyboard; 237 | sourceTree = ""; 238 | }; 239 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 240 | isa = PBXVariantGroup; 241 | children = ( 242 | 97C147001CF9000F007C117D /* Base */, 243 | ); 244 | name = LaunchScreen.storyboard; 245 | sourceTree = ""; 246 | }; 247 | /* End PBXVariantGroup section */ 248 | 249 | /* Begin XCBuildConfiguration section */ 250 | 97C147031CF9000F007C117D /* Debug */ = { 251 | isa = XCBuildConfiguration; 252 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 253 | buildSettings = { 254 | ALWAYS_SEARCH_USER_PATHS = NO; 255 | CLANG_ANALYZER_NONNULL = YES; 256 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 257 | CLANG_CXX_LIBRARY = "libc++"; 258 | CLANG_ENABLE_MODULES = YES; 259 | CLANG_ENABLE_OBJC_ARC = YES; 260 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 261 | CLANG_WARN_BOOL_CONVERSION = YES; 262 | CLANG_WARN_COMMA = YES; 263 | CLANG_WARN_CONSTANT_CONVERSION = YES; 264 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INFINITE_RECURSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 271 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 272 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 273 | CLANG_WARN_STRICT_PROTOTYPES = YES; 274 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 275 | CLANG_WARN_UNREACHABLE_CODE = YES; 276 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 277 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 278 | COPY_PHASE_STRIP = NO; 279 | DEBUG_INFORMATION_FORMAT = dwarf; 280 | ENABLE_STRICT_OBJC_MSGSEND = YES; 281 | ENABLE_TESTABILITY = YES; 282 | GCC_C_LANGUAGE_STANDARD = gnu99; 283 | GCC_DYNAMIC_NO_PIC = NO; 284 | GCC_NO_COMMON_BLOCKS = YES; 285 | GCC_OPTIMIZATION_LEVEL = 0; 286 | GCC_PREPROCESSOR_DEFINITIONS = ( 287 | "DEBUG=1", 288 | "$(inherited)", 289 | ); 290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 292 | GCC_WARN_UNDECLARED_SELECTOR = YES; 293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 294 | GCC_WARN_UNUSED_FUNCTION = YES; 295 | GCC_WARN_UNUSED_VARIABLE = YES; 296 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 297 | MTL_ENABLE_DEBUG_INFO = YES; 298 | ONLY_ACTIVE_ARCH = YES; 299 | SDKROOT = iphoneos; 300 | TARGETED_DEVICE_FAMILY = "1,2"; 301 | }; 302 | name = Debug; 303 | }; 304 | 97C147041CF9000F007C117D /* Release */ = { 305 | isa = XCBuildConfiguration; 306 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 307 | buildSettings = { 308 | ALWAYS_SEARCH_USER_PATHS = NO; 309 | CLANG_ANALYZER_NONNULL = YES; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN_ENUM_CONVERSION = YES; 321 | CLANG_WARN_INFINITE_RECURSION = YES; 322 | CLANG_WARN_INT_CONVERSION = YES; 323 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 327 | CLANG_WARN_STRICT_PROTOTYPES = YES; 328 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 329 | CLANG_WARN_UNREACHABLE_CODE = YES; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 332 | COPY_PHASE_STRIP = NO; 333 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 334 | ENABLE_NS_ASSERTIONS = NO; 335 | ENABLE_STRICT_OBJC_MSGSEND = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | SDKROOT = iphoneos; 347 | TARGETED_DEVICE_FAMILY = "1,2"; 348 | VALIDATE_PRODUCT = YES; 349 | }; 350 | name = Release; 351 | }; 352 | 97C147061CF9000F007C117D /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 355 | buildSettings = { 356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 357 | CURRENT_PROJECT_VERSION = 1; 358 | ENABLE_BITCODE = NO; 359 | FRAMEWORK_SEARCH_PATHS = ( 360 | "$(inherited)", 361 | "$(PROJECT_DIR)/Flutter", 362 | ); 363 | INFOPLIST_FILE = Runner/Info.plist; 364 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 365 | LIBRARY_SEARCH_PATHS = ( 366 | "$(inherited)", 367 | "$(PROJECT_DIR)/Flutter", 368 | ); 369 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | VERSIONING_SYSTEM = "apple-generic"; 372 | }; 373 | name = Debug; 374 | }; 375 | 97C147071CF9000F007C117D /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 378 | buildSettings = { 379 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 380 | CURRENT_PROJECT_VERSION = 1; 381 | ENABLE_BITCODE = NO; 382 | FRAMEWORK_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "$(PROJECT_DIR)/Flutter", 385 | ); 386 | INFOPLIST_FILE = Runner/Info.plist; 387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 388 | LIBRARY_SEARCH_PATHS = ( 389 | "$(inherited)", 390 | "$(PROJECT_DIR)/Flutter", 391 | ); 392 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | VERSIONING_SYSTEM = "apple-generic"; 395 | }; 396 | name = Release; 397 | }; 398 | /* End XCBuildConfiguration section */ 399 | 400 | /* Begin XCConfigurationList section */ 401 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 402 | isa = XCConfigurationList; 403 | buildConfigurations = ( 404 | 97C147031CF9000F007C117D /* Debug */, 405 | 97C147041CF9000F007C117D /* Release */, 406 | ); 407 | defaultConfigurationIsVisible = 0; 408 | defaultConfigurationName = Release; 409 | }; 410 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | 97C147061CF9000F007C117D /* Debug */, 414 | 97C147071CF9000F007C117D /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Release; 418 | }; 419 | /* End XCConfigurationList section */ 420 | }; 421 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 422 | } 423 | -------------------------------------------------------------------------------- /lib/hijri_picker.dart: -------------------------------------------------------------------------------- 1 | library hijri_picker; 2 | 3 | import 'dart:async'; 4 | import 'dart:math' as math; 5 | 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/rendering.dart'; 8 | import 'package:flutter/services.dart'; 9 | import 'package:hijri/hijri_calendar.dart'; 10 | 11 | import 'package:hijri_picker/src/hijri_calendar_builders.dart'; 12 | 13 | export 'src/hijri_calendar_builders.dart'; 14 | 15 | const double _kMonthPickerPortraitWidth = 330.0; 16 | const double _kMonthPickerLandscapeWidth = 344.0; 17 | const double _kDatePickerHeaderPortraitHeight = 100.0; 18 | const double _kDatePickerHeaderLandscapeWidth = 168.0; 19 | const double _kDayPickerRowHeight = 42.0; 20 | const int _kMaxDayPickerRowCount = 6; // A 31 day month that starts on Saturday. 21 | const double _kDialogActionBarHeight = 52.0; 22 | const double _kDatePickerLandscapeHeight = 23 | _kMaxDayPickerHeight + _kDialogActionBarHeight; 24 | // Two extra rows: one for the day-of-week header and one for the month header. 25 | const double _kMaxDayPickerHeight = 26 | _kDayPickerRowHeight * (_kMaxDayPickerRowCount + 2); 27 | const Duration _kMonthScrollDuration = const Duration(milliseconds: 200); 28 | 29 | /// Signature for predicating dates for enabled date selections. 30 | /// 31 | /// See [showDatePicker]. 32 | typedef bool SelectableDayPredicate(HijriCalendar day); 33 | 34 | class HijriDatePickerDialog extends StatefulWidget { 35 | const HijriDatePickerDialog({ 36 | Key? key, 37 | required this.initialDate, 38 | required this.firstDate, 39 | required this.lastDate, 40 | this.selectableDayPredicate, 41 | required this.initialDatePickerMode, 42 | }) : super(key: key); 43 | 44 | final HijriCalendar initialDate; 45 | final HijriCalendar firstDate; 46 | final HijriCalendar lastDate; 47 | final SelectableDayPredicate? selectableDayPredicate; 48 | final DatePickerMode initialDatePickerMode; 49 | 50 | @override 51 | _DatePickerDialogState createState() => _DatePickerDialogState(); 52 | } 53 | 54 | class _DatePickerDialogState extends State { 55 | @override 56 | void initState() { 57 | super.initState(); 58 | _selectedDate = widget.initialDate; 59 | _mode = widget.initialDatePickerMode; 60 | } 61 | 62 | bool _announcedInitialDate = false; 63 | 64 | late MaterialLocalizations localizations; 65 | late TextDirection textDirection; 66 | 67 | @override 68 | void didChangeDependencies() { 69 | super.didChangeDependencies(); 70 | localizations = MaterialLocalizations.of(context); 71 | textDirection = Directionality.of(context); 72 | if (!_announcedInitialDate) { 73 | _announcedInitialDate = true; 74 | SemanticsService.announce( 75 | _selectedDate.toString(), 76 | textDirection, 77 | ); 78 | } 79 | } 80 | 81 | late HijriCalendar _selectedDate; 82 | late DatePickerMode _mode; 83 | final GlobalKey _pickerKey = GlobalKey(); 84 | 85 | void _vibrate() { 86 | switch (Theme.of(context).platform) { 87 | case TargetPlatform.android: 88 | case TargetPlatform.fuchsia: 89 | HapticFeedback.vibrate(); 90 | break; 91 | case TargetPlatform.iOS: 92 | break; 93 | case TargetPlatform.linux: 94 | break; 95 | case TargetPlatform.macOS: 96 | break; 97 | case TargetPlatform.windows: 98 | break; 99 | } 100 | } 101 | 102 | void _handleModeChanged(DatePickerMode mode) { 103 | _vibrate(); 104 | setState(() { 105 | _mode = mode; 106 | if (_mode == DatePickerMode.day) { 107 | SemanticsService.announce(_selectedDate.toString(), textDirection); 108 | } else { 109 | SemanticsService.announce(_selectedDate.toString(), textDirection); 110 | } 111 | }); 112 | } 113 | 114 | void _handleYearChanged(HijriCalendar value) { 115 | _vibrate(); 116 | setState(() { 117 | _mode = DatePickerMode.day; 118 | _selectedDate = value; 119 | }); 120 | } 121 | 122 | void _handleDayChanged(HijriCalendar value) { 123 | _vibrate(); 124 | setState(() { 125 | _selectedDate = value; 126 | }); 127 | } 128 | 129 | void _handleCancel() { 130 | Navigator.pop(context); 131 | } 132 | 133 | void _handleOk() { 134 | Navigator.pop(context, _selectedDate); 135 | } 136 | 137 | Widget _buildPicker() { 138 | switch (_mode) { 139 | case DatePickerMode.day: 140 | return HijriMonthPicker( 141 | key: _pickerKey, 142 | selectedDate: _selectedDate, 143 | onChanged: _handleDayChanged, 144 | firstDate: widget.firstDate, 145 | lastDate: widget.lastDate, 146 | selectableDayPredicate: widget.selectableDayPredicate, 147 | ); 148 | case DatePickerMode.year: 149 | return HijriYearPicker( 150 | key: _pickerKey, 151 | selectedDate: _selectedDate, 152 | onChanged: _handleYearChanged, 153 | firstDate: widget.firstDate, 154 | lastDate: widget.lastDate, 155 | ); 156 | } 157 | } 158 | 159 | @override 160 | Widget build(BuildContext context) { 161 | final ThemeData theme = Theme.of(context); 162 | HijriCalendar.setLocal(Localizations.localeOf(context).languageCode); 163 | 164 | final Widget picker = Flexible( 165 | child: SizedBox( 166 | height: _kMaxDayPickerHeight, 167 | child: _buildPicker(), 168 | ), 169 | ); 170 | final Widget actions = ButtonBarTheme( 171 | data: ButtonBarThemeData(), 172 | child: ButtonBar( 173 | children: [ 174 | TextButton( 175 | child: Text(localizations.cancelButtonLabel), 176 | onPressed: _handleCancel, 177 | ), 178 | TextButton( 179 | child: Text(localizations.okButtonLabel), 180 | onPressed: _handleOk, 181 | ), 182 | ], 183 | ), 184 | ); 185 | final Dialog dialog = Dialog(child: OrientationBuilder( 186 | builder: (BuildContext context, Orientation orientation) { 187 | final Widget header = _DatePickerHeader( 188 | hSelectedDate: _selectedDate, 189 | mode: _mode, 190 | onModeChanged: _handleModeChanged, 191 | orientation: orientation, 192 | ); 193 | switch (orientation) { 194 | case Orientation.portrait: 195 | return SizedBox( 196 | width: _kMonthPickerPortraitWidth, 197 | child: Column( 198 | mainAxisSize: MainAxisSize.min, 199 | crossAxisAlignment: CrossAxisAlignment.stretch, 200 | children: [ 201 | header, 202 | Container( 203 | color: theme.dialogBackgroundColor, 204 | child: Column( 205 | mainAxisSize: MainAxisSize.min, 206 | crossAxisAlignment: CrossAxisAlignment.stretch, 207 | children: [ 208 | picker, 209 | actions, 210 | ], 211 | ), 212 | ), 213 | ], 214 | ), 215 | ); 216 | case Orientation.landscape: 217 | return SizedBox( 218 | height: _kDatePickerLandscapeHeight, 219 | child: Row( 220 | mainAxisSize: MainAxisSize.min, 221 | crossAxisAlignment: CrossAxisAlignment.stretch, 222 | children: [ 223 | header, 224 | Flexible( 225 | child: Container( 226 | width: _kMonthPickerLandscapeWidth, 227 | color: theme.dialogBackgroundColor, 228 | child: Column( 229 | mainAxisSize: MainAxisSize.min, 230 | crossAxisAlignment: CrossAxisAlignment.stretch, 231 | children: [picker, actions], 232 | ), 233 | ), 234 | ), 235 | ], 236 | ), 237 | ); 238 | } 239 | })); 240 | 241 | return Theme( 242 | data: theme.copyWith( 243 | dialogBackgroundColor: Colors.transparent, 244 | ), 245 | child: dialog, 246 | ); 247 | } 248 | } 249 | 250 | // Shows the selected date in large font and toggles between year and day mode 251 | class _DatePickerHeader extends StatelessWidget { 252 | const _DatePickerHeader({ 253 | Key? key, 254 | required this.hSelectedDate, 255 | required this.mode, 256 | required this.onModeChanged, 257 | required this.orientation, 258 | }) : super(key: key); 259 | 260 | final HijriCalendar hSelectedDate; 261 | final DatePickerMode mode; 262 | final ValueChanged onModeChanged; 263 | final Orientation orientation; 264 | 265 | void _handleChangeMode(DatePickerMode value) { 266 | if (value != mode) onModeChanged(value); 267 | } 268 | 269 | @override 270 | Widget build(BuildContext context) { 271 | final ThemeData themeData = Theme.of(context); 272 | final TextTheme headerTextTheme = themeData.primaryTextTheme; 273 | Color dayColor; 274 | Color yearColor; 275 | switch (themeData.brightness) { 276 | case Brightness.light: 277 | dayColor = mode == DatePickerMode.day ? Colors.black87 : Colors.black54; 278 | yearColor = 279 | mode == DatePickerMode.year ? Colors.black87 : Colors.black54; 280 | break; 281 | case Brightness.dark: 282 | dayColor = mode == DatePickerMode.day ? Colors.white : Colors.white70; 283 | yearColor = mode == DatePickerMode.year ? Colors.white : Colors.white70; 284 | break; 285 | } 286 | final TextStyle? dayStyle = 287 | headerTextTheme.headlineMedium?.copyWith(color: dayColor, height: 1.4); 288 | final TextStyle? yearStyle = 289 | headerTextTheme.titleMedium?.copyWith(color: yearColor, height: 1.4); 290 | 291 | Color backgroundColor; 292 | switch (themeData.brightness) { 293 | case Brightness.light: 294 | backgroundColor = themeData.primaryColor; 295 | break; 296 | case Brightness.dark: 297 | backgroundColor = themeData.colorScheme.background; 298 | break; 299 | } 300 | 301 | double? width; 302 | double? height; 303 | EdgeInsets? padding; 304 | MainAxisAlignment? mainAxisAlignment; 305 | switch (orientation) { 306 | case Orientation.portrait: 307 | height = _kDatePickerHeaderPortraitHeight; 308 | padding = const EdgeInsets.symmetric(horizontal: 16.0); 309 | mainAxisAlignment = MainAxisAlignment.center; 310 | break; 311 | case Orientation.landscape: 312 | width = _kDatePickerHeaderLandscapeWidth; 313 | padding = const EdgeInsets.all(8.0); 314 | mainAxisAlignment = MainAxisAlignment.start; 315 | break; 316 | } 317 | 318 | final Widget yearButton = IgnorePointer( 319 | ignoring: mode != DatePickerMode.day, 320 | ignoringSemantics: false, 321 | child: _DateHeaderButton( 322 | color: backgroundColor, 323 | onTap: Feedback.wrapForTap( 324 | () => _handleChangeMode(DatePickerMode.year), context), 325 | child: Semantics( 326 | selected: mode == DatePickerMode.year, 327 | child: Text("${hSelectedDate.hYear}", style: yearStyle), 328 | ), 329 | ), 330 | ); 331 | 332 | final Widget dayButton = IgnorePointer( 333 | ignoring: mode == DatePickerMode.day, 334 | ignoringSemantics: false, 335 | child: _DateHeaderButton( 336 | color: backgroundColor, 337 | onTap: Feedback.wrapForTap( 338 | () => _handleChangeMode(DatePickerMode.day), context), 339 | child: Semantics( 340 | selected: mode == DatePickerMode.day, 341 | child: FittedBox( 342 | fit: BoxFit.fitHeight, 343 | child: Text("${hSelectedDate.toFormat("DD,dd MMMM")}", 344 | style: dayStyle), 345 | ), 346 | ), 347 | ), 348 | ); 349 | 350 | return Container( 351 | width: width, 352 | height: height, 353 | padding: padding, 354 | color: backgroundColor, 355 | child: Column( 356 | mainAxisAlignment: mainAxisAlignment, 357 | crossAxisAlignment: CrossAxisAlignment.start, 358 | children: [yearButton, dayButton], 359 | ), 360 | ); 361 | } 362 | } 363 | 364 | class _DateHeaderButton extends StatelessWidget { 365 | const _DateHeaderButton({ 366 | Key? key, 367 | this.onTap, 368 | this.color, 369 | required this.child, 370 | }) : super(key: key); 371 | 372 | final VoidCallback? onTap; 373 | final Color? color; 374 | final Widget child; 375 | 376 | @override 377 | Widget build(BuildContext context) { 378 | final ThemeData theme = Theme.of(context); 379 | 380 | return Material( 381 | type: MaterialType.button, 382 | color: color, 383 | child: InkWell( 384 | borderRadius: kMaterialEdges[MaterialType.button], 385 | highlightColor: theme.highlightColor, 386 | splashColor: theme.splashColor, 387 | onTap: onTap, 388 | child: Container( 389 | padding: const EdgeInsets.symmetric(horizontal: 8.0), 390 | child: child, 391 | ), 392 | ), 393 | ); 394 | } 395 | } 396 | 397 | class HijriMonthPicker extends StatefulWidget { 398 | /// Creates a month picker. 399 | /// 400 | /// Rarely used directly. Instead, typically used as part of the dialog shown 401 | /// by [showDatePicker]. 402 | HijriMonthPicker({ 403 | Key? key, 404 | required this.selectedDate, 405 | required this.onChanged, 406 | required this.firstDate, 407 | required this.lastDate, 408 | this.selectableDayPredicate, 409 | this.builders = const HijriCalendarBuilders(), 410 | }) : assert( 411 | !firstDate.isAfter(lastDate.hYear, lastDate.hMonth, lastDate.hDay)), 412 | /* assert(selectedDate.isAfter( 413 | firstDate.hYear, firstDate.hMonth, firstDate.hDay) || 414 | selectedDate.isAtSameMomentAs( 415 | firstDate.hYear, firstDate.hMonth, firstDate.hDay)),*/ 416 | super(key: key); 417 | 418 | /// The currently selected date. 419 | /// 420 | /// This date is highlighted in the picker. 421 | final HijriCalendar selectedDate; 422 | 423 | /// Called when the user picks a month. 424 | final ValueChanged onChanged; 425 | 426 | /// The earliest date the user is permitted to pick. 427 | final HijriCalendar firstDate; 428 | 429 | /// The latest date the user is permitted to pick. 430 | final HijriCalendar lastDate; 431 | 432 | /// Optional user supplied predicate function to customize selectable days. 433 | final SelectableDayPredicate? selectableDayPredicate; 434 | 435 | /// Optional custom calendar builders 436 | final HijriCalendarBuilders builders; 437 | 438 | @override 439 | _HijriMonthPickerState createState() => _HijriMonthPickerState(); 440 | } 441 | 442 | class _HijriMonthPickerState extends State { 443 | @override 444 | void initState() { 445 | super.initState(); 446 | // Initially display the pre-selected date. 447 | final int monthPage = _monthDelta(widget.firstDate, widget.selectedDate); 448 | _dayPickerController = PageController(initialPage: monthPage); 449 | _handleMonthPageChanged(monthPage); 450 | _updateCurrentDate(); 451 | } 452 | 453 | @override 454 | void didUpdateWidget(HijriMonthPicker oldWidget) { 455 | super.didUpdateWidget(oldWidget); 456 | if (!widget.selectedDate.isAtSameMomentAs(oldWidget.selectedDate.hYear, 457 | oldWidget.selectedDate.hMonth, oldWidget.selectedDate.hDay)) { 458 | final int monthPage = _monthDelta(widget.firstDate, widget.selectedDate); 459 | _dayPickerController = PageController(initialPage: monthPage); 460 | _handleMonthPageChanged(monthPage); 461 | } 462 | } 463 | 464 | late MaterialLocalizations localizations; 465 | late TextDirection textDirection; 466 | 467 | @override 468 | void didChangeDependencies() { 469 | super.didChangeDependencies(); 470 | localizations = MaterialLocalizations.of(context); 471 | textDirection = Directionality.of(context); 472 | } 473 | 474 | late HijriCalendar _todayDate; 475 | late HijriCalendar _currentDisplayedMonthDate; 476 | Timer? _timer; 477 | PageController? _dayPickerController; 478 | 479 | void _updateCurrentDate() { 480 | _todayDate = HijriCalendar.now(); 481 | final HijriCalendar tomorrow = HijriCalendar() 482 | ..hYear = _todayDate.hYear 483 | ..hMonth = _todayDate.hMonth 484 | ..hDay = _todayDate.hDay + 1; 485 | Duration timeUntilTomorrow = tomorrow 486 | .hijriToGregorian(tomorrow.hYear, tomorrow.hMonth, tomorrow.hDay) 487 | .difference(_todayDate.hijriToGregorian( 488 | _todayDate.hYear, 489 | _todayDate.hMonth, 490 | _todayDate.hDay)); //tomorrow.difference(_todayDate); 491 | timeUntilTomorrow += 492 | const Duration(seconds: 1); // so we don't miss it by rounding 493 | _timer?.cancel(); 494 | _timer = Timer(timeUntilTomorrow, () { 495 | setState(() { 496 | _updateCurrentDate(); 497 | }); 498 | }); 499 | } 500 | 501 | static int _monthDelta(HijriCalendar startDate, HijriCalendar endDate) { 502 | return (endDate.hYear - startDate.hYear) * 12 + 503 | endDate.hMonth - 504 | startDate.hMonth; 505 | } 506 | 507 | /// Add months to a month truncated date. 508 | HijriCalendar _addMonthsToMonthDate( 509 | HijriCalendar monthDate, int monthsToAdd) { 510 | var x = HijriCalendar.addMonth( 511 | monthDate.hYear + (monthDate.hMonth + monthsToAdd) ~/ 12, 512 | monthDate.hMonth + monthsToAdd % 12); 513 | return x; 514 | } 515 | 516 | Widget _buildItems(BuildContext context, int index) { 517 | final month = _addMonthsToMonthDate(widget.firstDate, index); 518 | return HijriDayPicker( 519 | key: ValueKey(month), 520 | selectedDate: widget.selectedDate, 521 | currentDate: _todayDate, 522 | onChanged: widget.onChanged, 523 | firstDate: widget.firstDate, 524 | lastDate: widget.lastDate, 525 | displayedMonth: month, 526 | selectableDayPredicate: widget.selectableDayPredicate, 527 | builders: widget.builders, 528 | ); 529 | } 530 | 531 | void _handleNextMonth() { 532 | if (!_isDisplayingLastMonth) { 533 | SemanticsService.announce( 534 | (_nextMonthDate.hMonth.toString()), textDirection); 535 | _dayPickerController?.nextPage( 536 | duration: _kMonthScrollDuration, curve: Curves.ease); 537 | } 538 | } 539 | 540 | void _handlePreviousMonth() { 541 | if (!_isDisplayingFirstMonth) { 542 | SemanticsService.announce( 543 | (_previousMonthDate.hMonth.toString()), textDirection); 544 | _dayPickerController?.previousPage( 545 | duration: _kMonthScrollDuration, curve: Curves.ease); 546 | } 547 | } 548 | 549 | /// True if the earliest allowable month is displayed. 550 | bool get _isDisplayingFirstMonth { 551 | return !_currentDisplayedMonthDate.isAfter( 552 | widget.firstDate.hYear, widget.firstDate.hMonth, widget.firstDate.hDay); 553 | } 554 | 555 | /// True if the latest allowable month is displayed. 556 | bool get _isDisplayingLastMonth { 557 | return !_currentDisplayedMonthDate.isBefore( 558 | widget.lastDate.hYear, widget.lastDate.hMonth, widget.lastDate.hDay); 559 | } 560 | 561 | late HijriCalendar _previousMonthDate; 562 | late HijriCalendar _nextMonthDate; 563 | 564 | void _handleMonthPageChanged(int monthPage) { 565 | setState(() { 566 | _previousMonthDate = 567 | _addMonthsToMonthDate(widget.firstDate, monthPage - 1); 568 | _currentDisplayedMonthDate = 569 | _addMonthsToMonthDate(widget.firstDate, monthPage); 570 | _nextMonthDate = _addMonthsToMonthDate(widget.firstDate, monthPage + 1); 571 | }); 572 | } 573 | 574 | @override 575 | Widget build(BuildContext context) { 576 | return SizedBox( 577 | width: _kMonthPickerPortraitWidth, 578 | height: _kMaxDayPickerHeight, 579 | child: Stack( 580 | children: [ 581 | Semantics( 582 | sortKey: _MonthPickerSortKey.calendar, 583 | child: PageView.builder( 584 | key: ValueKey(widget.selectedDate), 585 | controller: _dayPickerController, 586 | scrollDirection: Axis.horizontal, 587 | itemCount: _monthDelta(widget.firstDate, widget.lastDate) + 1, 588 | itemBuilder: _buildItems, 589 | onPageChanged: _handleMonthPageChanged, 590 | ), 591 | ), 592 | PositionedDirectional( 593 | top: 0.0, 594 | start: 8.0, 595 | child: Semantics( 596 | sortKey: _MonthPickerSortKey.previousMonth, 597 | child: IconButton( 598 | icon: const Icon(Icons.chevron_left), 599 | tooltip: _isDisplayingFirstMonth 600 | ? null 601 | : '${localizations.previousMonthTooltip} ${_previousMonthDate.toString()}', 602 | onPressed: 603 | _isDisplayingFirstMonth ? null : _handlePreviousMonth, 604 | ), 605 | ), 606 | ), 607 | PositionedDirectional( 608 | top: 0.0, 609 | end: 8.0, 610 | child: Semantics( 611 | sortKey: _MonthPickerSortKey.nextMonth, 612 | child: IconButton( 613 | icon: const Icon(Icons.chevron_right), 614 | tooltip: _isDisplayingLastMonth 615 | ? null 616 | : '${localizations.nextMonthTooltip} ${_nextMonthDate.toString()}', 617 | onPressed: _isDisplayingLastMonth ? null : _handleNextMonth, 618 | ), 619 | ), 620 | ), 621 | ], 622 | ), 623 | ); 624 | } 625 | 626 | @override 627 | void dispose() { 628 | _timer?.cancel(); 629 | _dayPickerController?.dispose(); 630 | super.dispose(); 631 | } 632 | } 633 | 634 | // Defines semantic traversal order of the top-level widgets inside the month 635 | // picker. 636 | class _MonthPickerSortKey extends OrdinalSortKey { 637 | static const _MonthPickerSortKey previousMonth = 638 | const _MonthPickerSortKey(1.0); 639 | static const _MonthPickerSortKey nextMonth = const _MonthPickerSortKey(2.0); 640 | static const _MonthPickerSortKey calendar = const _MonthPickerSortKey(3.0); 641 | 642 | const _MonthPickerSortKey(double order) : super(order); 643 | } 644 | 645 | const int daysPerWeek = 7; 646 | 647 | /// Displays the days of a given month and allows choosing a day. 648 | /// 649 | /// The days are arranged in a rectangular grid with one column for each day of 650 | /// the week. 651 | /// 652 | /// The day picker widget is rarely used directly. Instead, consider using 653 | /// [showDatePicker], which creates a date picker dialog. 654 | /// 655 | /// See also: 656 | /// 657 | /// * [showDatePicker]. 658 | /// * 659 | /// 660 | /// 661 | 662 | class _HijriDayPickerGridDelegate extends SliverGridDelegate { 663 | const _HijriDayPickerGridDelegate(); 664 | 665 | @override 666 | SliverGridLayout getLayout(SliverConstraints constraints) { 667 | const int columnCount = daysPerWeek; 668 | final double tileWidth = constraints.crossAxisExtent / columnCount; 669 | final double tileHeight = math.min(_kDayPickerRowHeight, 670 | constraints.viewportMainAxisExtent / (_kMaxDayPickerRowCount + 1)); 671 | return SliverGridRegularTileLayout( 672 | crossAxisCount: columnCount, 673 | mainAxisStride: tileHeight, 674 | crossAxisStride: tileWidth, 675 | childMainAxisExtent: tileHeight, 676 | childCrossAxisExtent: tileWidth, 677 | reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), 678 | ); 679 | } 680 | 681 | @override 682 | bool shouldRelayout(_HijriDayPickerGridDelegate oldDelegate) => false; 683 | } 684 | 685 | const _HijriDayPickerGridDelegate _kDayPickerGridDelegate = 686 | const _HijriDayPickerGridDelegate(); 687 | 688 | class HijriDayPicker extends StatelessWidget { 689 | /// Creates a day picker. 690 | /// 691 | HijriDayPicker({ 692 | Key? key, 693 | required this.selectedDate, 694 | required this.currentDate, 695 | required this.onChanged, 696 | required this.firstDate, 697 | required this.lastDate, 698 | this.selectableDayPredicate, 699 | required this.displayedMonth, 700 | required this.builders, 701 | }) : assert( 702 | !firstDate.isAfter(lastDate.hYear, lastDate.hMonth, lastDate.hDay)), 703 | super(key: key); 704 | 705 | /// The currently selected date. 706 | /// 707 | /// This date is highlighted in the picker. 708 | final HijriCalendar selectedDate; 709 | 710 | /// The current date at the time the picker is displayed. 711 | final HijriCalendar currentDate; 712 | 713 | /// Called when the user picks a day. 714 | final ValueChanged onChanged; 715 | 716 | /// The earliest date the user is permitted to pick. 717 | final HijriCalendar firstDate; 718 | 719 | /// The latest date the user is permitted to pick. 720 | final HijriCalendar lastDate; 721 | 722 | /// The month whose days are displayed by this picker. 723 | final HijriCalendar displayedMonth; 724 | 725 | /// Optional user supplied predicate function to customize selectable days. 726 | final SelectableDayPredicate? selectableDayPredicate; 727 | 728 | /// Calendar builders 729 | final HijriCalendarBuilders builders; 730 | 731 | List _getDayHeaders(BuildContext context, TextStyle? headerStyle, 732 | MaterialLocalizations localizations) { 733 | final List result = []; 734 | 735 | /// { 0 } pick first day of week as sunday 736 | for (int i = 0; true; i = (i + 1) % 7) { 737 | final String weekday = localizations.narrowWeekdays[i]; 738 | 739 | Widget weekdayWidget = builders.weekdayBuilder == null 740 | ? ExcludeSemantics( 741 | child: Center(child: Text(weekday, style: headerStyle)), 742 | ) 743 | : builders.weekdayBuilder!(context, weekday); 744 | 745 | result.add(weekdayWidget); 746 | 747 | /// { 0 } pick first day of week as sunday 748 | if (i == (0 - 1) % 7) break; 749 | } 750 | return result; 751 | } 752 | 753 | /// Returns the number of days in a month, according to the proleptic 754 | /// Gregorian calendar. 755 | /// 756 | /// This applies the leap year logic introduced by the Gregorian reforms of 757 | /// 1582. It will not give valid results for dates prior to that time. 758 | static int getDaysInMonth(int year, int month) { 759 | return HijriCalendar().getDaysInMonth(year, month); 760 | } 761 | 762 | int _computeFirstDayOffset(int year, int month) { 763 | var convertDate = HijriCalendar(); 764 | DateTime wkDay = convertDate.hijriToGregorian(year, month, 1); 765 | // 0-based day of week, with 0 representing Monday. 766 | final int weekdayFromMonday = wkDay.weekday - 1; 767 | // 0-based day of week, with 0 representing Sunday. 768 | final int firstDayOfWeekFromSunday = 0; 769 | // firstDayOfWeekFromSunday recomputed to be Monday-based 770 | final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7; 771 | // Number of days between the first day of week appearing on the calendar, 772 | // and the day corresponding to the 1-st of the month. 773 | return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7; 774 | } 775 | 776 | @override 777 | Widget build(BuildContext context) { 778 | final ThemeData themeData = Theme.of(context); 779 | final MaterialLocalizations localizations = 780 | MaterialLocalizations.of(context); 781 | 782 | final int year = displayedMonth.hYear; 783 | final int month = displayedMonth.hMonth; 784 | final int daysInMonth = getDaysInMonth(year, month); 785 | final int firstDayOffset = _computeFirstDayOffset(year, month); 786 | final List labels = []; 787 | 788 | labels.addAll( 789 | _getDayHeaders(context, themeData.textTheme.bodySmall, localizations)); 790 | 791 | for (int i = 0; true; i += 1) { 792 | final int day = i - firstDayOffset + 1; 793 | if (day > daysInMonth) break; 794 | if (day < 1) { 795 | labels.add(Container()); 796 | } else { 797 | final HijriCalendar dayToBuild = HijriCalendar() 798 | ..hYear = year 799 | ..hMonth = month 800 | ..hDay = day; 801 | final bool disabled = dayToBuild.isAfter( 802 | lastDate.hYear, lastDate.hMonth, lastDate.hDay) || 803 | dayToBuild.isBefore( 804 | firstDate.hYear, firstDate.hMonth, firstDate.hDay) || 805 | (selectableDayPredicate != null && 806 | !selectableDayPredicate!(dayToBuild)); 807 | 808 | BoxDecoration? decoration; 809 | TextStyle? itemStyle = themeData.textTheme.bodyMedium; 810 | 811 | final bool isSelectedDay = selectedDate.hYear == year && 812 | selectedDate.hMonth == month && 813 | selectedDate.hDay == day; 814 | if (isSelectedDay) { 815 | // The selected day gets a circle background highlight, and a contrasting text color. 816 | itemStyle = themeData.textTheme.bodyLarge?.copyWith( 817 | color: themeData.colorScheme.onSecondary, 818 | ); 819 | decoration = BoxDecoration( 820 | color: themeData.colorScheme.secondary, shape: BoxShape.circle); 821 | } else if (disabled) { 822 | itemStyle = themeData.textTheme.bodyMedium 823 | ?.copyWith(color: themeData.disabledColor); 824 | } else if (currentDate.hYear == year && 825 | currentDate.hMonth == month && 826 | currentDate.hDay == day) { 827 | // The current day gets a different text color. 828 | itemStyle = themeData.textTheme.bodyLarge 829 | ?.copyWith(color: themeData.colorScheme.secondary); 830 | } 831 | 832 | final String dayText = localizations.formatDecimal(day); 833 | final bool useBuiltInDayBuilder = builders.dayBuilder == null; 834 | 835 | Widget dayWidget = useBuiltInDayBuilder 836 | ? Container( 837 | decoration: decoration, 838 | child: Center( 839 | child: Semantics( 840 | // We want the day of month to be spoken first irrespective of the 841 | // locale-specific preferences or TextDirection. This is because 842 | // an accessibility user is more likely to be interested in the 843 | // day of month before the rest of the date, as they are looking 844 | // for the day of month. To do that we prepend day of month to the 845 | // formatted full date. 846 | label: '$dayText, ${dayToBuild.toString()}', 847 | selected: isSelectedDay, 848 | child: ExcludeSemantics( 849 | child: Text(dayText, style: itemStyle), 850 | ), 851 | ), 852 | ), 853 | ) 854 | : builders.dayBuilder!(context, dayToBuild, isSelectedDay); 855 | 856 | if (!disabled) { 857 | dayWidget = GestureDetector( 858 | behavior: HitTestBehavior.opaque, 859 | onTap: () { 860 | onChanged(dayToBuild); 861 | }, 862 | child: dayWidget, 863 | ); 864 | } 865 | 866 | labels.add(dayWidget); 867 | } 868 | } 869 | 870 | return Padding( 871 | padding: const EdgeInsets.symmetric(horizontal: 8.0), 872 | child: Column( 873 | children: [ 874 | Container( 875 | height: _kDayPickerRowHeight, 876 | child: Center( 877 | child: ExcludeSemantics( 878 | child: Text( 879 | "${displayedMonth.toFormat("MMMM")} ${displayedMonth.hYear}", 880 | style: themeData.textTheme.titleMedium, 881 | ), 882 | ), 883 | ), 884 | ), 885 | Flexible( 886 | child: GridView.custom( 887 | gridDelegate: _kDayPickerGridDelegate, 888 | childrenDelegate: 889 | SliverChildListDelegate(labels, addRepaintBoundaries: false), 890 | ), 891 | ), 892 | ], 893 | ), 894 | ); 895 | } 896 | } 897 | 898 | /// A scrollable list of years to allow picking a year. 899 | /// 900 | /// The year picker widget is rarely used directly. Instead, consider using 901 | /// [showDatePicker], which creates a date picker dialog. 902 | /// 903 | /// Requires one of its ancestors to be a [Material] widget. 904 | /// 905 | /// See also: 906 | /// 907 | /// * [showDatePicker] 908 | /// * 909 | class HijriYearPicker extends StatefulWidget { 910 | /// Creates a year picker. 911 | /// 912 | /// The [selectedDate] and [onChanged] arguments must not be null. The 913 | /// [lastDate] must be after the [firstDate]. 914 | /// 915 | /// Rarely used directly. Instead, typically used as part of the dialog shown 916 | /// by [showDatePicker]. 917 | HijriYearPicker({ 918 | Key? key, 919 | required this.selectedDate, 920 | required this.onChanged, 921 | required this.firstDate, 922 | required this.lastDate, 923 | }) : assert( 924 | !firstDate.isAfter(lastDate.hYear, lastDate.hMonth, lastDate.hDay)), 925 | super(key: key); 926 | 927 | /// The currently selected date. 928 | /// 929 | /// This date is highlighted in the picker. 930 | final HijriCalendar selectedDate; 931 | 932 | /// Called when the user picks a year. 933 | final ValueChanged onChanged; 934 | 935 | /// The earliest date the user is permitted to pick. 936 | final HijriCalendar firstDate; 937 | 938 | /// The latest date the user is permitted to pick. 939 | final HijriCalendar lastDate; 940 | 941 | @override 942 | _HijriYearPickerState createState() => _HijriYearPickerState(); 943 | } 944 | 945 | class _HijriYearPickerState extends State { 946 | static const double _itemExtent = 50.0; 947 | late ScrollController scrollController; 948 | 949 | @override 950 | void initState() { 951 | super.initState(); 952 | scrollController = ScrollController( 953 | // Move the initial scroll position to the currently selected date's year. 954 | initialScrollOffset: 955 | (widget.selectedDate.hYear - widget.firstDate.hYear) * _itemExtent, 956 | ); 957 | } 958 | 959 | @override 960 | Widget build(BuildContext context) { 961 | assert(debugCheckHasMaterial(context)); 962 | 963 | final ThemeData themeData = Theme.of(context); 964 | final TextStyle? style = themeData.textTheme.bodyMedium; 965 | return ListView.builder( 966 | controller: scrollController, 967 | itemExtent: _itemExtent, 968 | itemCount: widget.lastDate.hYear - widget.firstDate.hYear + 1, 969 | itemBuilder: (BuildContext context, int index) { 970 | final int year = widget.firstDate.hYear + index; 971 | final bool isSelected = year == widget.selectedDate.hYear; 972 | final TextStyle? itemStyle = isSelected 973 | ? themeData.textTheme.headlineSmall 974 | ?.copyWith(color: themeData.colorScheme.secondary) 975 | : style; 976 | return InkWell( 977 | key: ValueKey(year), 978 | onTap: () { 979 | // year, widget.selectedDate.hMonth, widget.selectedDate.hMonth 980 | widget.onChanged(HijriCalendar() 981 | ..hYear = year 982 | ..hMonth = widget.selectedDate.hMonth 983 | ..hDay = widget.selectedDate.hDay); 984 | }, 985 | child: Center( 986 | child: Semantics( 987 | selected: isSelected, 988 | child: Text(year.toString(), style: itemStyle), 989 | ), 990 | ), 991 | ); 992 | }, 993 | ); 994 | } 995 | } 996 | 997 | Future showHijriDatePicker({ 998 | required BuildContext context, 999 | required HijriCalendar initialDate, 1000 | required HijriCalendar firstDate, 1001 | required HijriCalendar lastDate, 1002 | SelectableDayPredicate? selectableDayPredicate, 1003 | DatePickerMode initialDatePickerMode = DatePickerMode.day, 1004 | Locale? locale, 1005 | TextDirection? textDirection, 1006 | }) async { 1007 | assert( 1008 | !initialDate.isBefore(firstDate.hYear, firstDate.hMonth, firstDate.hDay), 1009 | 'initialDate must be on or after firstDate'); 1010 | assert(!initialDate.isAfter(lastDate.hYear, lastDate.hMonth, lastDate.hDay), 1011 | 'initialDate must be on or before lastDate'); 1012 | assert(!firstDate.isAfter(lastDate.hYear, lastDate.hMonth, lastDate.hDay), 1013 | 'lastDate must be on or after firstDate'); 1014 | assert(selectableDayPredicate == null || selectableDayPredicate(initialDate), 1015 | 'Provided initialDate must satisfy provided selectableDayPredicate'); 1016 | 1017 | Widget child = HijriDatePickerDialog( 1018 | initialDate: initialDate, 1019 | firstDate: firstDate, 1020 | lastDate: lastDate, 1021 | selectableDayPredicate: selectableDayPredicate, 1022 | initialDatePickerMode: initialDatePickerMode, 1023 | ); 1024 | 1025 | if (textDirection != null) { 1026 | child = Directionality( 1027 | textDirection: textDirection, 1028 | child: child, 1029 | ); 1030 | } 1031 | 1032 | if (locale != null) { 1033 | child = Localizations.override( 1034 | context: context, 1035 | locale: locale, 1036 | child: child, 1037 | ); 1038 | } 1039 | 1040 | return showDialog( 1041 | context: context, 1042 | builder: (BuildContext context) => child, 1043 | ); 1044 | } 1045 | --------------------------------------------------------------------------------