├── i18n └── .vscode │ ├── settings.json │ ├── tasks.json │ └── launch.json ├── .fvm ├── flutter_sdk └── fvm_config.json ├── ios ├── Runner │ ├── Runner-Bridging-Header.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-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.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 │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile └── Podfile.lock ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ └── Icon-512.png ├── index.html └── manifest.json ├── fonts ├── noorehira.ttf └── UthmanTN1-Ver10.otf ├── assets ├── quran-data │ ├── quran.db │ ├── quran-uthmani.db │ ├── translations.db │ └── translations │ │ ├── en.sahih.db │ │ └── id.indonesian.db ├── images │ └── quran-solid.png ├── i18n │ ├── default.yaml │ └── id_ID.yaml └── translations.yaml ├── macos ├── .gitignore ├── Runner │ ├── Configs │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── Warnings.xcconfig │ │ └── AppInfo.xcconfig │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ ├── app_icon_64.png │ │ │ ├── app_icon_1024.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Release.entitlements │ ├── DebugProfile.entitlements │ ├── MainFlutterWindow.swift │ └── Info.plist ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Podfile └── Podfile.lock ├── lib ├── models │ ├── setting_ids.dart │ ├── translation_data.dart │ └── translation_data.g.dart ├── services │ ├── moors │ │ └── appdb.moor │ ├── qurandb.g.dart │ ├── translationdb.g.dart │ ├── appdb.dart │ ├── qurandb.dart │ ├── translationdb.dart │ ├── theme_provider.g.dart │ ├── theme_provider.dart │ ├── bookmarks_provider.dart │ └── quran_provider.dart ├── baselib │ ├── base_widgetparameter_mixin.dart │ ├── base_state_mixin.dart │ ├── disposable.dart │ ├── base_store.dart │ ├── interaction.dart │ ├── app_services.dart │ ├── widgets.dart │ ├── command.dart │ ├── service_locator.dart │ └── localization_service.dart ├── routes │ └── routes.dart ├── pages │ ├── error │ │ └── error_widget.dart │ ├── home │ │ ├── home_store.dart │ │ ├── home_store.g.dart │ │ └── home_widget.dart │ ├── home_tab │ │ ├── home_tab_store.dart │ │ ├── home_tab_store.g.dart │ │ └── home_tab_widget.dart │ ├── main │ │ ├── main_store.g.dart │ │ ├── main_store.dart │ │ └── main_widget.dart │ ├── splash │ │ ├── splash_store.g.dart │ │ ├── splash_widget.dart │ │ └── splash_store.dart │ ├── bookmarks │ │ ├── bookmarks_store.g.dart │ │ └── bookmarks_store.dart │ ├── home_tab_juz │ │ ├── home_tab_juz_store.g.dart │ │ ├── home_tab_juz_store.dart │ │ └── home_tab_juz_widget.dart │ ├── quran_settings │ │ ├── quran_settings_store.g.dart │ │ ├── quran_settings_widget.dart │ │ └── quran_settings_store.dart │ ├── quran_settings_app │ │ ├── quran_settings_app_store.g.dart │ │ ├── quran_settings_app_store.dart │ │ └── quran_settings_app_widget.dart │ ├── quran_settings_theme │ │ ├── quran_settings_theme_store.g.dart │ │ ├── quran_settings_theme_store.dart │ │ └── quran_settings_theme_widget.dart │ ├── quran_settings_fontsizes │ │ ├── quran_settings_fontsizes_store.g.dart │ │ ├── quran_settings_fontsizes_store.dart │ │ └── quran_settings_fontsizes_widget.dart │ ├── quran_settings_translations │ │ ├── quran_settings_translations_store.g.dart │ │ ├── quran_settings_translation_item_store.dart │ │ └── quran_settings_translations_store.dart │ ├── home_tab_surah │ │ ├── home_tab_surah_store.g.dart │ │ └── home_tab_surah_store.dart │ ├── quran │ │ └── quran_store.g.dart │ └── quran_navigator │ │ ├── quran_navigator_store.g.dart │ │ └── quran_navigator_store.dart ├── app_widgets │ ├── shimmer_loading.dart │ └── app_icon_button.dart ├── extensions │ └── settings_extension.dart ├── helpers │ └── sort_comparison_builder.dart └── main.dart ├── android ├── gradle.properties ├── .gitignore ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── yunus │ │ │ │ │ └── quran_app │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── screenshots ├── Screenshot_2020-05-27-20-23-36-351_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-23-43-527_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-23-54-591_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-24-01-308_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-24-09-414_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-24-23-497_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-24-28-043_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-24-33-849_com.yunus.quran_app.png ├── Screenshot_2020-05-27-20-24-38-538_com.yunus.quran_app.png └── Screenshot_2020-05-27-20-24-44-403_com.yunus.quran_app.png ├── analysis_options.yaml ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── .metadata ├── .gitignore ├── LICENSE ├── test └── widget_test.dart ├── .github └── workflows │ └── main.yml ├── README.md └── pubspec.yaml /i18n/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /.fvm/flutter_sdk: -------------------------------------------------------------------------------- 1 | /Users/yunus/fvm/versions/2.2.1 -------------------------------------------------------------------------------- /.fvm/fvm_config.json: -------------------------------------------------------------------------------- 1 | {"flutterSdkVersion":"2.2.1"} -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/web/favicon.png -------------------------------------------------------------------------------- /fonts/noorehira.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/fonts/noorehira.ttf -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /assets/quran-data/quran.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/assets/quran-data/quran.db -------------------------------------------------------------------------------- /fonts/UthmanTN1-Ver10.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/fonts/UthmanTN1-Ver10.otf -------------------------------------------------------------------------------- /assets/images/quran-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/assets/images/quran-solid.png -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/quran-data/quran-uthmani.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/assets/quran-data/quran-uthmani.db -------------------------------------------------------------------------------- /assets/quran-data/translations.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/assets/quran-data/translations.db -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/models/setting_ids.dart: -------------------------------------------------------------------------------- 1 | class SettingIds { 2 | static const String translationId = '1804ef52-aabc-4b68-bbc7-c0eb685910a4'; 3 | } 4 | -------------------------------------------------------------------------------- /assets/quran-data/translations/en.sahih.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/assets/quran-data/translations/en.sahih.db -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/quran-data/translations/id.indonesian.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/assets/quran-data/translations/id.indonesian.db -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-23-36-351_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-23-36-351_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-23-43-527_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-23-43-527_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-23-54-591_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-23-54-591_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-24-01-308_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-24-01-308_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-24-09-414_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-24-09-414_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-24-23-497_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-24-23-497_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-24-28-043_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-24-28-043_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-24-33-849_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-24-33-849_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-24-38-538_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-24-38-538_com.yunus.quran_app.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2020-05-27-20-24-44-403_com.yunus.quran_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusefendi52/quran_app/HEAD/screenshots/Screenshot_2020-05-27-20-24-44-403_com.yunus.quran_app.png -------------------------------------------------------------------------------- /lib/services/moors/appdb.moor: -------------------------------------------------------------------------------- 1 | CREATE TABLE "quran_bookmarks" ( 2 | "id" INTEGER, 3 | "sura" INTEGER, 4 | "sura_name" TEXT, 5 | "aya" INTEGER, 6 | "insert_time" INTEGER, 7 | PRIMARY KEY("id") 8 | ); 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | quran_app 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/baselib/base_widgetparameter_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | mixin BaseWidgetParameterMixin on StatefulWidget { 4 | final _parameter = Map(); 5 | Map get parameter => _parameter; 6 | } 7 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | linter: 2 | rules: 3 | # - close_sinks # not reliable enough https://github.com/flutter/flutter/blob/6a337a76dda033098decc14fe0f8d0c497ad8ee1/analysis_options.yaml#L95 4 | analyzer: 5 | errors: 6 | import_of_legacy_library_into_null_safe: ignore 7 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dart.flutterSdkPaths": [ 3 | ".fvm/flutter_sdk" 4 | ], 5 | // Remove .fvm files from search 6 | "search.exclude": { 7 | "**/.fvm": true 8 | }, 9 | // Remove from file watching 10 | "files.watcherExclude": { 11 | "**/.fvm": true 12 | } 13 | } -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.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: 0b8abb4724aa590dd0f429683339b1e045a1594d 8 | channel: unknown 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /lib/baselib/base_state_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'base_store.dart'; 4 | 5 | mixin BaseStateMixin 6 | on State { 7 | TStore get store; 8 | 9 | @override 10 | void dispose() { 11 | store.dispose(); 12 | 13 | super.dispose(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/baselib/disposable.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/foundation.dart'; 3 | 4 | abstract class Disposable { 5 | void dispose(); 6 | } 7 | 8 | class DisposableBuilder extends Disposable { 9 | final Function disposeFunction; 10 | 11 | DisposableBuilder({ 12 | @required this.disposeFunction, 13 | }); 14 | 15 | @override 16 | void dispose() { 17 | if (disposeFunction != null) { 18 | disposeFunction(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/routes/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:quran_app/pages/home/home_widget.dart'; 3 | import 'package:quran_app/pages/quran/quran_widget.dart'; 4 | import 'package:quran_app/pages/splash/splash_widget.dart'; 5 | 6 | class Routes { 7 | static Map routes = { 8 | '/': (context) => SplashWidget(), 9 | '/home': (context) => HomeWidget(), 10 | '/quran': (context) => QuranWidget(), 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/pages/error/error_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | 4 | class MyErrorWidget extends StatelessWidget { 5 | final String message; 6 | 7 | const MyErrorWidget({ 8 | @required this.message, 9 | Key key, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | child: Center( 16 | child: Text( 17 | message, 18 | ), 19 | ), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/yunus/quran_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yunus.quran_app 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/baselib/base_store.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'disposable.dart'; 3 | 4 | class BaseStore extends Disposable { 5 | Queue _disposables = Queue(); 6 | Queue get disposables => _disposables; 7 | 8 | void registerDispose(Function function) { 9 | disposables.add( 10 | DisposableBuilder(disposeFunction: function), 11 | ); 12 | } 13 | 14 | @override 15 | Future dispose() async { 16 | disposables.forEach((v) { 17 | v.dispose(); 18 | }); 19 | disposables.clear(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /i18n/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "echo", 8 | "type": "shell", 9 | "command": "echo Hello" 10 | }, 11 | { 12 | "label": "watch", 13 | "type": "shell", 14 | "command": "flutter packages pub run build_runner watch --delete-conflicting-outputs", 15 | "problemMatcher": [] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /lib/pages/home/home_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | 4 | import '../../baselib/base_store.dart'; 5 | import '../../baselib/localization_service.dart'; 6 | import '../../main.dart'; 7 | 8 | part 'home_store.g.dart'; 9 | 10 | class HomeStore = _HomeStore with _$HomeStore; 11 | 12 | abstract class _HomeStore extends BaseStore with Store { 13 | var localization = sl.get(); 14 | 15 | _HomeStore({ 16 | ILocalizationService localization, 17 | }) { 18 | this.localization = localization ?? this.localization; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "echo", 8 | "type": "shell", 9 | "command": "echo Hello" 10 | }, 11 | { 12 | "label": "build_runner watch", 13 | "type": "shell", 14 | "command": "fvm flutter packages pub run build_runner watch --delete-conflicting-outputs", 15 | "problemMatcher": [] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /lib/pages/home_tab/home_tab_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/base_store.dart'; 4 | import 'package:quran_app/baselib/localization_service.dart'; 5 | 6 | import '../../main.dart'; 7 | 8 | part 'home_tab_store.g.dart'; 9 | 10 | class HomeTabStore = _HomeTabStore with _$HomeTabStore; 11 | 12 | abstract class _HomeTabStore extends BaseStore with Store { 13 | var localization = sl.get(); 14 | 15 | _HomeTabStore({ 16 | ILocalizationService localization, 17 | }) { 18 | this.localization = localization ?? this.localization; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /lib/baselib/interaction.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'disposable.dart'; 3 | 4 | typedef InteractionGetter = Future Function(T2 value); 5 | 6 | class Interaction { 7 | List> _handlers = []; 8 | 9 | Disposable registerHandler(InteractionGetter f) { 10 | _handlers.add(f); 11 | return DisposableBuilder(disposeFunction: () { 12 | _handlers.remove(f); 13 | }); 14 | } 15 | 16 | Future handle(TInput input) async { 17 | TOutput output; 18 | for (var item in _handlers) { 19 | output = await item(input); 20 | } 21 | return output; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/pages/home/home_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'home_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$HomeStore on _HomeStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/pages/main/main_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'main_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$MainStore on _MainStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = quran_app 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.yunus.quranApp 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2021 com.yunus. All rights reserved. 15 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quran_app", 3 | "short_name": "quran_app", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /lib/pages/splash/splash_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'splash_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$SplashStore on _SplashStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/services/qurandb.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'qurandb.dart'; 4 | 5 | // ************************************************************************** 6 | // MoorGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this 10 | abstract class _$QuranDb extends GeneratedDatabase { 11 | _$QuranDb(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); 12 | @override 13 | Iterable get allTables => allSchemaEntities.whereType(); 14 | @override 15 | List get allSchemaEntities => []; 16 | } 17 | -------------------------------------------------------------------------------- /lib/pages/home_tab/home_tab_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'home_tab_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$HomeTabStore on _HomeTabStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/pages/bookmarks/bookmarks_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'bookmarks_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$BookmarksStore on _BookmarksStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/services/translationdb.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'translationdb.dart'; 4 | 5 | // ************************************************************************** 6 | // MoorGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this 10 | abstract class _$TranslationDb extends GeneratedDatabase { 11 | _$TranslationDb(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); 12 | @override 13 | Iterable get allTables => allSchemaEntities.whereType(); 14 | @override 15 | List get allSchemaEntities => []; 16 | } 17 | -------------------------------------------------------------------------------- /lib/pages/home_tab_juz/home_tab_juz_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'home_tab_juz_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$HomeTabJuzStore on _HomeTabJuzStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import path_provider_macos 9 | import shared_preferences_macos 10 | import sqflite 11 | import sqlite3_flutter_libs 12 | 13 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 14 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 15 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 16 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 17 | Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) 18 | } 19 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /lib/pages/quran_settings/quran_settings_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'quran_settings_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$QuranSettingsStore on _QuranSettingsStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_app/quran_settings_app_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'quran_settings_app_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$QuranSettingsAppStore on _QuranSettingsAppStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /assets/i18n/default.yaml: -------------------------------------------------------------------------------- 1 | AppName: Quran App 2 | home_widget.sura: Sura 3 | home_widget.juz: Juz 4 | quran_navigator_widget.pick_sura: Pick Sura 5 | quran_navigator_widget.pick_aya: Pick Aya 6 | quran_settings_translations.translations: Translations 7 | quran_settings.title: Settings 8 | quran_settings_fontsizes.arabic_fontsize: Arabic font size 9 | quran_settings_fontsizes.translation_fontsize: Translation font size 10 | quran_settings_theme.title: Theme 11 | home_tab_juz.juz: Juz 12 | bookmarks.title: Bookmarks 13 | bookmarks.no_bookmarks: No bookmarks added 14 | bookmarks.add_to_bookmark: Add bookmark 15 | bookmarks.remove_bookmark: Remove bookmark 16 | quran_settings_app_widget.title: App Settings 17 | quran_settings_app_widget.language: App Language 18 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_theme/quran_settings_theme_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'quran_settings_theme_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$QuranSettingsThemeStore on _QuranSettingsThemeStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/app_widgets/shimmer_loading.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | import 'package:shimmer/shimmer.dart'; 4 | 5 | class ShimmerLoading extends StatelessWidget { 6 | final Widget child; 7 | final double height; 8 | final double width; 9 | 10 | const ShimmerLoading({ 11 | this.child, 12 | Key key, 13 | this.height = 0, 14 | this.width = 0, 15 | }) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Shimmer.fromColors( 20 | baseColor: Colors.grey[300], 21 | highlightColor: Colors.grey[100], 22 | child: Container( 23 | height: height, 24 | width: width, 25 | color: Colors.white, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /assets/i18n/id_ID.yaml: -------------------------------------------------------------------------------- 1 | AppName: Quran App 2 | home_widget.sura: Surah 3 | home_widget.juz: Juz 4 | quran_navigator_widget.pick_sura: Pilih Surah 5 | quran_navigator_widget.pick_aya: Pilih Ayat 6 | quran_settings_translations.translations: Terjemahan 7 | quran_settings.title: Pengaturan 8 | quran_settings_fontsizes.arabic_fontsize: Ukuran huruf arab 9 | quran_settings_fontsizes.translation_fontsize: Ukuran huruf terjemahan 10 | quran_settings_theme.title: Tema 11 | home_tab_juz.juz: Juz 12 | bookmarks.title: Bookmarks 13 | bookmarks.no_bookmarks: Anda belum menambahkan bookmarks 14 | bookmarks.add_to_bookmark: Tambah bookmark 15 | bookmarks.remove_bookmark: Hapus bookmark 16 | quran_settings_app_widget.title: Pengaturan App 17 | quran_settings_app_widget.language: Bahasa App 18 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_fontsizes/quran_settings_fontsizes_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'quran_settings_fontsizes_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$QuranSettingsFontsizesStore on _QuranSettingsFontsizesStore, Store { 13 | @override 14 | String toString() { 15 | return ''' 16 | 17 | '''; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /lib/app_widgets/app_icon_button.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | 4 | class AppIconButton extends StatelessWidget { 5 | final Widget icon; 6 | final Function onTap; 7 | 8 | const AppIconButton({ 9 | Key key, 10 | @required this.icon, 11 | @required this.onTap, 12 | }) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return InkWell( 17 | onTap: onTap, 18 | customBorder: RoundedRectangleBorder( 19 | borderRadius: BorderRadius.circular( 20 | 30, 21 | ), 22 | ), 23 | child: Container( 24 | padding: EdgeInsets.symmetric( 25 | horizontal: 6, 26 | vertical: 6, 27 | ), 28 | child: icon, 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /i18n/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart", 11 | }, 12 | { 13 | "name": "Flutter profile mode", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile", 17 | }, 18 | { 19 | "name": "PowerShell Launch Current File", 20 | "type": "PowerShell", 21 | "request": "launch", 22 | "script": "${file}", 23 | "cwd": "${file}" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /lib/pages/home/home_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | import 'package:quran_app/baselib/base_state_mixin.dart'; 4 | import 'package:quran_app/pages/home_tab/home_tab_widget.dart'; 5 | 6 | import 'home_store.dart'; 7 | 8 | class HomeWidget extends StatefulWidget { 9 | HomeWidget({Key key}) : super(key: key); 10 | 11 | _HomeWidgetState createState() => _HomeWidgetState(); 12 | } 13 | 14 | class _HomeWidgetState extends State 15 | with BaseStateMixin, AutomaticKeepAliveClientMixin { 16 | final _store = HomeStore(); 17 | @override 18 | HomeStore get store => _store; 19 | 20 | @override 21 | bool get wantKeepAlive => true; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | super.build(context); 26 | 27 | return Scaffold( 28 | body: HomeTabWidget(), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/extensions/settings_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:rx_shared_preferences/rx_shared_preferences.dart'; 2 | 3 | import '../main.dart'; 4 | 5 | extension SettingsExtensions on RxSharedPreferences { 6 | Future setArabicFontSize(double value) async { 7 | await rxPrefs.setDouble('arabicFontSize', value); 8 | return true; 9 | } 10 | 11 | Future getArabicFontSize() async { 12 | var f = await rxPrefs.getDouble('arabicFontSize'); 13 | if (f == null) { 14 | return 28.0; 15 | } 16 | return f; 17 | } 18 | 19 | Future setTranslationFontSize(double value) async { 20 | await rxPrefs.setDouble('TranslationFontSize', value); 21 | return true; 22 | } 23 | 24 | Future getTranslationFontSize() async { 25 | var f = await rxPrefs.getDouble('TranslationFontSize'); 26 | if (f == null) { 27 | return 16.0; 28 | } 29 | return f; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/services/appdb.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'dart:io'; 3 | 4 | import 'package:moor/moor.dart'; 5 | import 'package:moor/ffi.dart'; 6 | import 'package:path_provider/path_provider.dart'; 7 | import 'package:path/path.dart' as p; 8 | 9 | part 'appdb.g.dart'; 10 | 11 | @UseMoor( 12 | include: { 13 | 'moors/appdb.moor', 14 | }, 15 | ) 16 | class AppDb extends _$AppDb { 17 | AppDb() : super(_openConnection()); 18 | 19 | @override 20 | int get schemaVersion => 1; 21 | } 22 | 23 | LazyDatabase _openConnection() { 24 | return LazyDatabase(() async { 25 | final dbFolder = await getApplicationDocumentsDirectory(); 26 | final file = File(p.join(dbFolder.path, 'appdb.sqlite')); 27 | return VmDatabase(file); 28 | }); 29 | } 30 | 31 | extension QuranBookmarkExtension on QuranBookmark { 32 | DateTime get insertDateTime { 33 | var dateTime = DateTime.fromMicrosecondsSinceEpoch(this.insertTime); 34 | return dateTime; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart", 11 | "program": "lib/main.dart", 12 | "args": [ 13 | "--no-sound-null-safety" 14 | ] 15 | }, 16 | { 17 | "name": "Flutter profile mode", 18 | "request": "launch", 19 | "type": "dart", 20 | "flutterMode": "profile", 21 | }, 22 | { 23 | "name": "PowerShell Launch Current File", 24 | "type": "PowerShell", 25 | "request": "launch", 26 | "script": "${file}", 27 | "cwd": "${file}" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /lib/pages/splash/splash_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | import 'package:quran_app/baselib/base_state_mixin.dart'; 4 | import 'package:quran_app/baselib/base_widgetparameter_mixin.dart'; 5 | import 'splash_store.dart'; 6 | 7 | class SplashWidget extends StatefulWidget with BaseWidgetParameterMixin { 8 | SplashWidget({Key key}) : super(key: key); 9 | 10 | _SplashWidgetState createState() => _SplashWidgetState(); 11 | } 12 | 13 | class _SplashWidgetState extends State 14 | with BaseStateMixin { 15 | final _store = SplashStore(); 16 | @override 17 | SplashStore get store => _store; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | 23 | WidgetsBinding.instance.addPostFrameCallback((_) { 24 | store.initialize.execute(); 25 | }); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | body: Center( 32 | child: CircularProgressIndicator(), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yunus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/services/qurandb.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:moor/moor.dart'; 4 | import 'package:moor/ffi.dart'; 5 | import 'package:quran_app/baselib/app_services.dart'; 6 | import 'package:quran_app/services/quran_provider.dart'; 7 | import 'package:path/path.dart'; 8 | 9 | import '../main.dart'; 10 | 11 | part 'qurandb.g.dart'; 12 | 13 | @UseMoor() 14 | class QuranDb extends _$QuranDb { 15 | QuranDb() : super(_openConnection()); 16 | 17 | @override 18 | int get schemaVersion => 1; 19 | 20 | Future isQuranTableNameExists(String tableName) async { 21 | var l = await customSelect( 22 | 'SELECT name FROM sqlite_master WHERE type="table" AND name="${tableName}"', 23 | ).get(); 24 | var isExists = l.isNotEmpty; 25 | return isExists; 26 | } 27 | } 28 | 29 | LazyDatabase _openConnection() { 30 | return LazyDatabase(() { 31 | var appServices = sl.get(); 32 | var directory = getQuranFolder(appServices); 33 | var translationDbPath = join( 34 | directory.path, 35 | 'quran.db', 36 | ); 37 | var f = File(translationDbPath); 38 | return VmDatabase(f); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /lib/services/translationdb.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:moor/moor.dart'; 4 | import 'package:moor/ffi.dart'; 5 | import 'package:quran_app/baselib/app_services.dart'; 6 | import 'package:quran_app/services/quran_provider.dart'; 7 | import 'package:path/path.dart'; 8 | 9 | import '../main.dart'; 10 | 11 | part 'translationdb.g.dart'; 12 | 13 | @UseMoor() 14 | class TranslationDb extends _$TranslationDb { 15 | TranslationDb() : super(_openConnection()); 16 | 17 | @override 18 | int get schemaVersion => 1; 19 | 20 | Future isTranslationTableExists(String name) async { 21 | var l = await customSelect( 22 | 'SELECT name FROM sqlite_master WHERE type="table" AND name="${name}"', 23 | ).get(); 24 | var isExists = l.isNotEmpty; 25 | return isExists; 26 | } 27 | } 28 | 29 | LazyDatabase _openConnection() { 30 | return LazyDatabase(() { 31 | var appServices = sl.get(); 32 | var directory = getQuranFolder(appServices); 33 | var translationDbPath = join( 34 | directory.path, 35 | 'translations.db', 36 | ); 37 | var f = File(translationDbPath); 38 | return VmDatabase(f); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // // This is a basic Flutter widget test. 2 | // // 3 | // // To perform an interaction with a widget in your test, use the WidgetTester 4 | // // utility that Flutter provides. For example, you can send tap and scroll 5 | // // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | // import 'package:flutter/material.dart'; 9 | // import 'package:flutter_test/flutter_test.dart'; 10 | 11 | // import 'package:quran_app/main.dart'; 12 | 13 | // void main() { 14 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // // Build our app and trigger a frame. 16 | // await tester.pumpWidget(MyApp()); 17 | 18 | // // Verify that our counter starts at 0. 19 | // expect(find.text('0'), findsOneWidget); 20 | // expect(find.text('1'), findsNothing); 21 | 22 | // // Tap the '+' icon and trigger a frame. 23 | // await tester.tap(find.byIcon(Icons.add)); 24 | // await tester.pump(); 25 | 26 | // // Verify that our counter has incremented. 27 | // expect(find.text('0'), findsNothing); 28 | // expect(find.text('1'), findsOneWidget); 29 | // }); 30 | // } 31 | -------------------------------------------------------------------------------- /lib/baselib/app_services.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:logger/logger.dart'; 4 | import 'package:path_provider/path_provider.dart'; 5 | 6 | abstract class AppServices { 7 | GlobalKey navigatorStateKey; 8 | 9 | NavigatorState get navigatorState; 10 | 11 | Logger get logger; 12 | 13 | Future initialize(); 14 | 15 | String get applicationDocumentsDirectory; 16 | } 17 | 18 | class AppServicesImplementation implements AppServices { 19 | NavigatorState _navigatorState; 20 | NavigatorState get navigatorState { 21 | if (_navigatorState == null) { 22 | _navigatorState = navigatorStateKey.currentState; 23 | } 24 | return _navigatorState; 25 | } 26 | 27 | @override 28 | GlobalKey navigatorStateKey = GlobalKey(); 29 | 30 | Logger _logger; 31 | Logger get logger => _logger ?? (_logger = Logger()); 32 | 33 | String _applicationDocumentsDirectory; 34 | @override 35 | String get applicationDocumentsDirectory => _applicationDocumentsDirectory; 36 | 37 | @override 38 | Future initialize() async { 39 | var path = await getApplicationDocumentsDirectory(); 40 | _applicationDocumentsDirectory = path.path; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/models/translation_data.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:rxdart/rxdart.dart'; 4 | import 'package:quiver/core.dart'; 5 | import 'package:json_annotation/json_annotation.dart'; 6 | 7 | part 'translation_data.g.dart'; 8 | 9 | @JsonSerializable() 10 | class TranslationData { 11 | late String id; 12 | 13 | late String tableName; 14 | 15 | late String uri; 16 | 17 | late String languageCode; 18 | 19 | late String language; 20 | 21 | late String name; 22 | 23 | late String translator; 24 | 25 | late TranslationType type; 26 | 27 | String get filename { 28 | return type == TranslationType.builtIn ? uri : '$id.$languageCode.db'; 29 | } 30 | 31 | final _isSelected$ = BehaviorSubject( 32 | sync: true, 33 | ); 34 | BehaviorSubject get isSelected$ => _isSelected$; 35 | 36 | static TranslationData fromMap(Map json) => 37 | _$TranslationDataFromJson(json); 38 | 39 | String toJson() => jsonEncode(_$TranslationDataToJson(this)); 40 | 41 | bool operator ==(o) => 42 | o is TranslationData && id == o.id && name == o.name && uri == o.uri; 43 | 44 | @override 45 | int get hashCode => hash3(id, uri, name); 46 | } 47 | 48 | enum TranslationType { 49 | builtIn, 50 | download, 51 | } 52 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_translations/quran_settings_translations_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'quran_settings_translations_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$QuranSettingsTranslationsStore 13 | on _QuranSettingsTranslationsStore, Store { 14 | final _$translationsAtom = 15 | Atom(name: '_QuranSettingsTranslationsStore.translations'); 16 | 17 | @override 18 | ObservableList get translations { 19 | _$translationsAtom.reportRead(); 20 | return super.translations; 21 | } 22 | 23 | @override 24 | set translations(ObservableList value) { 25 | _$translationsAtom.reportWrite(value, super.translations, () { 26 | super.translations = value; 27 | }); 28 | } 29 | 30 | @override 31 | String toString() { 32 | return ''' 33 | translations: ${translations} 34 | '''; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/baselib/widgets.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class WidgetSelector extends StatelessWidget { 5 | final Map states; 6 | final T selectedState; 7 | 8 | WidgetSelector({ 9 | @required this.selectedState, 10 | @required this.states, 11 | }); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return states[selectedState] ?? Container(); 16 | } 17 | } 18 | 19 | class DataState { 20 | static DataState none = DataState( 21 | enumSelector: EnumSelector.none, 22 | ); 23 | static DataState loading = DataState( 24 | enumSelector: EnumSelector.loading, 25 | ); 26 | static DataState error = DataState( 27 | enumSelector: EnumSelector.error, 28 | ); 29 | static DataState success = DataState( 30 | enumSelector: EnumSelector.success, 31 | ); 32 | 33 | final EnumSelector enumSelector; 34 | String message; 35 | 36 | DataState({ 37 | @required this.enumSelector, 38 | this.message, 39 | }); 40 | 41 | bool operator ==(o) { 42 | DataState dataState = o; 43 | var b = enumSelector == dataState.enumSelector; 44 | return b; 45 | } 46 | 47 | @override 48 | int get hashCode => enumSelector.hashCode; 49 | 50 | @override 51 | String toString() { 52 | return '$enumSelector - $message'; 53 | } 54 | } 55 | 56 | enum EnumSelector { 57 | none, 58 | loading, 59 | error, 60 | success, 61 | } 62 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.11' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /assets/translations.yaml: -------------------------------------------------------------------------------- 1 | - id: 8034424dac8f44cb997db6b6ddc62147 2 | tableName: en_sahih 3 | languageCode: en 4 | language: English 5 | name: Saheeh International 6 | translator: Saheeh International 7 | type: builtIn 8 | - id: 1f0492c5d1fd42c1950d76ca74ddc0be 9 | tableName: id_indonesian 10 | languageCode: id 11 | language: Indonesia 12 | name: Bahasa Indonesia 13 | translator: Indonesian Ministry of Religious Affairs 14 | type: builtIn 15 | - id: 83506aba0cfa4d8f9486854e1f83775b 16 | tableName: es_garcia 17 | uri: http://tanzil.net/trans/?transID=es.garcia&type=xml 18 | languageCode: es 19 | language: Spanish 20 | name: Garcia 21 | translator: Muhammad Isa García 22 | type: download 23 | - id: 9873178e49fb4681a2681c81845e749c 24 | tableName: sqnahi 25 | uri: http://tanzil.net/trans/sq.nahi&type=xml 26 | languageCode: sq 27 | language: Albanian 28 | name: Efendi Nahi 29 | translator: Hasan Efendi Nahi 30 | type: download 31 | - id: a81152303f5147f6873487ffc603fe75 32 | tableName: de_aburida 33 | uri: http://tanzil.net/trans/de.aburida&type=xml 34 | languageCode: de 35 | language: German 36 | name: Abu Rida 37 | translator: Abu Rida Muhammad ibn Ahmad ibn Rassoul 38 | type: download 39 | - id: 3622b2a1a74740c7bff5cf44c69d177a 40 | tableName: hi_farooq 41 | uri: http://tanzil.net/trans/hi.farooq&type=xml 42 | languageCode: hi 43 | language: Hindi 44 | name: फ़ारूक़ ख़ान & अहमद 45 | translator: Muhammad Farooq Khan and Muhammad Ahmed 46 | type: download 47 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/pages/home_tab_surah/home_tab_surah_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'home_tab_surah_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$HomeTabSurahStore on _HomeTabSurahStore, Store { 13 | final _$chaptersAtom = Atom(name: '_HomeTabSurahStore.chapters'); 14 | 15 | @override 16 | ObservableList get chapters { 17 | _$chaptersAtom.reportRead(); 18 | return super.chapters; 19 | } 20 | 21 | @override 22 | set chapters(ObservableList value) { 23 | _$chaptersAtom.reportWrite(value, super.chapters, () { 24 | super.chapters = value; 25 | }); 26 | } 27 | 28 | final _$stateAtom = Atom(name: '_HomeTabSurahStore.state'); 29 | 30 | @override 31 | DataState get state { 32 | _$stateAtom.reportRead(); 33 | return super.state; 34 | } 35 | 36 | @override 37 | set state(DataState value) { 38 | _$stateAtom.reportWrite(value, super.state, () { 39 | super.state = value; 40 | }); 41 | } 42 | 43 | @override 44 | String toString() { 45 | return ''' 46 | chapters: ${chapters}, 47 | state: ${state} 48 | '''; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/pages/splash/splash_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/command.dart'; 4 | import 'package:quran_app/services/quran_provider.dart'; 5 | import '../../baselib/localization_service.dart'; 6 | 7 | import '../../baselib/app_services.dart'; 8 | import '../../baselib/base_store.dart'; 9 | import '../../main.dart'; 10 | 11 | part 'splash_store.g.dart'; 12 | 13 | class SplashStore = _SplashStore with _$SplashStore; 14 | 15 | abstract class _SplashStore extends BaseStore with Store { 16 | var _appServices = sl.get(); 17 | var _localizationService = sl.get(); 18 | var _quranProvider = sl.get(); 19 | 20 | _SplashStore({ 21 | AppServices appServices, 22 | ILocalizationService localizationService, 23 | QuranProvider quranProvider, 24 | }) { 25 | _appServices = appServices ?? _appServices; 26 | _localizationService = localizationService ?? _localizationService; 27 | _quranProvider = quranProvider ?? _quranProvider; 28 | 29 | initialize = Command(() async { 30 | await _appServices.initialize(); 31 | 32 | await _quranProvider.initialize(); 33 | 34 | // Load localization 35 | var language = await _localizationService.getSavedLanguage(); 36 | await _localizationService.loadFromBundle( 37 | language.locale, 38 | ); 39 | 40 | await _appServices.navigatorState.pushNamedAndRemoveUntil( 41 | '/home', 42 | (_) => false, 43 | ); 44 | }); 45 | } 46 | 47 | Command initialize; 48 | } 49 | -------------------------------------------------------------------------------- /lib/pages/quran/quran_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'quran_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$QuranStore on _QuranStore, Store { 13 | final _$sourceListAyaAtom = Atom(name: '_QuranStore.sourceListAya'); 14 | 15 | @override 16 | ObservableList get sourceListAya { 17 | _$sourceListAyaAtom.reportRead(); 18 | return super.sourceListAya; 19 | } 20 | 21 | @override 22 | set sourceListAya(ObservableList value) { 23 | _$sourceListAyaAtom.reportWrite(value, super.sourceListAya, () { 24 | super.sourceListAya = value; 25 | }); 26 | } 27 | 28 | final _$chaptersAtom = Atom(name: '_QuranStore.chapters'); 29 | 30 | @override 31 | ObservableList get chapters { 32 | _$chaptersAtom.reportRead(); 33 | return super.chapters; 34 | } 35 | 36 | @override 37 | set chapters(ObservableList value) { 38 | _$chaptersAtom.reportWrite(value, super.chapters, () { 39 | super.chapters = value; 40 | }); 41 | } 42 | 43 | @override 44 | String toString() { 45 | return ''' 46 | sourceListAya: ${sourceListAya}, 47 | chapters: ${chapters} 48 | '''; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - v* 5 | name: Build and Release apk 6 | jobs: 7 | build: 8 | name: Build APK 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | 13 | # - name: set environment variables 14 | # uses: allenevans/set-env@v1.0.0 15 | # with: 16 | # KEYSTORE_FILE: 'KEYSTORE' 17 | 18 | - uses: actions/setup-java@v1 19 | with: 20 | java-version: '12.x' 21 | 22 | - uses: subosito/flutter-action@v1 23 | with: 24 | flutter-version: '2.2.1' 25 | - run: flutter pub get 26 | # - run: flutter test 27 | - run: flutter build apk 28 | env: 29 | KEYSTORE_FILE: KEYSTORE_FILE 30 | 31 | - name: Rename apk 32 | shell: pwsh 33 | run: Rename-Item "build/app/outputs/apk/release/app-release.apk" "com.yunus.quran_app.apk" 34 | 35 | - name: Sign apk 36 | uses: r0adkll/sign-android-release@v1 37 | with: 38 | releaseDirectory: build/app/outputs/apk/release 39 | signingKeyBase64: ${{ secrets.KEYSTORE }} 40 | alias: ${{ secrets.KEYSTORE_ALIAS }} 41 | keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }} 42 | keyPassword: ${{ secrets.KEYSTORE_PASSWORD }} 43 | 44 | - name: Upload apk to App Center 45 | uses: wzieba/AppCenter-Github-Action@v1.0.0 46 | if: startsWith(github.ref, 'refs/tags/v') 47 | with: 48 | appName: yunus.efendin97/quran_app 49 | token: ${{secrets.APPCENTER_TOKEN}} 50 | group: Public 51 | file: ${{ env.SIGNED_RELEASE_FILE }} 52 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | quran_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /lib/services/theme_provider.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'theme_provider.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ThemeItem _$ThemeItemFromJson(Map json) => ThemeItem( 10 | name: json['name'] as String? ?? '', 11 | themeType: _$enumDecodeNullable(_$ThemeTypeEnumMap, json['themeType']), 12 | ); 13 | 14 | Map _$ThemeItemToJson(ThemeItem instance) => { 15 | 'name': instance.name, 16 | 'themeType': _$ThemeTypeEnumMap[instance.themeType], 17 | }; 18 | 19 | K _$enumDecode( 20 | Map enumValues, 21 | Object? source, { 22 | K? unknownValue, 23 | }) { 24 | if (source == null) { 25 | throw ArgumentError( 26 | 'A value must be provided. Supported values: ' 27 | '${enumValues.values.join(', ')}', 28 | ); 29 | } 30 | 31 | return enumValues.entries.singleWhere( 32 | (e) => e.value == source, 33 | orElse: () { 34 | if (unknownValue == null) { 35 | throw ArgumentError( 36 | '`$source` is not one of the supported values: ' 37 | '${enumValues.values.join(', ')}', 38 | ); 39 | } 40 | return MapEntry(unknownValue, enumValues.values.first); 41 | }, 42 | ).key; 43 | } 44 | 45 | K? _$enumDecodeNullable( 46 | Map enumValues, 47 | dynamic source, { 48 | K? unknownValue, 49 | }) { 50 | if (source == null) { 51 | return null; 52 | } 53 | return _$enumDecode(enumValues, source, unknownValue: unknownValue); 54 | } 55 | 56 | const _$ThemeTypeEnumMap = { 57 | ThemeType.Light: 'Light', 58 | ThemeType.Night: 'Night', 59 | }; 60 | -------------------------------------------------------------------------------- /lib/services/theme_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:rxdart/rxdart.dart'; 3 | import 'package:rx_shared_preferences/rx_shared_preferences.dart'; 4 | 5 | import '../main.dart'; 6 | 7 | part 'theme_provider.g.dart'; 8 | 9 | enum ThemeType { 10 | Light, 11 | Night, 12 | } 13 | 14 | @JsonSerializable() 15 | class ThemeItem { 16 | late String name; 17 | late ThemeType themeType; 18 | 19 | ThemeItem({ 20 | String name = '', 21 | ThemeType? themeType, 22 | }) { 23 | this.name = name; 24 | this.themeType = themeType!; 25 | } 26 | 27 | operator ==(other) { 28 | return other is ThemeItem && other.themeType == themeType; 29 | } 30 | 31 | @override 32 | int get hashCode => themeType.hashCode; 33 | 34 | @override 35 | String toString() { 36 | return themeType.toString(); 37 | } 38 | } 39 | 40 | class ThemeProviderImplementation { 41 | Future getCurrentTheme() async { 42 | var themeInt = await rxPrefs.getInt('theme'); 43 | var themes = await getThemes(); 44 | var theme = themes.firstWhere( 45 | (t) => t.themeType.index == themeInt, 46 | orElse: () => themes.first, 47 | ); 48 | return theme; 49 | } 50 | 51 | Future setTheme(ThemeItem themeItem) async { 52 | await rxPrefs.setInt('theme', themeItem.themeType.index); 53 | _themeChanged.add(themeItem); 54 | } 55 | 56 | final _themeChanged = BehaviorSubject(); 57 | BehaviorSubject get themeChanged => _themeChanged; 58 | 59 | Future> getThemes() { 60 | return Future.value([ 61 | ThemeItem( 62 | name: 'Light', 63 | themeType: ThemeType.Light, 64 | ), 65 | ThemeItem( 66 | name: 'Night', 67 | themeType: ThemeType.Night, 68 | ), 69 | ]); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/baselib/command.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'dart:async'; 3 | 4 | import 'package:rxdart/rxdart.dart'; 5 | 6 | class Command { 7 | Future Function(TParameter parameter) _execute; 8 | 9 | Command._( 10 | Future Function(TParameter parameter) execute, [ 11 | Future Function(TParameter parameter) canExecute, 12 | ]) { 13 | _execute = execute; 14 | this.canExecute = 15 | canExecute ?? (TParameter parameter) => Future.value(!isExecuting); 16 | } 17 | 18 | factory Command( 19 | Future Function() execute, [ 20 | Future Function() canExecute, 21 | ]) { 22 | return Command._( 23 | (_) => execute(), 24 | (_) => canExecute != null ? canExecute() : Future.value(true), 25 | ); 26 | } 27 | 28 | factory Command.parameter( 29 | Future Function(TParameter parameter) execute, [ 30 | Future Function(TParameter parameter) canExecute, 31 | ]) { 32 | return Command._(execute, canExecute); 33 | } 34 | 35 | Future Function(TParameter parameter) canExecute; 36 | 37 | var isExecuting = false; 38 | 39 | Future execute([TParameter parameter]) async { 40 | try { 41 | isExecuting = true; 42 | 43 | return await _execute(parameter).whenComplete(() { 44 | _subject.add(null); 45 | }); 46 | } finally { 47 | isExecuting = false; 48 | } 49 | } 50 | 51 | Future executeIf([TParameter parameter]) async { 52 | var canExecute = await this.canExecute(parameter); 53 | if (!canExecute || isExecuting) { 54 | return null; 55 | } 56 | 57 | return await execute(parameter).whenComplete(() { 58 | _subject.add(null); 59 | }); 60 | } 61 | 62 | var _subject = BehaviorSubject( 63 | sync: true, 64 | ); 65 | Future get next => _subject.take(1).last; 66 | 67 | BehaviorSubject get execute$ => _subject; 68 | 69 | void dispose() { 70 | _subject.close(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/pages/main/main_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/app_services.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/command.dart'; 6 | import 'package:quran_app/baselib/localization_service.dart'; 7 | import 'package:quran_app/services/theme_provider.dart'; 8 | import 'package:rxdart/rxdart.dart'; 9 | 10 | import '../../main.dart'; 11 | 12 | part 'main_store.g.dart'; 13 | 14 | class MainStore = _MainStore with _$MainStore; 15 | 16 | abstract class _MainStore extends BaseStore with Store { 17 | var localizationService = sl.get(); 18 | var appServices = sl.get(); 19 | var themeProvider = sl.get(); 20 | 21 | _MainStore({ 22 | ILocalizationService localizationService, 23 | AppServices appServices, 24 | ThemeProviderImplementation themeProvider, 25 | }) { 26 | this.localizationService = 27 | localizationService ?? (localizationService = this.localizationService); 28 | this.appServices = appServices ?? (appServices = this.appServices); 29 | this.themeProvider = themeProvider ?? (themeProvider = this.themeProvider); 30 | 31 | getCurrentTheme = Command(() async { 32 | var currentTheme = await themeProvider.getCurrentTheme(); 33 | currentTheme$.add(currentTheme); 34 | }); 35 | 36 | var ds = CompositeSubscription(); 37 | 38 | ds.add(themeProvider.themeChanged.map((_) => null).mergeWith([ 39 | currentThemeRefresher$.map( 40 | (_) => null, 41 | ), 42 | ]).asyncExpand((_) { 43 | return getCurrentTheme.executeIf().asStream(); 44 | }).listen(null)); 45 | 46 | registerDispose(() { 47 | ds.dispose(); 48 | ds = null; 49 | }); 50 | } 51 | 52 | final currentThemeRefresher$ = PublishSubject( 53 | sync: true, 54 | ); 55 | 56 | final currentTheme$ = BehaviorSubject( 57 | sync: true, 58 | ); 59 | 60 | Command getCurrentTheme; 61 | } 62 | -------------------------------------------------------------------------------- /lib/baselib/service_locator.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | class ServiceLocator { 3 | var _providers = Map>>(); 4 | 5 | void registerLazySingleton( 6 | T Function() instanceBuilder, { 7 | String name, 8 | }) { 9 | _setProvider( 10 | name: name, 11 | provider: _ServiceLocatorProvider.singleton(instanceBuilder), 12 | ); 13 | } 14 | 15 | void registerBuilder( 16 | T Function() instanceBuilder, { 17 | String name, 18 | }) { 19 | _setProvider( 20 | name: name, 21 | provider: _ServiceLocatorProvider.builder(instanceBuilder), 22 | ); 23 | } 24 | 25 | T get({ 26 | String name, 27 | }) { 28 | var namedProvider = _providers[name]; 29 | if (namedProvider?.containsKey(T) == true) { 30 | var provider = namedProvider[T]; 31 | return provider?.get(); 32 | } else { 33 | return null; 34 | } 35 | } 36 | 37 | void unregiser({ 38 | String name, 39 | }) { 40 | _providers[name].remove(T); 41 | } 42 | 43 | void clear() { 44 | _providers.clear(); 45 | } 46 | 47 | void _setProvider({ 48 | _ServiceLocatorProvider provider, 49 | String name, 50 | }) { 51 | var map = _providers.putIfAbsent(name, () { 52 | var map = Map>(); 53 | return map; 54 | }); 55 | map[T] = provider; 56 | } 57 | } 58 | 59 | class _ServiceLocatorProvider { 60 | _ServiceLocatorProvider.builder( 61 | this.instanceBuilder, 62 | ); 63 | 64 | _ServiceLocatorProvider.singleton(this.instanceBuilder) : onetime = true; 65 | 66 | final T Function() instanceBuilder; 67 | var onetime = false; 68 | T object; 69 | 70 | T get() { 71 | if (object != null) { 72 | return object; 73 | } 74 | if (onetime && instanceBuilder != null && object == null) { 75 | object = instanceBuilder(); 76 | return object; 77 | } 78 | 79 | if (instanceBuilder != null) { 80 | return instanceBuilder(); 81 | } 82 | return null; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/pages/quran_settings/quran_settings_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | import 'package:quran_app/baselib/base_state_mixin.dart'; 4 | 5 | import 'quran_settings_store.dart'; 6 | 7 | class QuranSettingsWidget extends StatefulWidget { 8 | final QuranSettingsStore store; 9 | 10 | QuranSettingsWidget({ 11 | @required this.store, 12 | Key key, 13 | }) : super(key: key); 14 | 15 | _QuranSettingsWidgetState createState() => _QuranSettingsWidgetState(); 16 | } 17 | 18 | class _QuranSettingsWidgetState extends State 19 | with BaseStateMixin { 20 | @override 21 | Widget build(BuildContext context) { 22 | return Scaffold( 23 | appBar: AppBar( 24 | automaticallyImplyLeading: false, 25 | backgroundColor: Colors.transparent, 26 | elevation: 0, 27 | title: Text( 28 | store.localization.getByKey('quran_settings.title'), 29 | style: TextStyle( 30 | color: Theme.of(context).textTheme.bodyText2.color, 31 | fontSize: 18, 32 | fontWeight: FontWeight.bold, 33 | ), 34 | ), 35 | ), 36 | body: Column( 37 | crossAxisAlignment: CrossAxisAlignment.stretch, 38 | children: [ 39 | // SafeArea( 40 | // child: Container( 41 | // padding: EdgeInsets.only( 42 | // left: 15, 43 | // right: 15, 44 | // top: 10, 45 | // ), 46 | // child: , 47 | // ), 48 | // ), 49 | Expanded( 50 | child: ListView.builder( 51 | itemCount: store.items.length, 52 | itemBuilder: (BuildContext context, int index) { 53 | final item = store.items[index]; 54 | 55 | return item.item1; 56 | }, 57 | ), 58 | ), 59 | ], 60 | ), 61 | ); 62 | } 63 | 64 | @override 65 | QuranSettingsStore get store => widget.store; 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quran_app 2 | 3 | [![Download APK](https://img.shields.io/badge/Download%20APK-App%20Center-GREEN.svg)](https://install.appcenter.ms/users/yunus.efendin97/apps/quran_app/distribution_groups/public) 4 | 5 | Quran app built with Flutter 6 | 7 | ### How to build from source 8 | You can use global flutter commands as usual, or you can install `fvm`: 9 | - Install [fvm](https://fvm.app/docs/getting_started/installation) 10 | - Install specific flutter version with `fvm install theversion`, see pubspec.yaml to see the flutter version 11 | - Run any flutter commands with `fvm flutter ...` instead of `flutter ...` 12 | 13 | ### Features: 14 | - Support dark and ligth theme 15 | - Multiple translations 16 | - Bookmark ayat 17 | - And some basic features 18 | 19 | There is still so much to improve (playing audio, ayah for today, etc) if you want to contribute whether it's a feature or a bug you can send a Pull Request, maybe I can check it out 20 | 21 | ## Screenshot (Dark theme) 22 |

23 | 24 | 25 | 26 | 27 | 28 |

29 | 30 | ## Screenshot (Light theme) 31 |

32 | 33 | 34 | 35 | 36 | 37 |

38 | -------------------------------------------------------------------------------- /lib/models/translation_data.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'translation_data.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TranslationData _$TranslationDataFromJson(Map json) => 10 | TranslationData() 11 | ..id = json['id'] as String 12 | ..tableName = json['tableName'] as String 13 | ..uri = json['uri'] as String 14 | ..languageCode = json['languageCode'] as String 15 | ..language = json['language'] as String 16 | ..name = json['name'] as String 17 | ..translator = json['translator'] as String 18 | ..type = _$enumDecode(_$TranslationTypeEnumMap, json['type']); 19 | 20 | Map _$TranslationDataToJson(TranslationData instance) => 21 | { 22 | 'id': instance.id, 23 | 'tableName': instance.tableName, 24 | 'uri': instance.uri, 25 | 'languageCode': instance.languageCode, 26 | 'language': instance.language, 27 | 'name': instance.name, 28 | 'translator': instance.translator, 29 | 'type': _$TranslationTypeEnumMap[instance.type], 30 | }; 31 | 32 | K _$enumDecode( 33 | Map enumValues, 34 | Object? source, { 35 | K? unknownValue, 36 | }) { 37 | if (source == null) { 38 | throw ArgumentError( 39 | 'A value must be provided. Supported values: ' 40 | '${enumValues.values.join(', ')}', 41 | ); 42 | } 43 | 44 | return enumValues.entries.singleWhere( 45 | (e) => e.value == source, 46 | orElse: () { 47 | if (unknownValue == null) { 48 | throw ArgumentError( 49 | '`$source` is not one of the supported values: ' 50 | '${enumValues.values.join(', ')}', 51 | ); 52 | } 53 | return MapEntry(unknownValue, enumValues.values.first); 54 | }, 55 | ).key; 56 | } 57 | 58 | const _$TranslationTypeEnumMap = { 59 | TranslationType.builtIn: 'builtIn', 60 | TranslationType.download: 'download', 61 | }; 62 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - FMDB (2.7.5): 4 | - FMDB/standard (= 2.7.5) 5 | - FMDB/standard (2.7.5) 6 | - path_provider (0.0.1): 7 | - Flutter 8 | - shared_preferences (0.0.1): 9 | - Flutter 10 | - sqflite (0.0.1): 11 | - Flutter 12 | - FMDB (~> 2.7.2) 13 | - sqlite3 (3.35.5): 14 | - sqlite3/common (= 3.35.5) 15 | - sqlite3/common (3.35.5) 16 | - sqlite3/fts5 (3.35.5): 17 | - sqlite3/common 18 | - sqlite3/json1 (3.35.5): 19 | - sqlite3/common 20 | - sqlite3/perf-threadsafe (3.35.5): 21 | - sqlite3/common 22 | - sqlite3/rtree (3.35.5): 23 | - sqlite3/common 24 | - sqlite3_flutter_libs (0.0.1): 25 | - Flutter 26 | - sqlite3 (~> 3.35.4) 27 | - sqlite3/fts5 28 | - sqlite3/json1 29 | - sqlite3/perf-threadsafe 30 | - sqlite3/rtree 31 | 32 | DEPENDENCIES: 33 | - Flutter (from `Flutter`) 34 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 35 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 36 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 37 | - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) 38 | 39 | SPEC REPOS: 40 | https://github.com/CocoaPods/Specs.git: 41 | - FMDB 42 | trunk: 43 | - sqlite3 44 | 45 | EXTERNAL SOURCES: 46 | Flutter: 47 | :path: Flutter 48 | path_provider: 49 | :path: ".symlinks/plugins/path_provider/ios" 50 | shared_preferences: 51 | :path: ".symlinks/plugins/shared_preferences/ios" 52 | sqflite: 53 | :path: ".symlinks/plugins/sqflite/ios" 54 | sqlite3_flutter_libs: 55 | :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" 56 | 57 | SPEC CHECKSUMS: 58 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c 59 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 60 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 61 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d 62 | sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 63 | sqlite3: 75da32fd4e3574f6c38566a9f0114cfe792c8db5 64 | sqlite3_flutter_libs: aedb7b6ba191f9a052dcef77691cf5d37437431c 65 | 66 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 67 | 68 | COCOAPODS: 1.10.1 69 | -------------------------------------------------------------------------------- /lib/pages/home_tab_surah/home_tab_surah_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/app_services.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/command.dart'; 6 | import 'package:quran_app/baselib/localization_service.dart'; 7 | import 'package:quran_app/baselib/widgets.dart'; 8 | import 'package:quran_app/models/models.dart'; 9 | import 'package:quran_app/services/quran_provider.dart'; 10 | 11 | import '../../main.dart'; 12 | 13 | part 'home_tab_surah_store.g.dart'; 14 | 15 | class HomeTabSurahStore = _HomeTabSurahStore with _$HomeTabSurahStore; 16 | 17 | abstract class _HomeTabSurahStore extends BaseStore with Store { 18 | var _appServices = sl.get(); 19 | var _localization = sl.get(); 20 | var _quranProvider = sl.get(); 21 | 22 | _HomeTabSurahStore({ 23 | AppServices appServices, 24 | QuranProvider quranProvider, 25 | ILocalizationService localizationService, 26 | }) { 27 | _appServices = appServices ?? _appServices; 28 | _quranProvider = quranProvider ?? _quranProvider; 29 | _localization = localizationService ?? _localization; 30 | 31 | fetchSurah = Command(() async { 32 | try { 33 | state = DataState( 34 | enumSelector: EnumSelector.loading, 35 | ); 36 | 37 | var l = await _quranProvider.getChapters(_localization.neutralLocale); 38 | chapters.clear(); 39 | chapters.addAll(l); 40 | 41 | state = DataState( 42 | enumSelector: EnumSelector.success, 43 | ); 44 | } catch (e) { 45 | _appServices.logger.e(e); 46 | 47 | state = DataState( 48 | enumSelector: EnumSelector.error, 49 | message: e?.toString() ?? '', 50 | ); 51 | } 52 | }); 53 | 54 | goToQuran = Command.parameter((v) async { 55 | await _appServices.navigatorState.pushNamed( 56 | '/quran', 57 | arguments: { 58 | 'chapter': v, 59 | }, 60 | ); 61 | }); 62 | } 63 | 64 | @observable 65 | ObservableList chapters = ObservableList(); 66 | 67 | Command fetchSurah; 68 | 69 | Command goToQuran; 70 | 71 | @observable 72 | DataState state = DataState( 73 | enumSelector: EnumSelector.none, 74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_app/quran_settings_app_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/app_services.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/localization_service.dart'; 6 | import 'package:rxdart/rxdart.dart'; 7 | import '../../main.dart'; 8 | import '../quran_settings/quran_settings_store.dart'; 9 | 10 | part 'quran_settings_app_store.g.dart'; 11 | 12 | class QuranSettingsAppStore = _QuranSettingsAppStore 13 | with _$QuranSettingsAppStore; 14 | 15 | abstract class _QuranSettingsAppStore extends BaseStore 16 | with Store 17 | implements QuranSettingsStoreProvider { 18 | var localization = sl.get(); 19 | var appServices = sl.get(); 20 | 21 | _QuranSettingsAppStore({ 22 | ILocalizationService localizationService, 23 | AppServices appServices, 24 | }) { 25 | localization = localizationService ?? localization; 26 | this.appServices = appServices ?? (appServices = this.appServices); 27 | 28 | _settingsItems = SettingsItem(); 29 | 30 | var _supportedLanguages = localization.getSupportedLanguages(); 31 | localization 32 | .getSavedLanguage() 33 | .asStream() 34 | .doOnData((v) { 35 | initialiSelectedLanguage.add(v); 36 | selectedLanguage.add(v); 37 | }) 38 | .take(1) 39 | .listen(null); 40 | supportedLanguages.add(_supportedLanguages); 41 | 42 | { 43 | var d = selectedLanguage.skip(1).asyncExpand((v) { 44 | return Rx.defer(() { 45 | return localization.saveLanguage(v).asStream(); 46 | }); 47 | }).asyncExpand((v) { 48 | return Rx.defer(() { 49 | return appServices.navigatorState 50 | .pushNamedAndRemoveUntil( 51 | '/', 52 | (_) => false, 53 | ) 54 | .asStream(); 55 | }); 56 | }).listen(null); 57 | registerDispose(() { 58 | d.cancel(); 59 | }); 60 | } 61 | } 62 | 63 | SettingsItem _settingsItems; 64 | @override 65 | SettingsItem get settingsItem => _settingsItems; 66 | 67 | final initialiSelectedLanguage = BehaviorSubject(); 68 | 69 | final selectedLanguage = BehaviorSubject(); 70 | 71 | final supportedLanguages = BehaviorSubject>(); 72 | } 73 | -------------------------------------------------------------------------------- /macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | - FMDB (2.7.5): 4 | - FMDB/standard (= 2.7.5) 5 | - FMDB/standard (2.7.5) 6 | - path_provider_macos (0.0.1): 7 | - FlutterMacOS 8 | - shared_preferences_macos (0.0.1): 9 | - FlutterMacOS 10 | - sqflite (0.0.1): 11 | - FlutterMacOS 12 | - FMDB (~> 2.7.2) 13 | - sqlite3 (3.35.5): 14 | - sqlite3/common (= 3.35.5) 15 | - sqlite3/common (3.35.5) 16 | - sqlite3/fts5 (3.35.5): 17 | - sqlite3/common 18 | - sqlite3/json1 (3.35.5): 19 | - sqlite3/common 20 | - sqlite3/perf-threadsafe (3.35.5): 21 | - sqlite3/common 22 | - sqlite3/rtree (3.35.5): 23 | - sqlite3/common 24 | - sqlite3_flutter_libs (0.0.1): 25 | - FlutterMacOS 26 | - sqlite3 (~> 3.35.4) 27 | - sqlite3/fts5 28 | - sqlite3/json1 29 | - sqlite3/perf-threadsafe 30 | - sqlite3/rtree 31 | 32 | DEPENDENCIES: 33 | - FlutterMacOS (from `Flutter/ephemeral`) 34 | - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) 35 | - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) 36 | - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) 37 | - sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`) 38 | 39 | SPEC REPOS: 40 | trunk: 41 | - FMDB 42 | - sqlite3 43 | 44 | EXTERNAL SOURCES: 45 | FlutterMacOS: 46 | :path: Flutter/ephemeral 47 | path_provider_macos: 48 | :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos 49 | shared_preferences_macos: 50 | :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos 51 | sqflite: 52 | :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos 53 | sqlite3_flutter_libs: 54 | :path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos 55 | 56 | SPEC CHECKSUMS: 57 | FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 58 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 59 | path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f 60 | shared_preferences_macos: 480ce071d0666e37cef23fe6c702293a3d21799e 61 | sqflite: 6c1f07e1d4399d619ea619fea9171251dd24d058 62 | sqlite3: 75da32fd4e3574f6c38566a9f0114cfe792c8db5 63 | sqlite3_flutter_libs: b8781af39b939e25ca76000127ed83312b974f08 64 | 65 | PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c 66 | 67 | COCOAPODS: 1.10.1 68 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/services/bookmarks_provider.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:moor/moor.dart'; 3 | 4 | import '../main.dart'; 5 | import 'appdb.dart'; 6 | 7 | // ignore_for_file: invalid_use_of_visible_for_testing_member 8 | // ignore_for_file: invalid_use_of_protected_member 9 | 10 | class BookmarksRequest {} 11 | 12 | abstract class BookmarksProvider { 13 | Future getItem(int aya, int sura); 14 | 15 | Future> getItems(BookmarksRequest request); 16 | 17 | Future addItem(QuranBookmarksCompanion item); 18 | 19 | Future removeItem(QuranBookmark item); 20 | 21 | Future udpateItem(QuranBookmark item); 22 | } 23 | 24 | class SqliteBookmarksProvider implements BookmarksProvider { 25 | AppDb appDb = sl.get(); 26 | 27 | SqliteBookmarksProvider({ 28 | AppDb appDb, 29 | }) { 30 | this.appDb = appDb ?? (appDb = this.appDb); 31 | } 32 | 33 | Future getItem(int aya, int sura) async { 34 | var l = await (appDb.select(appDb.quranBookmarks) 35 | ..where((t) { 36 | return t.sura.equals(sura) & t.aya.equals(aya); 37 | })) 38 | .get(); 39 | return l.firstWhere( 40 | (t) => t != null, 41 | orElse: () => null, 42 | ); 43 | } 44 | 45 | Future> getItems(BookmarksRequest request) async { 46 | var l = await (appDb.select(appDb.quranBookmarks) 47 | ..orderBy([ 48 | (t) => OrderingTerm( 49 | expression: t.insertTime, 50 | mode: OrderingMode.desc, 51 | ) 52 | ])) 53 | .get(); 54 | return l; 55 | } 56 | 57 | Future addItem(QuranBookmarksCompanion item) async { 58 | var existingItem = await (appDb.select(appDb.quranBookmarks) 59 | ..where( 60 | (t) => 61 | t.sura.equals(item.sura.value) & t.aya.equals(item.aya.value), 62 | )) 63 | .get(); 64 | if (existingItem.isNotEmpty) { 65 | return -1; 66 | } 67 | 68 | var id = await appDb.into(appDb.quranBookmarks).insert(item); 69 | return id; 70 | } 71 | 72 | Future removeItem(QuranBookmark item) async { 73 | var id = await (appDb.delete(appDb.quranBookmarks) 74 | ..where( 75 | (t) => t.id.equals(item.id), 76 | )) 77 | .go(); 78 | return id; 79 | } 80 | 81 | Future udpateItem(QuranBookmark item) async { 82 | var b = await appDb.update(appDb.quranBookmarks).replace(item); 83 | return b; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/pages/quran_navigator/quran_navigator_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // @dart=2.11 3 | 4 | part of 'quran_navigator_store.dart'; 5 | 6 | // ************************************************************************** 7 | // StoreGenerator 8 | // ************************************************************************** 9 | 10 | // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 11 | 12 | mixin _$QuranNavigatorStore on _QuranNavigatorStore, Store { 13 | final _$chaptersAtom = Atom(name: '_QuranNavigatorStore.chapters'); 14 | 15 | @override 16 | ObservableList get chapters { 17 | _$chaptersAtom.reportRead(); 18 | return super.chapters; 19 | } 20 | 21 | @override 22 | set chapters(ObservableList value) { 23 | _$chaptersAtom.reportWrite(value, super.chapters, () { 24 | super.chapters = value; 25 | }); 26 | } 27 | 28 | final _$initialSelectedChapterAtom = 29 | Atom(name: '_QuranNavigatorStore.initialSelectedChapter'); 30 | 31 | @override 32 | Chapters get initialSelectedChapter { 33 | _$initialSelectedChapterAtom.reportRead(); 34 | return super.initialSelectedChapter; 35 | } 36 | 37 | @override 38 | set initialSelectedChapter(Chapters value) { 39 | _$initialSelectedChapterAtom 40 | .reportWrite(value, super.initialSelectedChapter, () { 41 | super.initialSelectedChapter = value; 42 | }); 43 | } 44 | 45 | final _$listAyaAtom = Atom(name: '_QuranNavigatorStore.listAya'); 46 | 47 | @override 48 | ObservableList get listAya { 49 | _$listAyaAtom.reportRead(); 50 | return super.listAya; 51 | } 52 | 53 | @override 54 | set listAya(ObservableList value) { 55 | _$listAyaAtom.reportWrite(value, super.listAya, () { 56 | super.listAya = value; 57 | }); 58 | } 59 | 60 | final _$selectedAyaAtom = Atom(name: '_QuranNavigatorStore.selectedAya'); 61 | 62 | @override 63 | int get selectedAya { 64 | _$selectedAyaAtom.reportRead(); 65 | return super.selectedAya; 66 | } 67 | 68 | @override 69 | set selectedAya(int value) { 70 | _$selectedAyaAtom.reportWrite(value, super.selectedAya, () { 71 | super.selectedAya = value; 72 | }); 73 | } 74 | 75 | @override 76 | String toString() { 77 | return ''' 78 | chapters: ${chapters}, 79 | initialSelectedChapter: ${initialSelectedChapter}, 80 | listAya: ${listAya}, 81 | selectedAya: ${selectedAya} 82 | '''; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/pages/main/main_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | import 'package:quran_app/baselib/base_state_mixin.dart'; 4 | import 'package:quran_app/baselib/base_widgetparameter_mixin.dart'; 5 | import 'package:quran_app/routes/routes.dart'; 6 | import 'package:quran_app/services/theme_provider.dart'; 7 | 8 | import 'main_store.dart'; 9 | 10 | class MainWidget extends StatefulWidget { 11 | final MainStore mainStore; 12 | MainWidget({ 13 | @required this.mainStore, 14 | Key key, 15 | }) : super(key: key); 16 | 17 | _MainWidgetState createState() => _MainWidgetState(); 18 | } 19 | 20 | class _MainWidgetState extends State { 21 | @override 22 | void initState() { 23 | super.initState(); 24 | 25 | final store = widget.mainStore; 26 | store.currentThemeRefresher$.add(null); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | final store = widget.mainStore; 32 | return StreamBuilder( 33 | initialData: store.currentTheme$.valueOrNull, 34 | stream: store.currentTheme$, 35 | builder: ( 36 | BuildContext context, 37 | AsyncSnapshot snapshot, 38 | ) { 39 | final themeMapping = { 40 | ThemeItem( 41 | themeType: ThemeType.Light, 42 | ): ThemeData( 43 | primarySwatch: Colors.blue, 44 | ), 45 | ThemeItem( 46 | themeType: ThemeType.Night, 47 | ): ThemeData.dark() 48 | }; 49 | var themeData = themeMapping[snapshot.data] ?? 50 | ThemeData( 51 | primarySwatch: Colors.blue, 52 | ); 53 | 54 | return MaterialApp( 55 | title: 'Quran App', 56 | theme: themeData, 57 | onGenerateRoute: (s) { 58 | var widgetBuilder = Routes.routes[s.name]; 59 | return MaterialPageRoute( 60 | builder: (BuildContext context) { 61 | var widget = widgetBuilder(context); 62 | if (widget is BaseWidgetParameterMixin) { 63 | var baseWidgetParameterMixin = widget; 64 | if (s.arguments != null) { 65 | baseWidgetParameterMixin.parameter.addAll( 66 | Map.from( 67 | s.arguments, 68 | ), 69 | ); 70 | } 71 | } 72 | return widget; 73 | }, 74 | ); 75 | }, 76 | navigatorKey: store.appServices.navigatorStateKey, 77 | ); 78 | }, 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_fontsizes/quran_settings_fontsizes_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:mobx/mobx.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/localization_service.dart'; 6 | import 'package:rxdart/rxdart.dart'; 7 | import '../../extensions/settings_extension.dart'; 8 | 9 | import '../../main.dart'; 10 | import '../quran_settings/quran_settings_store.dart'; 11 | 12 | part 'quran_settings_fontsizes_store.g.dart'; 13 | 14 | class QuranSettingsFontsizesStore = _QuranSettingsFontsizesStore 15 | with _$QuranSettingsFontsizesStore; 16 | 17 | abstract class _QuranSettingsFontsizesStore extends BaseStore 18 | with Store 19 | implements QuranSettingsStoreProvider { 20 | var localization = sl.get(); 21 | 22 | _QuranSettingsFontsizesStore({ 23 | @required Map parameter, 24 | }) { 25 | _settingsItems = SettingsItem(); 26 | 27 | if (parameter[QuranSettingsFontsizesStore] != null) { 28 | Map p = parameter[QuranSettingsFontsizesStore]; 29 | _arabicFontSize$ = p['arabicFontSize']; 30 | _translationFontSize$ = p['translationFontSize']; 31 | } 32 | 33 | var ds = CompositeSubscription(); 34 | 35 | ds.add(arabicFontSizeChanged$ 36 | .doOnData((v) { 37 | arabicFontSize$.add(v); 38 | }) 39 | .debounceTime(const Duration(milliseconds: 300)) 40 | .switchMap((v) { 41 | return DeferStream(() { 42 | return rxPrefs.setArabicFontSize(v).asStream(); 43 | }); 44 | }) 45 | .listen(null)); 46 | 47 | ds.add(translationFontSizeChanged$ 48 | .doOnData((v) { 49 | translationFontSize$.add(v); 50 | }) 51 | .debounceTime(const Duration(milliseconds: 300)) 52 | .switchMap((v) { 53 | return DeferStream(() { 54 | return rxPrefs.setTranslationFontSize(v).asStream().map((_) => v); 55 | }); 56 | }) 57 | .listen(null)); 58 | 59 | registerDispose(() { 60 | ds.dispose(); 61 | ds = null; 62 | }); 63 | } 64 | 65 | SettingsItem _settingsItems; 66 | @override 67 | SettingsItem get settingsItem => _settingsItems; 68 | 69 | BehaviorSubject _arabicFontSize$; 70 | BehaviorSubject get arabicFontSize$ => _arabicFontSize$; 71 | 72 | final arabicFontSizeChanged$ = PublishSubject(); 73 | 74 | BehaviorSubject _translationFontSize$; 75 | BehaviorSubject get translationFontSize$ => _translationFontSize$; 76 | 77 | final translationFontSizeChanged$ = PublishSubject(); 78 | } 79 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.yunus.quran_app" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | if (System.getenv('KEYSTORE_FILE') != null) { 54 | signingConfig null 55 | 56 | applicationVariants.all { variant -> 57 | variant.outputs.all { output -> 58 | output.outputFileName = "app-release.apk" 59 | } 60 | } 61 | } else { 62 | signingConfig signingConfigs.debug 63 | } 64 | } 65 | } 66 | } 67 | 68 | flutter { 69 | source '../..' 70 | } 71 | 72 | dependencies { 73 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 74 | testImplementation 'junit:junit:4.12' 75 | androidTestImplementation 'androidx.test:runner:1.1.1' 76 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 77 | } 78 | -------------------------------------------------------------------------------- /lib/pages/bookmarks/bookmarks_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/app_services.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/localization_service.dart'; 6 | import 'package:quran_app/baselib/widgets.dart'; 7 | import 'package:quran_app/models/models.dart'; 8 | import 'package:quran_app/services/appdb.dart'; 9 | import 'package:quran_app/services/bookmarks_provider.dart'; 10 | import 'package:rx_command/rx_command.dart'; 11 | import 'package:rxdart/rxdart.dart'; 12 | 13 | import '../../main.dart'; 14 | 15 | part 'bookmarks_store.g.dart'; 16 | 17 | class BookmarksStore = _BookmarksStore with _$BookmarksStore; 18 | 19 | abstract class _BookmarksStore extends BaseStore with Store { 20 | var localization = sl.get(); 21 | var bookmarkProvider = sl.get(); 22 | var appServices = sl.get(); 23 | 24 | _BookmarksStore({ 25 | ILocalizationService localizationService, 26 | BookmarksProvider bookmarksProvider, 27 | AppServices appServices, 28 | }) { 29 | this.localization = 30 | localizationService ?? (localizationService = this.localization); 31 | this.bookmarkProvider = 32 | bookmarksProvider ?? (bookmarksProvider = this.bookmarkProvider); 33 | this.appServices = appServices ?? (appServices = this.appServices); 34 | 35 | getBookmarks = RxCommand.createAsyncNoParamNoResult(() async { 36 | bookmarkState.add(DataState.loading); 37 | 38 | var items = await bookmarksProvider.getItems(BookmarksRequest()); 39 | bookmarks.add(items); 40 | 41 | bookmarkState.add(DataState.success); 42 | }); 43 | 44 | removeBookmark = RxCommand.createAsyncNoResult( 45 | (QuranBookmark item) async { 46 | var id = await bookmarksProvider.removeItem(item); 47 | appServices.logger.i('Removed item id: $id'); 48 | 49 | var newList = (bookmarks.value..remove(item)).toList(); 50 | bookmarks.add(newList); 51 | }, 52 | ); 53 | 54 | goToQuran = RxCommand.createAsyncNoResult((QuranBookmark v) async { 55 | await appServices.navigatorState.pushNamed( 56 | '/quran', 57 | arguments: { 58 | 'chapter': Chapters((f) { 59 | f.chapterNumber = v.sura; 60 | f.nameSimple = v.suraName; 61 | }), 62 | 'aya': v.aya, 63 | }, 64 | ); 65 | }); 66 | } 67 | 68 | RxCommand getBookmarks; 69 | 70 | final bookmarkState = BehaviorSubject.seeded( 71 | DataState.none, 72 | ); 73 | 74 | final bookmarks = BehaviorSubject>(); 75 | 76 | RxCommand removeBookmark; 77 | 78 | RxCommand goToQuran; 79 | } 80 | -------------------------------------------------------------------------------- /lib/helpers/sort_comparison_builder.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:quiver/collection.dart'; 3 | 4 | /// 5 | /// Sort direction 6 | /// 7 | enum SortDirection { 8 | /// 9 | /// Sort items ascending 10 | /// 11 | Ascending, 12 | 13 | /// 14 | /// Sort items descending 15 | /// 16 | Descending 17 | } 18 | 19 | class SortExpression { 20 | SortDirection sortDirection; 21 | // int compareTo(T x, T y) { 22 | // return x.compareTo(y); 23 | // } 24 | Comparable Function(T) expression; 25 | } 26 | 27 | class SortComparisonBuilder extends DelegatingList> { 28 | List> _list = []; 29 | 30 | ascending(Comparable Function(T) expression) { 31 | _list.add( 32 | SortExpression() 33 | ..sortDirection = SortDirection.Ascending 34 | ..expression = (t) { 35 | var tt = expression(t); 36 | return tt; 37 | }, 38 | ); 39 | } 40 | 41 | thenByAscending(Comparable Function(T) expression) { 42 | _list.add( 43 | SortExpression() 44 | ..sortDirection = SortDirection.Ascending 45 | ..expression = (t) { 46 | var tt = expression(t); 47 | return tt; 48 | }, 49 | ); 50 | } 51 | 52 | descending(Comparable Function(T) expression) { 53 | _list.add( 54 | SortExpression() 55 | ..sortDirection = SortDirection.Descending 56 | ..expression = (t) { 57 | var tt = expression(t); 58 | return tt; 59 | }, 60 | ); 61 | } 62 | 63 | thenByDescending(Comparable Function(T) expression) { 64 | _list.add( 65 | SortExpression() 66 | ..sortDirection = SortDirection.Descending 67 | ..expression = (t) { 68 | var tt = expression(t); 69 | return tt; 70 | }, 71 | ); 72 | } 73 | 74 | int getCompareTo(T x, T y) { 75 | for (var item in _list) { 76 | if (x == null && y == null) { 77 | continue; 78 | } 79 | 80 | if (x == null) { 81 | return -1; 82 | } 83 | 84 | if (y == null) { 85 | return 1; 86 | } 87 | 88 | var xItem = item.expression(x); 89 | var yItem = item.expression(y); 90 | if (xItem == null && yItem == null) { 91 | continue; 92 | } 93 | 94 | if (xItem == null) { 95 | return -1; 96 | } 97 | 98 | if (yItem == null) { 99 | return 1; 100 | } 101 | 102 | var result = xItem.compareTo(yItem); 103 | if (result == 0) { 104 | continue; 105 | } 106 | 107 | return item.sortDirection == SortDirection.Descending ? -result : result; 108 | } 109 | 110 | return 0; 111 | } 112 | 113 | @override 114 | List> get delegate => _list; 115 | } 116 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_theme/quran_settings_theme_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/base_store.dart'; 4 | import 'package:quran_app/baselib/command.dart'; 5 | import 'package:quran_app/baselib/localization_service.dart'; 6 | import 'package:quran_app/baselib/widgets.dart'; 7 | import 'package:quran_app/services/quran_provider.dart'; 8 | import 'package:quran_app/services/theme_provider.dart'; 9 | import 'package:rxdart/rxdart.dart'; 10 | 11 | import '../../main.dart'; 12 | import '../quran_settings/quran_settings_store.dart'; 13 | 14 | part 'quran_settings_theme_store.g.dart'; 15 | 16 | class QuranSettingsThemeStore = _QuranSettingsThemeStore 17 | with _$QuranSettingsThemeStore; 18 | 19 | abstract class _QuranSettingsThemeStore extends BaseStore 20 | with Store 21 | implements QuranSettingsStoreProvider { 22 | var localization = sl.get(); 23 | var quranProvider = sl.get(); 24 | var themeProvider = sl.get(); 25 | 26 | _QuranSettingsThemeStore({ 27 | QuranProvider quranProvider, 28 | ThemeProviderImplementation themeProvider, 29 | }) { 30 | this.quranProvider = quranProvider ?? (quranProvider = this.quranProvider); 31 | this.themeProvider = themeProvider ?? (themeProvider = this.themeProvider); 32 | 33 | _settingsItems = SettingsItem(); 34 | 35 | getThemes = Command(() async { 36 | dataState$.add(DataState( 37 | enumSelector: EnumSelector.loading, 38 | )); 39 | 40 | var themes = await themeProvider.getThemes(); 41 | themes$.add(themes); 42 | 43 | var currentTheme = await themeProvider.getCurrentTheme(); 44 | currentTheme = themes.firstWhere( 45 | (t) => t == currentTheme, 46 | orElse: () => themes.first, 47 | ); 48 | currentTheme$.add(currentTheme); 49 | 50 | dataState$.add((DataState( 51 | enumSelector: EnumSelector.success, 52 | ))); 53 | }); 54 | 55 | var ds = CompositeSubscription(); 56 | 57 | ds.add(currentThemeChanged$.asyncExpand((f) { 58 | return themeProvider.setTheme(f).asStream().map((_) => f); 59 | }).asyncExpand((f) { 60 | currentTheme$.add(f); 61 | return currentTheme$.take(1).last.asStream(); 62 | }).listen(null)); 63 | 64 | registerDispose(() { 65 | ds.dispose(); 66 | ds = null; 67 | }); 68 | } 69 | 70 | SettingsItem _settingsItems; 71 | @override 72 | SettingsItem get settingsItem => _settingsItems; 73 | 74 | BehaviorSubject dataState$ = BehaviorSubject.seeded(DataState( 75 | enumSelector: EnumSelector.none, 76 | )); 77 | 78 | final themes$ = BehaviorSubject>.seeded( 79 | [], 80 | sync: true, 81 | ); 82 | 83 | final currentTheme$ = BehaviorSubject( 84 | sync: true, 85 | ); 86 | 87 | final currentThemeChanged$ = BehaviorSubject( 88 | sync: true, 89 | ); 90 | 91 | Command getThemes; 92 | } 93 | -------------------------------------------------------------------------------- /lib/pages/quran_settings/quran_settings_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:mobx/mobx.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/localization_service.dart'; 6 | import 'package:quran_app/pages/quran_settings_app/quran_settings_app_store.dart'; 7 | import 'package:quran_app/pages/quran_settings_app/quran_settings_app_widget.dart'; 8 | import 'package:quran_app/pages/quran_settings_fontsizes/quran_settings_fontsizes_store.dart'; 9 | import 'package:quran_app/pages/quran_settings_fontsizes/quran_settings_fontsizes_widget.dart'; 10 | import 'package:quran_app/pages/quran_settings_theme/quran_settings_theme_store.dart'; 11 | import 'package:quran_app/pages/quran_settings_theme/quran_settings_theme_widget.dart'; 12 | import 'package:quran_app/pages/quran_settings_translations/quran_settings_translations_widget.dart'; 13 | import 'package:tuple/tuple.dart'; 14 | 15 | import '../../main.dart'; 16 | import '../quran_settings_translations/quran_settings_translations_store.dart'; 17 | 18 | part 'quran_settings_store.g.dart'; 19 | 20 | class SettingsItem { 21 | String name; 22 | } 23 | 24 | abstract class QuranSettingsStoreProvider { 25 | SettingsItem get settingsItem; 26 | } 27 | 28 | class QuranSettingsStore = _QuranSettingsStore with _$QuranSettingsStore; 29 | 30 | abstract class _QuranSettingsStore extends BaseStore with Store { 31 | var localization = sl.get(); 32 | 33 | _QuranSettingsStore({ 34 | @required Map parameter, 35 | ILocalizationService localizationService, 36 | }) { 37 | this.localization = 38 | localizationService ?? (localizationService = this.localization); 39 | { 40 | { 41 | final store = QuranSettingsAppStore(); 42 | items.add( 43 | Tuple2( 44 | QuranSettingsAppWidget( 45 | store: store, 46 | ), 47 | store, 48 | ), 49 | ); 50 | } 51 | { 52 | final store = QuranSettingsTranslationsStore( 53 | parameter: parameter, 54 | ); 55 | items.add( 56 | Tuple2( 57 | QuranSettingsTranslationsWidget( 58 | store: store, 59 | ), 60 | store, 61 | ), 62 | ); 63 | } 64 | { 65 | final store = QuranSettingsFontsizesStore( 66 | parameter: parameter, 67 | ); 68 | items.add( 69 | Tuple2( 70 | QuranSettingsFontSizesWidget( 71 | store: store, 72 | ), 73 | store, 74 | ), 75 | ); 76 | } 77 | { 78 | final store = QuranSettingsThemeStore(); 79 | items.add( 80 | Tuple2( 81 | QuranSettingsThemeWidget( 82 | store: store, 83 | ), 84 | store, 85 | ), 86 | ); 87 | } 88 | } 89 | } 90 | 91 | List> items = []; 92 | } 93 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/pages/quran_navigator/quran_navigator_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:mobx/mobx.dart'; 3 | import 'package:quran_app/baselib/app_services.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/command.dart'; 6 | import 'package:quran_app/baselib/localization_service.dart'; 7 | import 'package:quran_app/models/models.dart'; 8 | import 'package:quran_app/services/quran_provider.dart'; 9 | import 'package:rxdart/rxdart.dart'; 10 | 11 | import '../../main.dart'; 12 | 13 | part 'quran_navigator_store.g.dart'; 14 | 15 | class QuranNavigatorStore = _QuranNavigatorStore with _$QuranNavigatorStore; 16 | 17 | abstract class _QuranNavigatorStore extends BaseStore with Store { 18 | var appServices = sl.get(); 19 | var quranProvider = sl.get(); 20 | var localization = sl.get(); 21 | 22 | _QuranNavigatorStore({ 23 | Map parameter, 24 | AppServices appServices, 25 | ILocalizationService localizationService, 26 | }) { 27 | this.quranProvider = quranProvider ?? (quranProvider = this.quranProvider); 28 | this.appServices = appServices ?? (appServices = this.appServices); 29 | this.localization = 30 | localizationService ?? (localizationService = this.localization); 31 | 32 | var _chapters = parameter['chapters']; 33 | chapters.clear(); 34 | chapters.addAll(_chapters); 35 | 36 | initialSelectedChapter = chapters.firstWhere( 37 | (t) => t == parameter['selectedChapter'], 38 | orElse: () => null, 39 | ); 40 | selectedChapter$.add(initialSelectedChapter); 41 | 42 | selectedAya = parameter['selectedAya']; 43 | 44 | { 45 | var d = selectedChapter$.listen((v) { 46 | var _listAya = List.generate(v.versesCount, (v) { 47 | return ++v; 48 | }); 49 | listAya.clear(); 50 | listAya.addAll(_listAya); 51 | 52 | var initialSelectedAya = listAya.firstWhere((t) { 53 | return t == selectedAya; 54 | }, orElse: () => 1); 55 | initialSelectedaya$.add(initialSelectedAya); 56 | selectedAya = initialSelectedAya; 57 | }); 58 | registerDispose(() { 59 | d.cancel(); 60 | }); 61 | } 62 | 63 | pickSura = Command(() { 64 | appServices.navigatorState.pop({ 65 | 'chapter': selectedChapter$.value, 66 | 'aya': 1, 67 | }); 68 | return Future.value(); 69 | }); 70 | 71 | pickAya = Command(() { 72 | appServices.navigatorState.pop({ 73 | 'chapter': selectedChapter$.value, 74 | 'aya': selectedAya, 75 | }); 76 | return Future.value(); 77 | }); 78 | 79 | registerDispose(() { 80 | selectedChapter$.close(); 81 | initialSelectedaya$.close(); 82 | }); 83 | } 84 | 85 | @observable 86 | ObservableList chapters = ObservableList(); 87 | 88 | @observable 89 | Chapters initialSelectedChapter; 90 | 91 | BehaviorSubject selectedChapter$ = BehaviorSubject( 92 | sync: true, 93 | ); 94 | 95 | @observable 96 | ObservableList listAya = ObservableList(); 97 | 98 | BehaviorSubject initialSelectedaya$ = BehaviorSubject( 99 | sync: true, 100 | ); 101 | 102 | @observable 103 | int selectedAya; 104 | 105 | Command pickSura; 106 | 107 | Command pickAya; 108 | } 109 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 71 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_translations/quran_settings_translation_item_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:quran_app/baselib/app_services.dart'; 3 | import 'package:quran_app/baselib/command.dart'; 4 | import 'package:quran_app/baselib/disposable.dart'; 5 | import 'package:quran_app/models/translation_data.dart'; 6 | import 'package:quran_app/services/quran_translation_file_provider.dart'; 7 | import 'package:rx_command/rx_command.dart'; 8 | import 'package:rxdart/rxdart.dart'; 9 | 10 | import '../../main.dart'; 11 | 12 | class QuranSettingsTranslationItemStore implements Disposable { 13 | var appServices = sl.get(); 14 | var quranTranslationFileProvider = sl.get(); 15 | 16 | List disposeFunction = []; 17 | 18 | QuranSettingsTranslationItemStore( 19 | TranslationData translationData, { 20 | AppServices appServices, 21 | QuranTranslationFileProvider quranTranslationFileProvider, 22 | }) { 23 | this.translationData = translationData; 24 | 25 | this.appServices = appServices ?? (appServices = this.appServices); 26 | this.quranTranslationFileProvider = quranTranslationFileProvider ?? 27 | (quranTranslationFileProvider = this.quranTranslationFileProvider); 28 | 29 | checkTranslationFile = RxCommand.createAsyncNoParamNoResult(() async { 30 | try { 31 | var exists = await quranTranslationFileProvider.translationFileExists( 32 | translationData.tableName, 33 | ); 34 | appServices.logger.i('Translation file exists: $exists'); 35 | translationFileExists.add(exists); 36 | } catch (e) { 37 | appServices.logger.i(e); 38 | } 39 | }); 40 | 41 | { 42 | var dataFile = quranTranslationFileProvider.getDataFileById( 43 | translationData.id, 44 | ); 45 | if (dataFile != null) { 46 | var d = dataFile.onChangeStatus.doOnData((v) { 47 | onChangeStatus.add(v); 48 | }).listen(null); 49 | disposeFunction.add(() { 50 | d.cancel(); 51 | }); 52 | } 53 | } 54 | 55 | downloadTranslation = Command.parameter((v) async { 56 | onChangeStatus.add( 57 | QueueStatusModel()..queueStatus = QueueStatus.downloading, 58 | ); 59 | 60 | var dataFile = quranTranslationFileProvider.queueDownload( 61 | DataFile() 62 | ..id = translationData.id 63 | ..url = translationData.uri 64 | ..tableName = translationData.tableName, 65 | ); 66 | var queueStatus = await dataFile.onChangeStatus.skip(1).take(1).first; 67 | onChangeStatus.add(queueStatus); 68 | translationData.isSelected$.add( 69 | translationData.isSelected$.value ?? false, 70 | ); 71 | checkTranslationFile.execute(); 72 | await checkTranslationFile.next; 73 | }); 74 | 75 | removeTranslation = Command.parameter((t) async { 76 | await quranTranslationFileProvider.removeTranslation(t.tableName); 77 | checkTranslationFile.execute(); 78 | await checkTranslationFile.next; 79 | translationData.isSelected$.add(false); 80 | }); 81 | } 82 | 83 | TranslationData translationData; 84 | 85 | RxCommand checkTranslationFile; 86 | 87 | final translationFileExists = BehaviorSubject.seeded(false); 88 | 89 | Command downloadTranslation; 90 | 91 | var onChangeStatus = BehaviorSubject.seeded( 92 | QueueStatusModel()..queueStatus = QueueStatus.notDownloaded, 93 | ); 94 | 95 | bool operator ==(o) => 96 | o is QuranSettingsTranslationItemStore && 97 | o.translationData == translationData; 98 | 99 | @override 100 | int get hashCode => translationData.hashCode; 101 | 102 | @override 103 | void dispose() { 104 | disposeFunction.forEach((v) { 105 | v(); 106 | }); 107 | } 108 | 109 | Command removeTranslation; 110 | } 111 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:quran_app/baselib/base_widgetparameter_mixin.dart'; 5 | import 'package:quran_app/pages/main/main_store.dart'; 6 | import 'package:quran_app/routes/routes.dart'; 7 | import 'package:quran_app/services/quran_translation_file_provider.dart'; 8 | import 'package:quran_app/services/theme_provider.dart'; 9 | import 'package:rx_shared_preferences/rx_shared_preferences.dart'; 10 | 11 | import 'baselib/app_services.dart'; 12 | import 'baselib/localization_service.dart'; 13 | import 'baselib/service_locator.dart'; 14 | import 'pages/main/main_widget.dart'; 15 | import 'services/appdb.dart'; 16 | import 'services/bookmarks_provider.dart'; 17 | import 'services/quran_provider.dart'; 18 | import 'services/sqlite_quran_provider.dart'; 19 | 20 | var sl = ServiceLocator(); 21 | 22 | RxSharedPreferences get rxPrefs => RxSharedPreferences.getInstance(); 23 | 24 | void registerInjector() { 25 | sl.registerLazySingleton(() { 26 | return LocalizationService(); 27 | }); 28 | sl.registerLazySingleton(() { 29 | return AppServicesImplementation(); 30 | }); 31 | sl.registerBuilder(() { 32 | return PlatformAssetBundle(); 33 | }); 34 | // sl.registerLazySingleton(() { 35 | // return XmlQuranProvider(); 36 | // }); 37 | sl.registerLazySingleton(() { 38 | return SqliteQuranProvider(); 39 | }); 40 | sl.registerLazySingleton(() { 41 | return ThemeProviderImplementation(); 42 | }); 43 | sl.registerLazySingleton(() { 44 | return AppDb(); 45 | }); 46 | // sl.registerLazySingleton(() { 47 | // return QuranDb(); 48 | // }); 49 | // sl.registerLazySingleton(() { 50 | // return TranslationDb(); 51 | // }); 52 | sl.registerLazySingleton(() { 53 | return SqliteBookmarksProvider(); 54 | }); 55 | sl.registerLazySingleton(() { 56 | return QuranTranslationFileProviderImplementation(); 57 | }); 58 | } 59 | 60 | void main() { 61 | registerInjector(); 62 | 63 | runApp( 64 | MainApp(), 65 | ); 66 | } 67 | 68 | class MainApp extends StatelessWidget { 69 | final store = MainStore(); 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | final themeMapping = { 74 | ThemeItem( 75 | themeType: ThemeType.Light, 76 | ): ThemeData( 77 | primarySwatch: Colors.blue, 78 | ), 79 | ThemeItem( 80 | themeType: ThemeType.Night, 81 | ): ThemeData.dark() 82 | }; 83 | return StreamBuilder( 84 | initialData: store.currentTheme$.valueOrNull, 85 | stream: store.currentTheme$, 86 | builder: ( 87 | BuildContext context, 88 | AsyncSnapshot snapshot, 89 | ) { 90 | var themeData = themeMapping[snapshot.data] ?? 91 | ThemeData( 92 | primarySwatch: Colors.blue, 93 | ); 94 | 95 | return MaterialApp( 96 | title: 'Quran App', 97 | theme: themeData, 98 | onGenerateRoute: (s) { 99 | var widgetBuilder = Routes.routes[s.name]; 100 | return MaterialPageRoute( 101 | builder: (BuildContext context) { 102 | var widget = widgetBuilder(context); 103 | if (widget is BaseWidgetParameterMixin) { 104 | var baseWidgetParameterMixin = widget; 105 | if (s.arguments != null) { 106 | baseWidgetParameterMixin.parameter.addAll( 107 | Map.from( 108 | s.arguments, 109 | ), 110 | ); 111 | } 112 | } 113 | return widget; 114 | }, 115 | ); 116 | }, 117 | navigatorKey: store.appServices.navigatorStateKey, 118 | ); 119 | }, 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: quran_app 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.1.0 15 | 16 | environment: 17 | sdk: ">=2.12.0 <3.0.0" 18 | flutter: 2.2.1 19 | 20 | dependencies: 21 | flutter: 22 | sdk: flutter 23 | 24 | # The following adds the Cupertino Icons font to your application. 25 | # Use with the CupertinoIcons class for iOS style icons. 26 | cupertino_icons: ^0.1.2 27 | logger: ^0.7.0+2 28 | rxdart: ^0.27.1 29 | rx_command: ^6.0.1 30 | mobx: ^2.0.4 31 | flutter_mobx: ^2.0.2 32 | material_design_icons_flutter: ^3.0.3289 33 | intl: ^0.16.0 34 | font_awesome_flutter: ^8.2.0 35 | bubble_tab_indicator: ^0.1.4 36 | built_collection: ^5.1.0 37 | built_value: ^8.1.2 38 | shimmer: ^1.0.1 39 | scrollable_positioned_list: ^0.1.10 40 | xml2json: ^5.3.1 41 | tuple: ^2.0.0 42 | rx_shared_preferences: ^2.0.7 43 | expandable: ^4.1.2 44 | sqflite: ^1.2.1 45 | path_provider: ^2.0.2 46 | json_annotation: ^4.1.0 47 | moor: ^4.5.0 48 | sqlite3_flutter_libs: ^0.5.0 49 | animator: ^1.0.0+5 50 | draggable_scrollbar: ^0.0.4 51 | yaml: ^3.1.0 52 | dio: ^4.0.0 53 | flutter_launcher_icons: ^0.9.2 54 | 55 | dev_dependencies: 56 | flutter_test: 57 | sdk: flutter 58 | build_runner: ^2.1.1 59 | mobx_codegen: ^2.0.3 60 | built_value_generator: ^8.1.2 61 | json_serializable: ^5.0.0 62 | moor_generator: ^4.5.1 63 | 64 | # Remove this after upgrade to latest flutter version (maybe 2.4.x?) 65 | dependency_overrides: 66 | meta: ^1.3.0 67 | 68 | # For information on the generic Dart part of this file, see the 69 | # following page: https://dart.dev/tools/pub/pubspec 70 | 71 | # The following section is specific to Flutter. 72 | flutter: 73 | # The following line ensures that the Material Icons font is 74 | # included with your application, so that you can use the icons in 75 | # the material Icons class. 76 | uses-material-design: true 77 | # To add assets to your application, add an assets section, like this: 78 | # assets: 79 | # - images/a_dot_burr.jpeg 80 | # - images/a_dot_ham.jpeg 81 | assets: 82 | - assets/quran-data/chapters/ 83 | - assets/quran-data/chapters/ 84 | - assets/quran-data/translations/ 85 | - assets/quran-data/ 86 | - assets/i18n/ 87 | - assets/ 88 | 89 | # An image asset can refer to one or more resolution-specific "variants", see 90 | # https://flutter.dev/assets-and-images/#resolution-aware. 91 | # For details regarding adding assets from package dependencies, see 92 | # https://flutter.dev/assets-and-images/#from-packages 93 | # To add custom fonts to your application, add a fonts section here, 94 | # in this "flutter" section. Each entry in this list should have a 95 | # "family" key with the font family name, and a "fonts" key with a 96 | # list giving the asset and other descriptors for the font. For 97 | # example: 98 | fonts: 99 | - family: noorehira 100 | fonts: 101 | - asset: fonts/UthmanTN1-Ver10.otf 102 | - family: KFGQPC Uthman Taha Naskh 103 | fonts: 104 | - asset: fonts/UthmanTN1-Ver10.otf 105 | # 106 | # For details regarding fonts from package dependencies, 107 | # see https://flutter.dev/custom-fonts/#from-packages 108 | 109 | flutter_icons: 110 | android: true 111 | ios: true 112 | image_path: "assets/images/quran-solid.png" 113 | -------------------------------------------------------------------------------- /lib/baselib/localization_service.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/services.dart'; 3 | import 'package:intl/locale.dart'; 4 | import 'package:path/path.dart' as p; 5 | import 'package:rxdart/rxdart.dart'; 6 | import 'package:yaml/yaml.dart'; 7 | import 'package:rx_shared_preferences/rx_shared_preferences.dart'; 8 | 9 | import '../main.dart'; 10 | 11 | abstract class ILocalizationService { 12 | Locale get locale; 13 | 14 | Locale get neutralLocale; 15 | 16 | Future loadFromBundle(Locale locale); 17 | 18 | String getByKey(String key); 19 | 20 | List getSupportedLanguages(); 21 | 22 | Future saveLanguage(LanguageModel language); 23 | 24 | Future getSavedLanguage(); 25 | 26 | Stream get onLanguageChanged; 27 | } 28 | 29 | class LocalizationService extends ILocalizationService { 30 | var _assetBundle = sl.get(); 31 | 32 | LocalizationService({ 33 | AssetBundle assetBundle, 34 | }) { 35 | _assetBundle = assetBundle ?? _assetBundle; 36 | } 37 | 38 | Locale _locale; 39 | Locale get locale => _locale; 40 | 41 | Map _localeResources = {}; 42 | Map _defaultResources = {}; 43 | 44 | Future saveLanguage(LanguageModel language) async { 45 | return await rxPrefs 46 | .setString('app_locale', language.locale.toLanguageTag()) 47 | .then( 48 | (v) { 49 | _onLanguageChanged.add(language); 50 | return Future.value(true); 51 | }, 52 | ); 53 | } 54 | 55 | Future getSavedLanguage() async { 56 | var languageTag = await rxPrefs.getString('app_locale'); 57 | var languages = getSupportedLanguages(); 58 | var saved = languages.firstWhere( 59 | (t) => t.locale.toLanguageTag() == languageTag, 60 | orElse: () => languages.firstWhere( 61 | (t) => t.locale.toLanguageTag() == 'en-US', 62 | ), 63 | ); 64 | return saved; 65 | } 66 | 67 | final _onLanguageChanged = PublishSubject(); 68 | Stream get onLanguageChanged => _onLanguageChanged 69 | .asyncExpand( 70 | (v) => Rx.defer(() { 71 | return loadFromBundle(v.locale).asStream().map((_) => v); 72 | }), 73 | ) 74 | .asBroadcastStream(); 75 | 76 | Future loadFromBundle(Locale l) async { 77 | _locale = l; 78 | 79 | { 80 | _defaultResources.clear(); 81 | var defaultResourcePath = p.join('assets', 'i18n', 'default.yaml'); 82 | var raw = await _assetBundle.loadString( 83 | defaultResourcePath, 84 | ); 85 | YamlMap r = loadYaml(raw); 86 | _defaultResources.addAll(Map.from(r)); 87 | } 88 | 89 | { 90 | try { 91 | _localeResources.clear(); 92 | 93 | var defaultResourcePath = p.join( 94 | 'assets', 95 | 'i18n', 96 | '${l.toLanguageTag().replaceAll('-', '_')}.yaml', 97 | ); 98 | var raw = await _assetBundle.loadString( 99 | defaultResourcePath, 100 | ); 101 | YamlMap r = loadYaml(raw); 102 | _localeResources.addAll(Map.from(r)); 103 | } catch (e) { 104 | print(e); 105 | } 106 | } 107 | } 108 | 109 | String getByKey(String key) { 110 | return _localeResources[key] ?? _defaultResources[key] ?? '-'; 111 | } 112 | 113 | var _neutralLocale = Locale.parse('en-US'); 114 | @override 115 | Locale get neutralLocale => _neutralLocale; 116 | 117 | List getSupportedLanguages() { 118 | return [ 119 | LanguageModel() 120 | ..locale = Locale.parse('en-US') 121 | ..name = 'English (United States)', 122 | LanguageModel() 123 | ..locale = Locale.parse('id-ID') 124 | ..name = 'Indonesian', 125 | ]; 126 | } 127 | } 128 | 129 | class LanguageModel { 130 | Locale locale; 131 | 132 | String name; 133 | 134 | @override 135 | bool operator ==(Object other) { 136 | if (identical(this, other)) return true; 137 | return other is LanguageModel && locale == other.locale; 138 | } 139 | 140 | @override 141 | int get hashCode { 142 | return locale.hashCode; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /lib/pages/home_tab_juz/home_tab_juz_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/services.dart'; 3 | import 'package:mobx/mobx.dart'; 4 | import 'package:quran_app/baselib/app_services.dart'; 5 | import 'package:quran_app/baselib/base_store.dart'; 6 | import 'package:quran_app/baselib/command.dart'; 7 | import 'package:quran_app/baselib/localization_service.dart'; 8 | import 'package:quran_app/baselib/widgets.dart'; 9 | import 'package:quran_app/models/models.dart'; 10 | import 'package:path/path.dart'; 11 | import 'package:quran_app/services/quran_provider.dart'; 12 | import 'package:rxdart/rxdart.dart'; 13 | 14 | import '../../main.dart'; 15 | 16 | part 'home_tab_juz_store.g.dart'; 17 | 18 | class HomeTabJuzStore = _HomeTabJuzStore with _$HomeTabJuzStore; 19 | 20 | abstract class _HomeTabJuzStore extends BaseStore with Store { 21 | var assetBundle = sl.get(); 22 | var quranProvider = sl.get(); 23 | var localization = sl.get(); 24 | var appServices = sl.get(); 25 | 26 | _HomeTabJuzStore({ 27 | AssetBundle assetBundle, 28 | QuranProvider quranProvider, 29 | ILocalizationService localizationService, 30 | AppServices appServices, 31 | }) { 32 | this.assetBundle = assetBundle ?? (assetBundle = this.assetBundle); 33 | this.quranProvider = quranProvider ?? (quranProvider = this.quranProvider); 34 | this.localization = 35 | localizationService ?? (localizationService = this.localization); 36 | this.appServices = appServices ?? (appServices = this.appServices); 37 | 38 | getListJuz = Command(() async { 39 | state$.add(DataState(enumSelector: EnumSelector.loading)); 40 | 41 | var path = join('assets', 'quran-data', 'juz.json'); 42 | var raw = await assetBundle.loadString(path); 43 | var rootJuzItem = RootJuzItem.fromJson(raw); 44 | var chapters = 45 | await quranProvider.getChapters(localization.neutralLocale); 46 | // var _listJuz = rootJuzItem.juzs.map((f) { 47 | 48 | // }).toList(); 49 | // listJuz.clear(); 50 | // listJuz.addAll(_listJuz); 51 | var _listJuz = await Stream.fromIterable( 52 | rootJuzItem.juzs.toList(), 53 | ).asyncExpand((f) { 54 | return DeferStream(() { 55 | var verseMapping = JuzItem.getVerseMappingJuzItem(f.verseMapping); 56 | var chapter = chapters.firstWhere( 57 | (t) => t.chapterNumber == verseMapping.first.surah, 58 | ); 59 | var b = f.toBuilder(); 60 | b.update((v) { 61 | v.chapters.replace(chapter); 62 | }); 63 | return Stream.value(b); 64 | }); 65 | }).asyncExpand((b) { 66 | return DeferStream(() { 67 | return quranProvider 68 | .getListQuranTextData() 69 | .asStream() 70 | .map((t) => t[0]) 71 | .asyncExpand((t) { 72 | return DeferStream(() { 73 | var f = Future(() async { 74 | var verseMapping = 75 | JuzItem.getVerseMappingJuzItem(b.verseMapping.build()); 76 | var first = verseMapping.first; 77 | var aya = await quranProvider.getAya( 78 | first.surah, 79 | first.startAya, 80 | t, 81 | ); 82 | b.listAya.replace([aya]); 83 | return b.build(); 84 | }); 85 | return f.asStream(); 86 | }); 87 | }); 88 | }); 89 | }).toList(); 90 | listJuz.clear(); 91 | listJuz.addAll(_listJuz); 92 | 93 | state$.add(DataState(enumSelector: EnumSelector.success)); 94 | }); 95 | 96 | juzItemTapped = Command.parameter((JuzItem v) { 97 | return appServices.navigatorState.pushNamed( 98 | '/quran', 99 | arguments: { 100 | 'chapter': v.chapters, 101 | 'aya': JuzItem.getVerseMappingJuzItem(v.verseMapping).first.startAya, 102 | }, 103 | ); 104 | }); 105 | } 106 | 107 | var state$ = BehaviorSubject.seeded( 108 | DataState( 109 | enumSelector: EnumSelector.none, 110 | ), 111 | ); 112 | 113 | final listJuz = ObservableList(); 114 | 115 | Command getListJuz; 116 | 117 | Command juzItemTapped; 118 | } 119 | -------------------------------------------------------------------------------- /lib/services/quran_provider.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | import 'package:built_collection/built_collection.dart'; 6 | import 'package:built_value/built_value.dart'; 7 | import 'package:built_value/serializer.dart'; 8 | import 'package:intl/locale.dart'; 9 | import 'package:quran_app/baselib/app_services.dart'; 10 | import 'package:quran_app/models/models.dart'; 11 | import 'package:path/path.dart' as p; 12 | import 'package:quran_app/models/translation_data.dart'; 13 | 14 | part 'quran_provider.g.dart'; 15 | 16 | abstract class Sura implements Built { 17 | Sura._(); 18 | 19 | factory Sura([updates(SuraBuilder b)]) = _$Sura; 20 | 21 | @BuiltValueField(wireName: 'index') 22 | String get index; 23 | @BuiltValueField(wireName: 'name') 24 | String get name; 25 | @BuiltValueField(wireName: 'aya') 26 | BuiltList get aya; 27 | String toJson() { 28 | return json.encode(serializers.serializeWith(Sura.serializer, this)); 29 | } 30 | 31 | static Sura fromJson(String jsonString) { 32 | return serializers.deserializeWith( 33 | Sura.serializer, json.decode(jsonString)); 34 | } 35 | 36 | static Serializer get serializer => _$suraSerializer; 37 | } 38 | 39 | abstract class Aya implements Built { 40 | Aya._(); 41 | 42 | factory Aya([updates(AyaBuilder b)]) = _$Aya; 43 | 44 | @BuiltValueField(wireName: 'index') 45 | String get indexString; 46 | 47 | @BuiltValueField(serialize: false) 48 | int get index => int.parse(indexString); 49 | 50 | @BuiltValueField(wireName: 'text') 51 | String get text; 52 | 53 | @nullable 54 | @BuiltValueField(serialize: false) 55 | TranslationData get translationData; 56 | 57 | String toJson() { 58 | return json.encode(serializers..serializeWith(Aya.serializer, this)); 59 | } 60 | 61 | static Aya fromJson(String jsonString) { 62 | return serializers.deserializeWith(Aya.serializer, json.decode(jsonString)); 63 | } 64 | 65 | static Serializer get serializer => _$ayaSerializer; 66 | 67 | @nullable 68 | @BuiltValueField(serialize: false) 69 | BuiltList get translations; 70 | } 71 | 72 | abstract class AyaTranslation 73 | implements Built { 74 | AyaTranslation._(); 75 | 76 | factory AyaTranslation([updates(AyaTranslationBuilder b)]) = _$AyaTranslation; 77 | 78 | @BuiltValueField(wireName: 'languageCode') 79 | String get languageCode; 80 | @BuiltValueField(wireName: 'translation') 81 | String get translation; 82 | String toJson() { 83 | return json 84 | .encode(serializers.serializeWith(AyaTranslation.serializer, this)); 85 | } 86 | 87 | static AyaTranslation fromJson(String jsonString) { 88 | return serializers.deserializeWith( 89 | AyaTranslation.serializer, json.decode(jsonString)); 90 | } 91 | 92 | static Serializer get serializer => 93 | _$ayaTranslationSerializer; 94 | } 95 | 96 | abstract class QuranProvider { 97 | Future> getListQuranTextData(); 98 | 99 | Future> getListTranslations(); 100 | 101 | Future initialize(); 102 | 103 | Future> getChapters(Locale locale); 104 | 105 | Future> getAyaByChapter( 106 | int chapter, 107 | QuranTextData quranTextData, [ 108 | List translations, 109 | ]); 110 | 111 | Future getAya( 112 | int chapter, 113 | int aya, 114 | QuranTextData quranTextData, [ 115 | List translations, 116 | ]); 117 | 118 | Future> getTranslations( 119 | int chapter, 120 | TranslationData translationData, 121 | ); 122 | 123 | Future getTranslation( 124 | int chapter, 125 | int aya, 126 | TranslationData translationData, 127 | ); 128 | 129 | Future isTableExists(String tableName); 130 | 131 | void dispose(); 132 | } 133 | 134 | Directory getQuranFolder(AppServices appServices) { 135 | var appDocDir = appServices.applicationDocumentsDirectory; 136 | var quranFolder = Directory(p.join(appDocDir, 'q')); 137 | return quranFolder; 138 | } 139 | 140 | class QuranTextData { 141 | String id; 142 | 143 | String name; 144 | 145 | String tableName; 146 | 147 | bool operator ==(o) => o is QuranTextData && id == o.id; 148 | 149 | @override 150 | int get hashCode => id.hashCode; 151 | } 152 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_translations/quran_settings_translations_store.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:mobx/mobx.dart'; 4 | import 'package:quran_app/baselib/base_store.dart'; 5 | import 'package:quran_app/baselib/command.dart'; 6 | import 'package:quran_app/baselib/disposable.dart'; 7 | import 'package:quran_app/baselib/localization_service.dart'; 8 | import 'package:quran_app/baselib/widgets.dart'; 9 | import 'package:quran_app/helpers/sort_comparison_builder.dart'; 10 | import 'package:quran_app/models/translation_data.dart'; 11 | import 'package:quran_app/services/quran_provider.dart'; 12 | import 'package:rxdart/rxdart.dart'; 13 | import 'package:tuple/tuple.dart'; 14 | import 'package:rx_shared_preferences/rx_shared_preferences.dart'; 15 | 16 | import '../../main.dart'; 17 | import '../quran_settings/quran_settings_store.dart'; 18 | import '../../models/setting_ids.dart'; 19 | import 'quran_settings_translation_item_store.dart'; 20 | 21 | part 'quran_settings_translations_store.g.dart'; 22 | 23 | class QuranSettingsTranslationsStore = _QuranSettingsTranslationsStore 24 | with _$QuranSettingsTranslationsStore; 25 | 26 | abstract class _QuranSettingsTranslationsStore extends BaseStore 27 | with Store 28 | implements QuranSettingsStoreProvider { 29 | var localization = sl.get(); 30 | var quranProvider = sl.get(); 31 | 32 | _QuranSettingsTranslationsStore({ 33 | @required Map parameter, 34 | QuranProvider quranProvider, 35 | }) { 36 | this.quranProvider = quranProvider ?? (quranProvider = this.quranProvider); 37 | 38 | _settingsItems = SettingsItem() 39 | ..name = localization.getByKey( 40 | 'quran_settings_translations.translations', 41 | ); 42 | 43 | void disposeTraslations() { 44 | translations.forEach((f) { 45 | if (f is Disposable) { 46 | f.dispose(); 47 | } 48 | }); 49 | } 50 | 51 | var comparisoBuilder = 52 | SortComparisonBuilder(); 53 | comparisoBuilder 54 | ..ascending((t) => t.translationData.type.index) 55 | ..thenByAscending((t) => t.translationData.language) 56 | ..thenByAscending((t) => t.translationData.name) 57 | ..thenByAscending((t) => t.translationData.translator); 58 | getListTranslations = Command(() async { 59 | try { 60 | dataState$.add(DataState(enumSelector: EnumSelector.loading)); 61 | 62 | if (parameter[QuranSettingsTranslationsStore] != null) { 63 | ObservableList fetchedTranslations = 64 | parameter[QuranSettingsTranslationsStore]; 65 | var _translations = 66 | await Stream.fromIterable(fetchedTranslations).asyncExpand((v) { 67 | var item = QuranSettingsTranslationItemStore(v); 68 | return Stream.value(item); 69 | }).toList(); 70 | _translations.sort((x, y) => comparisoBuilder.getCompareTo(x, y)); 71 | disposeTraslations(); 72 | translations.clear(); 73 | translations.addAll(_translations); 74 | } 75 | } finally { 76 | dataState$.add(DataState(enumSelector: EnumSelector.success)); 77 | } 78 | }); 79 | 80 | translationChanged = Command.parameter((v) async { 81 | try { 82 | dataState$.add(DataState(enumSelector: EnumSelector.loading)); 83 | 84 | v.item1.translationData.isSelected$.add(v.item2); 85 | 86 | var selectedTranslations = translations.where((t) { 87 | return t.translationData.isSelected$.value == true; 88 | }).map((f) { 89 | return f.translationData.id; 90 | }).toList(); 91 | await rxPrefs.setStringList( 92 | SettingIds.translationId, 93 | selectedTranslations, 94 | ); 95 | } finally { 96 | dataState$.add(DataState(enumSelector: EnumSelector.success)); 97 | } 98 | }); 99 | 100 | registerDispose(() { 101 | disposeTraslations(); 102 | }); 103 | } 104 | 105 | SettingsItem _settingsItems; 106 | @override 107 | SettingsItem get settingsItem => _settingsItems; 108 | 109 | BehaviorSubject dataState$ = BehaviorSubject.seeded(DataState( 110 | enumSelector: EnumSelector.none, 111 | )); 112 | 113 | Command getListTranslations; 114 | 115 | @observable 116 | var translations = ObservableList(); 117 | 118 | Command> translationChanged; 119 | } 120 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_theme/quran_settings_theme_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:expandable/expandable.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:quran_app/baselib/base_state_mixin.dart'; 5 | import 'package:quran_app/services/theme_provider.dart'; 6 | import 'quran_settings_theme_store.dart'; 7 | 8 | class QuranSettingsThemeWidget extends StatefulWidget { 9 | final QuranSettingsThemeStore store; 10 | QuranSettingsThemeWidget({ 11 | @required this.store, 12 | Key key, 13 | }) : super(key: key); 14 | 15 | _QuranSettingsThemeWidgetState createState() => 16 | _QuranSettingsThemeWidgetState(); 17 | } 18 | 19 | class _QuranSettingsThemeWidgetState extends State 20 | with BaseStateMixin { 21 | @override 22 | QuranSettingsThemeStore get store => widget.store; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | // initState only called once, try to change the theme 27 | (() async { 28 | await store.getThemes.executeIf(); 29 | })(); 30 | 31 | return Container( 32 | padding: EdgeInsets.symmetric( 33 | horizontal: 10, 34 | ), 35 | child: Column( 36 | children: [ 37 | ExpandableNotifier( 38 | initialExpanded: true, 39 | child: Card( 40 | clipBehavior: Clip.antiAlias, 41 | child: Column( 42 | children: [ 43 | ExpandablePanel( 44 | theme: ExpandableThemeData( 45 | iconColor: Theme.of(context).accentColor, 46 | ), 47 | header: Padding( 48 | padding: EdgeInsets.all(10), 49 | child: Text( 50 | store.localization.getByKey( 51 | 'quran_settings_theme.title', 52 | ), 53 | style: TextStyle( 54 | fontWeight: FontWeight.bold, 55 | ), 56 | ), 57 | ), 58 | expanded: StreamBuilder>( 59 | initialData: store.themes$.valueOrNull, 60 | stream: store.themes$, 61 | builder: ( 62 | BuildContext context, 63 | AsyncSnapshot> snapshot, 64 | ) { 65 | final themes = snapshot.data; 66 | 67 | return StreamBuilder( 68 | initialData: store.currentTheme$.valueOrNull, 69 | stream: store.currentTheme$, 70 | builder: ( 71 | BuildContext context, 72 | AsyncSnapshot snapshot, 73 | ) { 74 | final currentTheme = snapshot.data; 75 | if (currentTheme == null) { 76 | return Container(); 77 | } 78 | 79 | return Container( 80 | padding: EdgeInsets.only( 81 | top: 0, 82 | left: 10, 83 | right: 10, 84 | bottom: 10, 85 | ), 86 | child: DropdownButtonHideUnderline( 87 | child: DropdownButton( 88 | value: currentTheme, 89 | isDense: true, 90 | isExpanded: true, 91 | items: themes.map((f) { 92 | return DropdownMenuItem( 93 | value: f, 94 | child: Text( 95 | f.name, 96 | ), 97 | ); 98 | }).toList(), 99 | onChanged: (value) { 100 | store.currentThemeChanged$.add(value); 101 | }, 102 | ), 103 | ), 104 | ); 105 | }, 106 | ); 107 | }, 108 | ), 109 | ), 110 | ], 111 | ), 112 | ), 113 | ), 114 | ], 115 | ), 116 | ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/pages/home_tab/home_tab_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:bubble_tab_indicator/bubble_tab_indicator.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 | import 'package:quran_app/baselib/base_state_mixin.dart'; 6 | import 'package:quran_app/pages/bookmarks/bookmarks_widget.dart'; 7 | import 'package:quran_app/pages/home_tab/home_tab_store.dart'; 8 | import 'package:quran_app/pages/home_tab_juz/home_tab_juz_widget.dart'; 9 | import 'package:quran_app/pages/home_tab_surah/home_tab_surah_widget.dart'; 10 | 11 | import 'home_tab_store.dart'; 12 | 13 | class HomeTabWidget extends StatefulWidget { 14 | HomeTabWidget({Key key}) : super(key: key); 15 | 16 | _HomeTabWidgetState createState() => _HomeTabWidgetState(); 17 | } 18 | 19 | class _HomeTabWidgetState extends State 20 | with BaseStateMixin, TickerProviderStateMixin { 21 | final _store = HomeTabStore(); 22 | @override 23 | HomeTabStore get store => _store; 24 | 25 | TabController tabController; 26 | PageController pageTabController; 27 | 28 | TabController quranTabController; 29 | PageController pageQuranTabController; 30 | final List pagesQuranTab = [ 31 | () => HomeTabSurahWidget(), 32 | () => HomeTabJuzWidget(), 33 | ]; 34 | 35 | @override 36 | void initState() { 37 | super.initState(); 38 | 39 | tabController = TabController( 40 | length: 2, 41 | vsync: this, 42 | ); 43 | pageTabController = PageController(); 44 | tabController.addListener(() { 45 | pageTabController.jumpToPage(tabController.index); 46 | }); 47 | 48 | quranTabController = TabController( 49 | length: 2, 50 | vsync: this, 51 | ); 52 | pageQuranTabController = PageController(); 53 | quranTabController.addListener(() { 54 | pageQuranTabController.jumpToPage(quranTabController.index); 55 | }); 56 | } 57 | 58 | @override 59 | void dispose() { 60 | tabController.dispose(); 61 | quranTabController.dispose(); 62 | pageTabController.dispose(); 63 | 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return DefaultTabController( 70 | length: 2, 71 | child: Scaffold( 72 | appBar: AppBar( 73 | title: Text( 74 | store.localization.getByKey('AppName'), 75 | ), 76 | bottom: TabBar( 77 | controller: tabController, 78 | tabs: [ 79 | Tab( 80 | icon: Icon(FontAwesomeIcons.quran), 81 | ), 82 | Tab( 83 | icon: Icon(FontAwesomeIcons.solidBookmark), 84 | ), 85 | ], 86 | ), 87 | ), 88 | body: PageView( 89 | controller: pageTabController, 90 | children: [ 91 | Container( 92 | child: Column( 93 | crossAxisAlignment: CrossAxisAlignment.stretch, 94 | children: [ 95 | TabBar( 96 | controller: quranTabController, 97 | indicatorSize: TabBarIndicatorSize.tab, 98 | indicator: BubbleTabIndicator( 99 | indicatorHeight: 25.0, 100 | indicatorColor: Theme.of(context).accentColor, 101 | tabBarIndicatorSize: TabBarIndicatorSize.tab, 102 | ), 103 | labelColor: 104 | Theme.of(context).accentTextTheme.headline4.color, 105 | unselectedLabelColor: 106 | Theme.of(context).textTheme.headline4.color, 107 | tabs: [ 108 | Tab( 109 | text: store.localization.getByKey('home_widget.sura'), 110 | ), 111 | Tab( 112 | text: store.localization.getByKey('home_widget.juz'), 113 | ), 114 | ], 115 | ), 116 | Expanded( 117 | child: PageView.builder( 118 | controller: pageQuranTabController, 119 | itemCount: pagesQuranTab.length, 120 | itemBuilder: ( 121 | BuildContext context, 122 | int index, 123 | ) { 124 | var widget = pagesQuranTab[index]; 125 | return widget(); 126 | }, 127 | ), 128 | ), 129 | ], 130 | ), 131 | ), 132 | Container( 133 | child: BookmarksWidget(), 134 | ), 135 | ], 136 | ), 137 | ), 138 | ); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_fontsizes/quran_settings_fontsizes_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:expandable/expandable.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:quran_app/baselib/base_state_mixin.dart'; 5 | import 'package:quran_app/pages/quran_settings_fontsizes/quran_settings_fontsizes_store.dart'; 6 | 7 | class QuranSettingsFontSizesWidget extends StatefulWidget { 8 | final QuranSettingsFontsizesStore store; 9 | QuranSettingsFontSizesWidget({ 10 | @required this.store, 11 | Key key, 12 | }) : super(key: key); 13 | 14 | _QuranSettingsFontSizesWidgetState createState() => 15 | _QuranSettingsFontSizesWidgetState(); 16 | } 17 | 18 | class _QuranSettingsFontSizesWidgetState 19 | extends State 20 | with 21 | BaseStateMixin { 23 | @override 24 | QuranSettingsFontsizesStore get store => widget.store; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return Container( 34 | padding: EdgeInsets.symmetric( 35 | horizontal: 10, 36 | ), 37 | child: Column( 38 | children: [ 39 | ExpandableNotifier( 40 | initialExpanded: true, 41 | child: Card( 42 | clipBehavior: Clip.antiAlias, 43 | child: Column( 44 | children: [ 45 | ExpandablePanel( 46 | theme: ExpandableThemeData( 47 | iconColor: Theme.of(context).accentColor, 48 | ), 49 | header: Padding( 50 | padding: EdgeInsets.all(10), 51 | child: Text( 52 | store.localization.getByKey( 53 | 'quran_settings_fontsizes.arabic_fontsize', 54 | ), 55 | style: TextStyle( 56 | fontWeight: FontWeight.bold, 57 | ), 58 | ), 59 | ), 60 | expanded: StreamBuilder( 61 | initialData: store.arabicFontSize$.value, 62 | stream: store.arabicFontSize$, 63 | builder: ( 64 | BuildContext context, 65 | AsyncSnapshot snapshot, 66 | ) { 67 | return Slider( 68 | min: 15, 69 | max: 100, 70 | value: snapshot.data, 71 | onChanged: (double value) { 72 | store.arabicFontSizeChanged$.add(value); 73 | }, 74 | ); 75 | }, 76 | ), 77 | ), 78 | ], 79 | ), 80 | ), 81 | ), 82 | ExpandableNotifier( 83 | initialExpanded: true, 84 | child: Card( 85 | clipBehavior: Clip.antiAlias, 86 | child: Column( 87 | children: [ 88 | ExpandablePanel( 89 | theme: ExpandableThemeData( 90 | iconColor: Theme.of(context).accentColor, 91 | ), 92 | header: Padding( 93 | padding: EdgeInsets.all(10), 94 | child: Text( 95 | store.localization.getByKey( 96 | 'quran_settings_fontsizes.translation_fontsize', 97 | ), 98 | style: TextStyle( 99 | fontWeight: FontWeight.bold, 100 | ), 101 | ), 102 | ), 103 | expanded: StreamBuilder( 104 | initialData: store.translationFontSize$.value, 105 | stream: store.translationFontSize$, 106 | builder: ( 107 | BuildContext context, 108 | AsyncSnapshot snapshot, 109 | ) { 110 | return Slider( 111 | min: 15, 112 | max: 100, 113 | value: snapshot.data, 114 | onChanged: (double value) { 115 | store.translationFontSizeChanged$.add(value); 116 | }, 117 | ); 118 | }, 119 | ), 120 | ), 121 | ], 122 | ), 123 | ), 124 | ), 125 | ], 126 | ), 127 | ); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/pages/home_tab_juz/home_tab_juz_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_mobx/flutter_mobx.dart'; 4 | import 'package:quran_app/app_widgets/shimmer_loading.dart'; 5 | import 'package:quran_app/baselib/base_state_mixin.dart'; 6 | import 'package:quran_app/baselib/widgets.dart'; 7 | import 'package:quran_app/models/models.dart'; 8 | 9 | import 'home_tab_juz_store.dart'; 10 | 11 | class HomeTabJuzWidget extends StatefulWidget { 12 | HomeTabJuzWidget({Key key}) : super(key: key); 13 | 14 | _HomeTabJuzWidgetState createState() => _HomeTabJuzWidgetState(); 15 | } 16 | 17 | class _HomeTabJuzWidgetState extends State 18 | with 19 | AutomaticKeepAliveClientMixin, 20 | BaseStateMixin { 21 | final _store = HomeTabJuzStore(); 22 | @override 23 | HomeTabJuzStore get store => _store; 24 | 25 | @override 26 | bool get wantKeepAlive => true; 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | 32 | WidgetsBinding.instance.addPostFrameCallback((_) { 33 | store.getListJuz.executeIf(); 34 | }); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | super.build(context); 40 | 41 | return Scaffold( 42 | body: StreamBuilder( 43 | initialData: store.state$.valueOrNull, 44 | stream: store.state$, 45 | builder: ( 46 | BuildContext context, 47 | AsyncSnapshot snapshot, 48 | ) { 49 | return WidgetSelector( 50 | selectedState: snapshot.data, 51 | states: { 52 | DataState( 53 | enumSelector: EnumSelector.success, 54 | ): Observer( 55 | builder: (BuildContext context) { 56 | return ListView.builder( 57 | itemCount: store.listJuz.length, 58 | itemBuilder: ( 59 | BuildContext context, 60 | int index, 61 | ) { 62 | var item = store.listJuz[index]; 63 | var verseMapping = 64 | JuzItem.getVerseMappingJuzItem(item.verseMapping); 65 | 66 | return ListTile( 67 | onTap: () { 68 | store.juzItemTapped.executeIf(item); 69 | }, 70 | title: Column( 71 | crossAxisAlignment: CrossAxisAlignment.stretch, 72 | children: [ 73 | Text( 74 | '${store.localization.getByKey('home_tab_juz.juz')} ${item.juzNumber}', 75 | style: TextStyle( 76 | fontSize: 18, 77 | ), 78 | ), 79 | Text( 80 | '${item.chapters.nameSimple} ${verseMapping.first.surah}:${verseMapping.first.startAya}', 81 | ), 82 | ], 83 | ), 84 | trailing: Container( 85 | width: 175, 86 | child: Text( 87 | item.listAya.first.text, 88 | textDirection: TextDirection.rtl, 89 | textAlign: TextAlign.right, 90 | maxLines: 1, 91 | overflow: TextOverflow.ellipsis, 92 | style: TextStyle( 93 | fontSize: 20, 94 | ), 95 | ), 96 | ), 97 | ); 98 | }, 99 | ); 100 | }, 101 | ), 102 | DataState( 103 | enumSelector: EnumSelector.loading, 104 | ): ListView.builder( 105 | itemCount: 10, 106 | itemBuilder: ( 107 | BuildContext context, 108 | int index, 109 | ) { 110 | return ListTile( 111 | title: Column( 112 | crossAxisAlignment: CrossAxisAlignment.stretch, 113 | children: [ 114 | ShimmerLoading( 115 | height: 10, 116 | ), 117 | SizedBox( 118 | height: 5, 119 | ), 120 | ShimmerLoading( 121 | height: 10, 122 | ), 123 | ], 124 | ), 125 | trailing: Container( 126 | width: 175, 127 | child: ShimmerLoading( 128 | height: 20, 129 | ), 130 | ), 131 | ); 132 | }, 133 | ), 134 | }, 135 | ); 136 | }, 137 | ), 138 | ); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/pages/quran_settings_app/quran_settings_app_widget.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.11 2 | import 'package:expandable/expandable.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:quran_app/baselib/base_state_mixin.dart'; 5 | import 'package:quran_app/baselib/localization_service.dart'; 6 | import 'package:quran_app/pages/quran_settings_app/quran_settings_app_store.dart'; 7 | import 'package:rxdart/rxdart.dart'; 8 | 9 | class QuranSettingsAppWidget extends StatefulWidget { 10 | final QuranSettingsAppStore store; 11 | QuranSettingsAppWidget({ 12 | @required this.store, 13 | Key key, 14 | }) : super(key: key); 15 | 16 | _QuranSettingsFontSApptState createState() => _QuranSettingsFontSApptState(); 17 | } 18 | 19 | class _QuranSettingsFontSApptState extends State 20 | with BaseStateMixin { 21 | @override 22 | QuranSettingsAppStore get store => widget.store; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Container( 32 | padding: EdgeInsets.symmetric( 33 | horizontal: 10, 34 | ), 35 | child: Column( 36 | children: [ 37 | ExpandableNotifier( 38 | initialExpanded: false, 39 | child: Card( 40 | clipBehavior: Clip.antiAlias, 41 | child: Column( 42 | children: [ 43 | ExpandablePanel( 44 | theme: ExpandableThemeData( 45 | iconColor: Theme.of(context).accentColor, 46 | ), 47 | header: Padding( 48 | padding: EdgeInsets.all(10), 49 | child: Text( 50 | store.localization.getByKey( 51 | 'quran_settings_app_widget.title', 52 | ), 53 | style: TextStyle( 54 | fontWeight: FontWeight.bold, 55 | ), 56 | ), 57 | ), 58 | expanded: Container( 59 | margin: EdgeInsets.symmetric( 60 | horizontal: 15, 61 | ), 62 | child: Column( 63 | crossAxisAlignment: CrossAxisAlignment.stretch, 64 | children: [ 65 | StreamBuilder>( 66 | initialData: [], 67 | stream: store.supportedLanguages, 68 | builder: ( 69 | BuildContext context, 70 | AsyncSnapshot> snapshot, 71 | ) { 72 | var items = snapshot.data; 73 | 74 | return StreamBuilder( 75 | initialData: null, 76 | stream: 77 | store.initialiSelectedLanguage.mergeWith([ 78 | store.selectedLanguage, 79 | ]), 80 | builder: ( 81 | BuildContext context, 82 | AsyncSnapshot snapshot, 83 | ) { 84 | var initialValue = snapshot.data; 85 | if (initialValue == null) { 86 | return Container(); 87 | } 88 | 89 | return DropdownButtonHideUnderline( 90 | child: DropdownButton( 91 | onChanged: (v) { 92 | store.selectedLanguage.add(v); 93 | }, 94 | value: initialValue, 95 | isExpanded: true, 96 | isDense: true, 97 | items: items.map((f) { 98 | return DropdownMenuItem( 99 | value: f, 100 | child: Text( 101 | f.name, 102 | ), 103 | ); 104 | }).toList(), 105 | ), 106 | ); 107 | }, 108 | ); 109 | }, 110 | ), 111 | Container( 112 | height: 5, 113 | child: Center( 114 | child: Container( 115 | height: 1, 116 | color: Theme.of(context).dividerColor, 117 | ), 118 | ), 119 | ), 120 | Text( 121 | store.localization.getByKey( 122 | 'quran_settings_app_widget.language', 123 | ), 124 | style: TextStyle( 125 | fontWeight: FontWeight.bold, 126 | fontSize: 12, 127 | ), 128 | ), 129 | SizedBox( 130 | height: 10, 131 | ), 132 | ], 133 | ), 134 | ), 135 | ), 136 | ], 137 | ), 138 | ), 139 | ), 140 | ], 141 | ), 142 | ); 143 | } 144 | } 145 | --------------------------------------------------------------------------------