diff --git a/.github/workflows/analyze.yaml b/.github/workflows/analyze.yaml new file mode 100644 index 0000000..fdf76ba --- /dev/null +++ b/.github/workflows/analyze.yaml @@ -0,0 +1,40 @@ +name: analyze + +env: + FLUTTER_VERSION: stable + +on: + pull_request: + paths-ignore: + - '**.md' + push: + branches: + - main + paths-ignore: + - '**.md' +jobs: + format: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2.3.4 + - uses: subosito/flutter-action@v1 + with: + channel: 'stable' + + - run: flutter pub get + + - run: flutter analyze . + + - run: flutter format lib/** --set-exit-if-changed + + test: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2.3.4 + - uses: subosito/flutter-action@v1 + with: + channel: 'stable' + + - run: flutter pub get + + - run: flutter test diff --git a/README.md b/README.md index 23f3dc0..ab60579 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # [Noor App | تطبيق نُور](https://noorathkar.com) +[![Codemagic build status](https://api.codemagic.io/apps/6208f023546bd24402e57b64/6208f023546bd24402e57b63/status_badge.svg)](https://codemagic.io/apps/6208f023546bd24402e57b64/6208f023546bd24402e57b63/latest_build) +

diff --git a/analysis_options.yaml b/analysis_options.yaml index 5bfd898..a3be6b8 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,17 +1 @@ -linter: - rules: - - always_specify_types - - avoid_unnecessary_containers - - unnecessary_statements - - prefer_conditional_assignment - - curly_braces_in_flow_control_structures - - flutter_style_todos - - join_return_with_assignment - - prefer_single_quotes - - avoid_returning_null_for_future - - avoid_returning_null - - unnecessary_this - - sized_box_for_whitespace - - always_use_package_imports - - avoid_relative_lib_imports - - lines_longer_than_80_chars +include: package:flutter_lints/flutter.yaml \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 5b548d3..b414d17 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -29,7 +29,7 @@ if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) } android { - compileSdkVersion 29 + compileSdkVersion 31 lintOptions { disable 'InvalidPackage' @@ -37,8 +37,8 @@ android { defaultConfig { applicationId "com.noor.sa" - minSdkVersion 16 - targetSdkVersion 29 + minSdkVersion 19 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 86dc806..9442ec6 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ @@ -24,7 +24,8 @@ - + android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" + android:value="true" /> + diff --git a/android/app/src/main/kotlin/com/noor/noor/MainActivity.kt b/android/app/src/main/kotlin/com/noor/noor/MainActivity.kt deleted file mode 100644 index ef6a635..0000000 --- a/android/app/src/main/kotlin/com/noor/noor/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.noor.noor - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/android/build.gradle b/android/build.gradle index 417af53..fed9310 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,6 @@ buildscript { repositories { google() mavenCentral() - jcenter() } dependencies { @@ -13,7 +12,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/android/gradle.properties b/android/gradle.properties index 1515360..755300e 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -2,4 +2,3 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -android.enableR8=true diff --git a/assets/icons/p_subha_list.svg b/assets/icons/p_subha_list.svg new file mode 100644 index 0000000..5e94e09 --- /dev/null +++ b/assets/icons/p_subha_list.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + diff --git a/assets/icons/p_subha_lock.svg b/assets/icons/p_subha_lock.svg new file mode 100644 index 0000000..1a38f2f --- /dev/null +++ b/assets/icons/p_subha_lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/p_subha_reset.svg b/assets/icons/p_subha_reset.svg new file mode 100644 index 0000000..1154aa3 --- /dev/null +++ b/assets/icons/p_subha_reset.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/json/default_subha_list.json b/assets/json/default_subha_list.json new file mode 100644 index 0000000..b4e0e3e --- /dev/null +++ b/assets/json/default_subha_list.json @@ -0,0 +1,11 @@ +{ + "default": [ + "سبحان الله", + "الحمدلله", + "لا إله إلا الله", + "الله أكبر", + "لا حول ولاقوة إلا بالله العلي العظيم", + "سبحان الله العظيم وبحمده", + "لا إله إلا الله وحده لا شريك له، له الملك وله الحمد، وهو على كل شيء قدير" + ] +} \ No newline at end of file diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9367d48..8d4492f 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f6150e2..6f0be01 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,93 +1,89 @@ PODS: - - Firebase/CoreOnly (7.11.0): - - FirebaseCore (= 7.11.0) - - Firebase/Messaging (7.11.0): + - Firebase/CoreOnly (8.9.0): + - FirebaseCore (= 8.9.0) + - Firebase/Messaging (8.9.0): - Firebase/CoreOnly - - FirebaseMessaging (~> 7.11.0) - - Firebase/RemoteConfig (7.11.0): + - FirebaseMessaging (~> 8.9.0) + - Firebase/RemoteConfig (8.9.0): - Firebase/CoreOnly - - FirebaseRemoteConfig (~> 7.11.0) - - firebase_core (1.1.1): - - Firebase/CoreOnly (= 7.11.0) + - FirebaseRemoteConfig (~> 8.9.0) + - firebase_core (1.10.2): + - Firebase/CoreOnly (= 8.9.0) - Flutter - - firebase_messaging (9.1.4): - - Firebase/Messaging (= 7.11.0) + - firebase_messaging (11.2.0): + - Firebase/Messaging (= 8.9.0) - firebase_core - Flutter - - firebase_remote_config (0.10.0-dev.3): - - Firebase/RemoteConfig (= 7.11.0) + - firebase_remote_config (1.0.0-dev.3): + - Firebase/RemoteConfig (= 8.9.0) - firebase_core - Flutter - - FirebaseABTesting (7.11.0): - - FirebaseCore (~> 7.0) - - FirebaseCore (7.11.0): - - FirebaseCoreDiagnostics (~> 7.4) - - GoogleUtilities/Environment (~> 7.0) - - GoogleUtilities/Logger (~> 7.0) - - FirebaseCoreDiagnostics (7.11.0): - - GoogleDataTransport (~> 8.4) - - GoogleUtilities/Environment (~> 7.0) - - GoogleUtilities/Logger (~> 7.0) + - FirebaseABTesting (8.9.0): + - FirebaseCore (~> 8.0) + - FirebaseCore (8.9.0): + - FirebaseCoreDiagnostics (~> 8.0) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/Logger (~> 7.6) + - FirebaseCoreDiagnostics (8.9.0): + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/Logger (~> 7.6) - nanopb (~> 2.30908.0) - - FirebaseInstallations (7.11.0): - - FirebaseCore (~> 7.0) - - GoogleUtilities/Environment (~> 7.0) - - GoogleUtilities/UserDefaults (~> 7.0) - - PromisesObjC (~> 1.2) - - FirebaseInstanceID (7.11.0): - - FirebaseCore (~> 7.0) - - FirebaseInstallations (~> 7.0) - - GoogleUtilities/Environment (~> 7.0) - - GoogleUtilities/UserDefaults (~> 7.0) - - FirebaseMessaging (7.11.0): - - FirebaseCore (~> 7.0) - - FirebaseInstallations (~> 7.0) - - FirebaseInstanceID (~> 7.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.0) - - GoogleUtilities/Environment (~> 7.0) - - GoogleUtilities/Reachability (~> 7.0) - - GoogleUtilities/UserDefaults (~> 7.0) - - FirebaseRemoteConfig (7.11.0): - - FirebaseABTesting (~> 7.0) - - FirebaseCore (~> 7.0) - - FirebaseInstallations (~> 7.0) - - GoogleUtilities/Environment (~> 7.0) - - "GoogleUtilities/NSData+zlib (~> 7.0)" + - FirebaseInstallations (8.9.0): + - FirebaseCore (~> 8.0) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/UserDefaults (~> 7.6) + - PromisesObjC (< 3.0, >= 1.2) + - FirebaseMessaging (8.9.0): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/AppDelegateSwizzler (~> 7.6) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/Reachability (~> 7.6) + - GoogleUtilities/UserDefaults (~> 7.6) + - nanopb (~> 2.30908.0) + - FirebaseRemoteConfig (8.9.0): + - FirebaseABTesting (~> 8.0) + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleUtilities/Environment (~> 7.6) + - "GoogleUtilities/NSData+zlib (~> 7.6)" - Flutter (1.0.0) - flutter_local_notifications (0.0.1): - Flutter - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - GoogleDataTransport (8.4.0): + - GoogleDataTransport (9.1.2): - GoogleUtilities/Environment (~> 7.2) - nanopb (~> 2.30908.0) - - PromisesObjC (~> 1.2) - - GoogleUtilities/AppDelegateSwizzler (7.4.1): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.6.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.4.1): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (7.4.1): + - GoogleUtilities/Environment (7.6.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.6.0): - GoogleUtilities/Environment - - GoogleUtilities/Network (7.4.1): + - GoogleUtilities/Network (7.6.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.4.1)" - - GoogleUtilities/Reachability (7.4.1): + - "GoogleUtilities/NSData+zlib (7.6.0)" + - GoogleUtilities/Reachability (7.6.0): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.4.1): + - GoogleUtilities/UserDefaults (7.6.0): - GoogleUtilities/Logger - nanopb (2.30908.0): - nanopb/decode (= 2.30908.0) - nanopb/encode (= 2.30908.0) - nanopb/decode (2.30908.0) - nanopb/encode (2.30908.0) - - path_provider (0.0.1): + - path_provider_ios (0.0.1): - Flutter - - PromisesObjC (1.2.12) + - PromisesObjC (2.0.0) - share (0.0.1): - Flutter - shared_preferences (0.0.1): @@ -104,7 +100,7 @@ DEPENDENCIES: - firebase_remote_config (from `.symlinks/plugins/firebase_remote_config/ios`) - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - share (from `.symlinks/plugins/share/ios`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -117,7 +113,6 @@ SPEC REPOS: - FirebaseCore - FirebaseCoreDiagnostics - FirebaseInstallations - - FirebaseInstanceID - FirebaseMessaging - FirebaseRemoteConfig - FMDB @@ -137,8 +132,8 @@ EXTERNAL SOURCES: :path: Flutter flutter_local_notifications: :path: ".symlinks/plugins/flutter_local_notifications/ios" - path_provider: - :path: ".symlinks/plugins/path_provider/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" share: :path: ".symlinks/plugins/share/ios" shared_preferences: @@ -149,25 +144,24 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/url_launcher/ios" SPEC CHECKSUMS: - Firebase: c121feb35e4126c0b355e3313fa9b487d47319fd - firebase_core: 54856a8a39b8f3e35f34fdd3373f3b92a1daa68b - firebase_messaging: 64d2a9b095bc7afa54eff3f4e13522d9098619d2 - firebase_remote_config: 44bb7d35f010caf2d28c2719a5b3585cea4ee558 - FirebaseABTesting: e66f1f80747792630d9b292966de206d5df9853b - FirebaseCore: 907447d8917a4d3eb0cce2829c5a0ad21d90b432 - FirebaseCoreDiagnostics: 68ad972f99206cef818230f3f3179d52ccfb7f8c - FirebaseInstallations: a58d4f72ec5861840b84df489f2668d970df558a - FirebaseInstanceID: ad5135045a498d7775903efd39762d2cdfa1be27 - FirebaseMessaging: 163435fb6db065e3b6228f1e577b10ed2cc506d2 - FirebaseRemoteConfig: 0ea30de5fb0231df8c1bdcdf3b6c23bdc5066131 - Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + Firebase: 13d8d96499e2635428d5bf0ec675df21f95d9a95 + firebase_core: 6d1a5e12a83b6d9419ecb8f2a53d876e7b4695da + firebase_messaging: 4f7eab3f100c30d0544f5a6f6ca549f7f8c12705 + firebase_remote_config: 387ba6e443177a276acfdcb24bce201c023e0d99 + FirebaseABTesting: 9de50b34bf9eb4a07d4edb7af82c14152fd905aa + FirebaseCore: 599ee609343eaf4941bd188f85e3aa077ffe325b + FirebaseCoreDiagnostics: 5daa63f1c1409d981a2d5007daa100b36eac6a34 + FirebaseInstallations: caa7c8e0d3e2345b8829d2fa9ca1b4dfbf2fcc85 + FirebaseMessaging: 82c4a48638f53f7b184f3cc9f6cd2cbe533ab316 + FirebaseRemoteConfig: a75c1bd44ebd3ed4ad3fa1ff09414a8b133be405 + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - GoogleDataTransport: cd9db2180fcecd8da1b561aea31e3e56cf834aa7 - GoogleUtilities: f8a43108b38a68eebe8b3540e1f4f2d28843ce20 + GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940 + GoogleUtilities: 684ee790a24f73ebb2d1d966e9711c203f2a4237 nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 + path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 share: 0b2c3e82132f5888bccca3351c504d0003b3b410 shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 @@ -175,4 +169,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c -COCOAPODS: 1.10.0 +COCOAPODS: 1.11.2 diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/lib/app/app.dart b/lib/app/app.dart index 39339f6..d7e4d70 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -9,8 +9,11 @@ import 'package:noor/exports/models.dart' show DataModel, SettingsModel; import 'package:noor/exports/components.dart' show CustomScrollBehavior; import 'package:noor/exports/controllers.dart' show ThemeModel; import 'package:noor/exports/constants.dart' show lightTheme, darkTheme; +import 'package:noor/pages/tabs/page_3_counter/counter_view_model.dart'; class NoorApp extends StatelessWidget { + const NoorApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MultiProvider( @@ -24,28 +27,33 @@ class NoorApp extends StatelessWidget { ChangeNotifierProvider( create: (_) => GetIt.I(), ), + ChangeNotifierProvider( + create: (_) => GetIt.I(), + ), ], - child: MaterialAppWithTheme(), + child: const MaterialAppWithTheme(), ); } } class MaterialAppWithTheme extends StatelessWidget { + const MaterialAppWithTheme({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ThemeModel themeProvider = context.watch(); return MaterialApp( - localizationsDelegates: >[ + localizationsDelegates: const >[ // ... app-specific localization delegate[s] here GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], - supportedLocales: [ - const Locale('ar'), // Arabic + supportedLocales: const [ + Locale('ar'), // Arabic ], - locale: Locale('ar'), + locale: const Locale('ar'), debugShowCheckedModeBanner: false, title: 'نُور', themeMode: themeProvider.theme, @@ -61,7 +69,7 @@ class MaterialAppWithTheme extends StatelessWidget { ), ); }, - home: SplashScreen(), + home: const SplashScreen(), ); } } diff --git a/lib/components/add_dialog.dart b/lib/components/add_dialog.dart new file mode 100644 index 0000000..7c69a5e --- /dev/null +++ b/lib/components/add_dialog.dart @@ -0,0 +1,205 @@ +import 'package:flutter/material.dart'; +import 'package:noor/components/dialog_button.dart'; +import 'package:noor/pages/tabs/page_3_counter/counter_list_view.dart'; + +import 'editable_text_max_length_highlighter.dart'; + +class AddDialog extends StatefulWidget { + const AddDialog( + this.context, { + Key? key, + this.mainContent = '', + this.mainContentMaxLength, + this.secondaryContent = '', + this.onSave, + this.onCancel, + this.enableSecondaryContent = false, + }) : super(key: key); + final BuildContext context; + final String mainContent; + final String secondaryContent; + final int? mainContentMaxLength; + final void Function(String)? onSave; + final void Function()? onCancel; + final bool enableSecondaryContent; + + static AddDialog of(BuildContext context) { + return AddDialog(context); + } + + Future show({ + String mainContent = '', + String secondaryContent = '', + void Function(String)? onSave, + void Function()? onCancel, + bool enableSecondaryContent = false, + bool barrierDismissible = true, + final int? mainContentMaxLength, + }) async { + return await showGeneralDialog( + transitionDuration: const Duration(milliseconds: 600), + barrierColor: Colors.black.withOpacity(0.75), + barrierLabel: '', + barrierDismissible: barrierDismissible, + context: context, + transitionBuilder: (_, Animation a1, Animation a2, __) { + final double curvedValue = + Curves.easeInOutBack.transform(a1.value) - 1.0; + return Transform( + transform: Matrix4.translationValues(0.0, curvedValue * 800, 0.0), + child: AddDialog( + context, + mainContent: mainContent, + mainContentMaxLength: mainContentMaxLength, + secondaryContent: secondaryContent, + onSave: onSave, + onCancel: onCancel, + enableSecondaryContent: enableSecondaryContent, + ), + ); + }, + pageBuilder: (_, __, ___) => const SizedBox(), + ); + } + + @override + _AddDialogState createState() => _AddDialogState(); +} + +class _AddDialogState extends State { + late TextFieldMaxLengthHighlighter mainContentController; + late TextEditingController secondaryContentController; + bool mainContentActive = false; + + @override + void initState() { + super.initState(); + mainContentController = TextFieldMaxLengthHighlighter( + text: widget.mainContent, + maxLength: widget.mainContentMaxLength, + ); + + secondaryContentController = + TextEditingController(text: widget.secondaryContent); + + mainContentController.addListener(() { + setState(() { + if (mainContentController.text.isEmpty || + mainContentController.text.length > kMaxLength) { + mainContentActive = false; + } else { + mainContentActive = true; + } + }); + }); + } + + button({ + required String text, + required BorderRadiusGeometry radius, + void Function()? onPressed, + BoxBorder? border, + Color? textColor, + }) { + return DialogButton( + label: text, + border: border, + onPressed: onPressed, + radius: radius, + textColor: textColor, + ); + } + + // Text input design (used in dialoges) + input(double height, String text, TextEditingController controller) { + return ConstrainedBox( + constraints: BoxConstraints(minHeight: height), + child: SingleChildScrollView( + child: TextField( + autofocus: true, + controller: controller, + style: Theme.of(context).textTheme.bodyText1, + decoration: InputDecoration( + filled: false, + contentPadding: + const EdgeInsets.symmetric(vertical: 12, horizontal: 10.0), + border: InputBorder.none, + hintText: text, + counterText: "", + ), + maxLines: 3, + minLines: 3, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Dialog( + elevation: 6.0, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).brightness == Brightness.light + ? Colors.white + : const Color(0xff1B2349), + borderRadius: BorderRadius.circular(15.0), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + input(100.0, 'أضِف ذِكر...', mainContentController), + if (widget.enableSecondaryContent) const Divider(), + if (widget.enableSecondaryContent) + input(80.0, 'نص إضافي...', secondaryContentController), + if (widget.enableSecondaryContent) const SizedBox(height: 40) + ], + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + constraints: const BoxConstraints.expand(height: 40), + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + button( + text: 'حفظ', + border: Border( + left: BorderSide( + width: 0.5, color: Theme.of(context).cardColor), + ), + radius: const BorderRadius.only( + bottomRight: Radius.circular(15)), + onPressed: !mainContentActive + ? null + : () => + widget.onSave?.call(mainContentController.text), + ), + button( + text: 'إلغاء', + border: Border( + right: BorderSide( + width: 0.5, color: Theme.of(context).cardColor), + ), + textColor: Colors.white, + radius: const BorderRadius.only( + bottomLeft: Radius.circular(15)), + onPressed: widget.onCancel, + ), + ], + ), + ), + ) + ], + ), + ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), + ); + } +} diff --git a/lib/components/alert_dialog.dart b/lib/components/alert_dialog.dart new file mode 100644 index 0000000..98537ff --- /dev/null +++ b/lib/components/alert_dialog.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; + +import 'dialog_button.dart'; + +class NoorAlertDialog extends StatelessWidget { + const NoorAlertDialog( + this.context, { + Key? key, + this.title = '', + this.content = '', + }) : super(key: key); + final String title; + final String content; + final BuildContext context; + + static NoorAlertDialog of(BuildContext context) { + return NoorAlertDialog(context); + } + + Future show({ + required String title, + required String content, + }) async { + return await showGeneralDialog( + transitionDuration: const Duration(milliseconds: 600), + barrierColor: Colors.black.withOpacity(0.75), + barrierLabel: '', + barrierDismissible: true, + context: context, + transitionBuilder: (_, Animation a1, Animation a2, __) { + final double curvedValue = + Curves.easeInOutBack.transform(a1.value) - 1.0; + return Transform( + transform: Matrix4.translationValues(0.0, curvedValue * 800, 0.0), + child: NoorAlertDialog( + context, + title: title, + content: content, + ), + ); + }, + pageBuilder: (_, __, ___) => const SizedBox(), + ); + } + + // Text input design (used in dialoges) + input(double height, String text, TextEditingController controller) { + return ConstrainedBox( + constraints: BoxConstraints(minHeight: height), + child: SingleChildScrollView( + child: TextField( + autofocus: true, + controller: controller, + style: Theme.of(context).textTheme.bodyText1, + decoration: InputDecoration( + filled: false, + contentPadding: + const EdgeInsets.symmetric(vertical: 12, horizontal: 10.0), + border: InputBorder.none, + hintText: text, + counterText: "", + ), + maxLines: 3, + minLines: 3, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Dialog( + elevation: 6.0, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), + child: Container( + constraints: const BoxConstraints(maxHeight: 130), + child: Stack( + children: [ + Container( + padding: const EdgeInsets.all(10), + alignment: Alignment.center, + child: Column( + children: [ + Text( + title, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + Text(content) + ], + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + constraints: const BoxConstraints.expand(height: 40), + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DialogButton( + label: 'إلغاء', + textColor: Colors.white, + border: Border( + right: BorderSide( + width: 0.5, color: Theme.of(context).cardColor), + ), + onPressed: () async { + Navigator.of(context).pop(false); + }, + radius: const BorderRadius.only( + bottomLeft: Radius.circular(15), + bottomRight: Radius.circular(15), + ), + ), + ], + ), + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/components/allah_names_title.dart b/lib/components/allah_names_title.dart index 9a7205e..ca83617 100644 --- a/lib/components/allah_names_title.dart +++ b/lib/components/allah_names_title.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:noor/exports/constants.dart' show Images; class NameTitleCard extends StatelessWidget { - NameTitleCard({this.title}); + const NameTitleCard({Key? key, this.title}) : super(key: key); final String? title; @override @@ -18,13 +18,13 @@ class NameTitleCard extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline2, ), - margin: EdgeInsets.only( + margin: const EdgeInsets.only( left: 30, right: 30, top: 10.0, bottom: 10.0, ), - padding: EdgeInsets.only( + padding: const EdgeInsets.only( right: 90, left: 90, top: 10.0, @@ -34,8 +34,8 @@ class NameTitleCard extends StatelessWidget { height: 95, alignment: Alignment.center, decoration: BoxDecoration( - boxShadow: [ - new BoxShadow( + boxShadow: const [ + BoxShadow( color: Colors.black26, blurRadius: 8.0, ), diff --git a/lib/components/athkar_card.dart b/lib/components/athkar_card.dart index bfe3524..c9b7258 100644 --- a/lib/components/athkar_card.dart +++ b/lib/components/athkar_card.dart @@ -44,10 +44,10 @@ class AthkarCard extends StatelessWidget { alignment: Alignment.bottomCenter, child: counter.position < 1 ? AnimatedContainer( - duration: Duration(milliseconds: 400), + duration: const Duration(milliseconds: 400), curve: Curves.bounceInOut, alignment: Alignment.center, - child: Icon( + child: const Icon( Icons.done, color: Color(0xff58d1ed), ), @@ -55,9 +55,9 @@ class AthkarCard extends StatelessWidget { color: Theme.of(context).brightness == Brightness.light ? Colors.white - : Color(0xff202b54), + : const Color(0xff202b54), border: Border.all( - color: Color(0xff6f86d6), width: 2), + color: const Color(0xff6f86d6), width: 2), borderRadius: BorderRadius.circular(50.0)), width: 45.0, height: 45.0, @@ -69,21 +69,22 @@ class AthkarCard extends StatelessWidget { '${counter.position}'.arabicDigit(), key: ValueKey(counter.position), style: TextStyle( - color: Theme.of(context).accentColor, - fontSize: 16, - height: 1.25), + color: Theme.of(context).colorScheme.secondary, + fontSize: 16, + height: 1.25, + ), ), ), decoration: BoxDecoration( color: Theme.of(context).brightness == Brightness.light ? Colors.white - : Color(0xff202b54), + : const Color(0xff202b54), border: Border.all( color: Theme.of(context).brightness == Brightness.light ? Colors.grey[400]! - : Color(0xff33477f), + : const Color(0xff33477f), width: 2), borderRadius: BorderRadius.circular(50.0), ), diff --git a/lib/components/athkar_title.dart b/lib/components/athkar_title.dart index a8f0152..b0a2597 100644 --- a/lib/components/athkar_title.dart +++ b/lib/components/athkar_title.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:noor/exports/constants.dart' show Images; class ThekrTitleCard extends StatelessWidget { - ThekrTitleCard({this.title}); + const ThekrTitleCard({Key? key, this.title}) : super(key: key); final String? title; @override @@ -13,7 +13,7 @@ class ThekrTitleCard extends StatelessWidget { children: [ Positioned( child: Container( - margin: EdgeInsets.only( + margin: const EdgeInsets.only( left: 30, right: 30, top: 10.0, @@ -29,7 +29,7 @@ class ThekrTitleCard extends StatelessWidget { style: Theme.of(context).textTheme.headline2, ), alignment: Alignment.center, - padding: EdgeInsets.only( + padding: const EdgeInsets.only( right: 70, left: 70, top: 10.0, @@ -37,8 +37,8 @@ class ThekrTitleCard extends StatelessWidget { ), ), decoration: BoxDecoration( - boxShadow: [ - new BoxShadow( + boxShadow: const [ + BoxShadow( color: Colors.black26, blurRadius: 8.0, ), diff --git a/lib/components/bottom_nav.dart b/lib/components/bottom_nav.dart index 7e7c73a..794a06b 100644 --- a/lib/components/bottom_nav.dart +++ b/lib/components/bottom_nav.dart @@ -3,7 +3,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:noor/exports/constants.dart' show NoorIcons; class BottomNav extends StatefulWidget { - BottomNav({ + const BottomNav({ Key? key, this.onTap, }) : super(key: key); @@ -112,17 +112,17 @@ class _BottomItemState extends State reverseDuration: Duration(milliseconds: duration), ); - textOffset1 = - Tween(begin: Offset(0.0, textOffset), end: Offset(0.0, 1.0)) - .animate( + textOffset1 = Tween( + begin: Offset(0.0, textOffset), end: const Offset(0.0, 1.0)) + .animate( CurvedAnimation( parent: controller, curve: Curves.elasticOut, reverseCurve: Curves.elasticIn), ); - iconOffset1 = - Tween(begin: Offset(0.0, -1.0), end: Offset(0.0, iconOffset)) - .animate( + iconOffset1 = Tween( + begin: const Offset(0.0, -1.0), end: Offset(0.0, iconOffset)) + .animate( CurvedAnimation( parent: controller, curve: Curves.elasticOut, diff --git a/lib/components/card_template.dart b/lib/components/card_template.dart index e5f81a2..acba26d 100644 --- a/lib/components/card_template.dart +++ b/lib/components/card_template.dart @@ -32,16 +32,16 @@ class CardTemplate extends StatelessWidget { end: Alignment.bottomCenter, colors: Theme.of(context).brightness == Brightness.light ? [ - Color(0xfff3f3f3), - Color(0xfff1f1f1), + const Color(0xfff3f3f3), + const Color(0xfff1f1f1), ] : [ - Color(0xff1B2349), - Color(0xff161A3A), + const Color(0xff1B2349), + const Color(0xff161A3A), ], ), boxShadow: [ - new BoxShadow( + BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 12.0, ), @@ -58,9 +58,9 @@ class CardTemplate extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - padding: EdgeInsets.symmetric(horizontal: 5.0), + padding: const EdgeInsets.symmetric(horizontal: 5.0), decoration: BoxDecoration( - borderRadius: BorderRadius.only( + borderRadius: const BorderRadius.only( topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0), ), @@ -81,7 +81,7 @@ class CardTemplate extends StatelessWidget { alignment: Alignment.topRight, margin: const EdgeInsets.all(5.0), child: Scrollbar( - radius: Radius.circular(4), + radius: const Radius.circular(4), child: SingleChildScrollView( child: Container( width: double.infinity, @@ -98,7 +98,7 @@ class CardTemplate extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(5.0), child: Scrollbar( - radius: Radius.circular(4), + radius: const Radius.circular(4), child: SingleChildScrollView( child: DefaultTextStyle.merge( textAlign: TextAlign.justify, diff --git a/lib/components/card_text.dart b/lib/components/card_text.dart index 328995f..0fa778e 100644 --- a/lib/components/card_text.dart +++ b/lib/components/card_text.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use + import 'package:flutter/material.dart'; import 'package:noor/models/allah_name.dart'; import 'package:provider/provider.dart'; @@ -75,7 +77,7 @@ class CardText extends StatelessWidget { highlight.map((String e) => Tashkeel.remove(e)).toList().cast(); final RegExp marks = RegExp( - r'[\u{060C}|\u{FD3F}|\u{060C}|\u{FD3E}|\u{002E}|\u{0029}|\u{0028}|\u{0021}|\u{005B}|\u{005D}|\u{003A}]', + r'^[\u{060C}|\u{FD3F}|\u{060C}|\u{FD3E}|\u{002E}|\u{0029}|\u{0028}|\u{0021}|\u{005B}|\u{005D}|\u{003A}]', unicode: true, ); @@ -83,6 +85,7 @@ class CardText extends StatelessWidget { r'^[\u{0648}|\u{0644}|\u{0628}]', unicode: true, ); + int i = 0; for (String s in sourceList) { @@ -91,7 +94,7 @@ class CardText extends StatelessWidget { List match = []; - if (s.length > 0) { + if (s.isNotEmpty) { for (String m in tmpMatch) { if (letters.hasMatch(m) && !highlight.contains(m)) { match.addAll(m.splitWithDelim(letters)); @@ -114,14 +117,12 @@ class CardText extends StatelessWidget { children.add( TextSpan( text: m, - style: TextStyle(color: Theme.of(context).buttonColor), + style: TextStyle(color: Theme.of(context).colorScheme.outline), ), ); } else { children.add( - TextSpan( - text: m, - ), + TextSpan(text: m), ); } } @@ -132,7 +133,7 @@ class CardText extends StatelessWidget { children.add( TextSpan( text: s, - style: TextStyle(color: Theme.of(context).buttonColor), + style: TextStyle(color: Theme.of(context).colorScheme.outline), ), ); } else { @@ -145,7 +146,7 @@ class CardText extends StatelessWidget { } children.add( - TextSpan( + const TextSpan( text: ' ', ), ); @@ -175,7 +176,7 @@ class CardText extends StatelessWidget { child: Builder( builder: (BuildContext context) { return AnimatedSwitcher( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), child: item is AllahName ? RichText( textScaleFactor: settings.fontSize, diff --git a/lib/components/copy_action.dart b/lib/components/copy_action.dart index 7ff1fb2..62e0b67 100644 --- a/lib/components/copy_action.dart +++ b/lib/components/copy_action.dart @@ -20,7 +20,7 @@ class CopyAction extends StatelessWidget { barrierLabel: 'Label', barrierDismissible: false, barrierColor: Colors.black.withOpacity(0.2), - transitionDuration: Duration(milliseconds: 500), + transitionDuration: const Duration(milliseconds: 500), context: context, pageBuilder: (BuildContext context, _, __) { return Align( @@ -40,7 +40,7 @@ class CopyAction extends StatelessWidget { decoration: BoxDecoration( color: const Color(0xff6f85d5), borderRadius: BorderRadius.circular(15.0), - boxShadow: [ + boxShadow: const [ BoxShadow(blurRadius: 5, color: Colors.black26), ], ), @@ -49,17 +49,20 @@ class CopyAction extends StatelessWidget { }, transitionBuilder: (_, Animation anim1, __, Widget child) { return SlideTransition( - position: Tween(begin: Offset(0, 0.6), end: Offset(0, 0)) - .animate(CurvedAnimation( - parent: anim1, - curve: Curves.easeOutCirc, - reverseCurve: Curves.easeIn, - )), + position: Tween( + begin: const Offset(0, 0.6), end: const Offset(0, 0)) + .animate( + CurvedAnimation( + parent: anim1, + curve: Curves.easeOutCirc, + reverseCurve: Curves.easeIn, + ), + ), child: child, ); }, ); - Future.delayed(Duration(milliseconds: 1000)).then((_) { + Future.delayed(const Duration(milliseconds: 1000)).then((_) { Navigator.of(context, rootNavigator: true).pop('dialog'); }); } diff --git a/lib/components/custom_scroll_bhaviour.dart b/lib/components/custom_scroll_bhaviour.dart index d822a0b..c6e2212 100644 --- a/lib/components/custom_scroll_bhaviour.dart +++ b/lib/components/custom_scroll_bhaviour.dart @@ -6,4 +6,4 @@ class CustomScrollBehavior extends ScrollBehavior { BuildContext context, Widget child, AxisDirection axisDirection) { return child; } -} \ No newline at end of file +} diff --git a/lib/components/delete_dialog.dart b/lib/components/delete_dialog.dart index 9c801cc..cce657c 100644 --- a/lib/components/delete_dialog.dart +++ b/lib/components/delete_dialog.dart @@ -1,4 +1,9 @@ +// ignore_for_file: deprecated_member_use + import 'package:flutter/material.dart'; +import 'package:noor/constants/strings.dart'; + +import 'dialog_button.dart'; class DeleteConfirmationDialog extends StatelessWidget { const DeleteConfirmationDialog( @@ -13,7 +18,7 @@ class DeleteConfirmationDialog extends StatelessWidget { Future show() async { return await showGeneralDialog( - transitionDuration: Duration(milliseconds: 600), + transitionDuration: const Duration(milliseconds: 600), barrierColor: Colors.black.withOpacity(0.75), barrierLabel: '', context: context, @@ -25,40 +30,7 @@ class DeleteConfirmationDialog extends StatelessWidget { child: DeleteConfirmationDialog(context), ); }, - pageBuilder: (_, __, ___) => SizedBox(), - ); - } - - // TODO(Mais): Refactor - button( - {required String text, - BoxBorder? border, - required BorderRadiusGeometry radius, - Color? textColor, - Function? onPress}) { - return Expanded( - flex: 1, - child: Container( - decoration: BoxDecoration(border: border), - child: RaisedButton( - shape: RoundedRectangleBorder(borderRadius: radius), - elevation: 0.0, - focusElevation: 0.0, - highlightElevation: 0.0, - hoverElevation: 0.0, - splashColor: Colors.white24, - highlightColor: Colors.white24, - onPressed: onPress as void Function()?, - child: Text( - text, - style: TextStyle( - color: textColor, - fontWeight: FontWeight.w300, - fontSize: 12, - ), - ), - ), - ), + pageBuilder: (_, __, ___) => const SizedBox(), ); } @@ -66,59 +38,63 @@ class DeleteConfirmationDialog extends StatelessWidget { Widget build(BuildContext context) { return Dialog( elevation: 6.0, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), child: Container( - constraints: BoxConstraints(maxHeight: 130), + constraints: const BoxConstraints(maxHeight: 130), child: Stack( children: [ Container( - padding: EdgeInsets.all(10), + padding: const EdgeInsets.all(10), alignment: Alignment.center, child: Column( children: [ Text( - 'تأكيد الحذف', + Strings.confirmDeleteTitle, style: TextStyle( color: Theme.of(context).primaryColor, fontSize: 18, fontWeight: FontWeight.bold, ), ), - Text('هل أنت مُتأكد من رغبتك في الحذف؟') + const Text(Strings.confirmDeleteContent) ], ), ), Align( alignment: Alignment.bottomCenter, child: Container( - constraints: BoxConstraints.expand(height: 40), + constraints: const BoxConstraints.expand(height: 40), child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ - button( - text: 'حذف', - border: Border( - left: BorderSide(width: 0.5, color: Colors.white), - ), - radius: BorderRadius.only( - bottomRight: Radius.circular(15), - ), - textColor: Colors.lightBlue[100], - onPress: () async { - Navigator.of(context).pop(true); - }), - button( - text: 'إلغاء', - border: Border( - right: BorderSide(width: 0.5, color: Colors.white), - ), - radius: BorderRadius.only( - bottomLeft: Radius.circular(15), - ), - textColor: Colors.white, - onPress: () { - Navigator.of(context).pop(false); - }), + DialogButton( + label: 'حذف', + border: Border( + left: BorderSide( + width: 0.5, color: Theme.of(context).cardColor), + ), + onPressed: () async { + Navigator.of(context).pop(true); + }, + radius: const BorderRadius.only( + bottomRight: Radius.circular(15), + ), + ), + DialogButton( + label: 'إلغاء', + textColor: Colors.white, + border: Border( + right: BorderSide( + width: 0.5, color: Theme.of(context).cardColor), + ), + onPressed: () async { + Navigator.of(context).pop(false); + }, + radius: const BorderRadius.only( + bottomLeft: Radius.circular(15), + ), + ), ], ), ), @@ -126,7 +102,6 @@ class DeleteConfirmationDialog extends StatelessWidget { ], ), ), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), ); } } diff --git a/lib/components/dialog_button.dart b/lib/components/dialog_button.dart new file mode 100644 index 0000000..58c7ecc --- /dev/null +++ b/lib/components/dialog_button.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class DialogButton extends StatelessWidget { + const DialogButton({ + Key? key, + required this.label, + this.onPressed, + this.radius, + this.border, + this.textColor, + }) : super(key: key); + + final Function()? onPressed; + final String label; + final BorderRadiusGeometry? radius; + final BoxBorder? border; + final Color? textColor; + + @override + Widget build(BuildContext context) { + return Expanded( + flex: 1, + child: Container( + decoration: BoxDecoration(border: border), + child: ElevatedButton( + style: radius == null + ? null + : ElevatedButton.styleFrom( + shape: RoundedRectangleBorder(borderRadius: radius!), + ), + onPressed: onPressed, + child: DefaultTextStyle.merge( + child: Text( + label, + textScaleFactor: 1, + style: TextStyle(color: textColor), + ), + ), + ), + ), + ); + } +} diff --git a/lib/components/editable_text_max_length_highlighter.dart b/lib/components/editable_text_max_length_highlighter.dart new file mode 100644 index 0000000..71661b5 --- /dev/null +++ b/lib/components/editable_text_max_length_highlighter.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +class TextFieldMaxLengthHighlighter extends TextEditingController { + TextFieldMaxLengthHighlighter({String? text, this.maxLength}) + : super(text: text); + + final int? maxLength; + + @override + TextSpan buildTextSpan( + {required BuildContext context, + TextStyle? style, + required bool withComposing}) { + String allowedText = text; + String nonAllowedText = ''; + + if (maxLength != null && allowedText.length >= maxLength!) { + allowedText = text.substring(0, maxLength!); + nonAllowedText = text.substring(maxLength!); + } + + return TextSpan(style: style, children: [ + TextSpan(text: allowedText), + if (nonAllowedText.isNotEmpty) + TextSpan( + text: nonAllowedText, + style: style?.copyWith( + color: Colors.red, + ), + ), + ]); + } +} diff --git a/lib/components/fav_action.dart b/lib/components/fav_action.dart index 8bfa27f..0f6516c 100644 --- a/lib/components/fav_action.dart +++ b/lib/components/fav_action.dart @@ -31,7 +31,7 @@ class _FavActionState extends State { crossFadeState: widget.item.isFav ? CrossFadeState.showSecond : CrossFadeState.showFirst, - duration: Duration(milliseconds: 500), + duration: const Duration(milliseconds: 500), ), ); } diff --git a/lib/components/glowing_stars.dart b/lib/components/glowing_stars.dart index 2d4330c..5e7c839 100644 --- a/lib/components/glowing_stars.dart +++ b/lib/components/glowing_stars.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:noor/constants/images.dart'; -import 'package:provider/provider.dart'; - -import 'package:noor/exports/controllers.dart' show ThemeModel; enum StarType { circle, normal } class GlowingStars extends StatelessWidget { + const GlowingStars({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { double width = MediaQuery.of(context).size.width; @@ -15,7 +14,7 @@ class GlowingStars extends StatelessWidget { height: 140, width: width, child: Stack( - children: [ + children: const [ //star 0 Positioned( top: 10, @@ -191,7 +190,7 @@ class GlowingStars extends StatelessWidget { } class Star extends StatefulWidget { - Star({ + const Star({ Key? key, required this.size, required this.radius, @@ -218,8 +217,8 @@ class _StarState extends State with SingleTickerProviderStateMixin { void initState() { controller = AnimationController( vsync: this, - duration: Duration(seconds: 2), - reverseDuration: Duration(seconds: 2)) + duration: const Duration(seconds: 2), + reverseDuration: const Duration(seconds: 2)) ..forward() ..repeat(reverse: true); animationStar = @@ -248,8 +247,6 @@ class _StarState extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { - final ThemeModel themeProvider = context.watch(); - return ValueListenableBuilder?>( valueListenable: opacity, builder: diff --git a/lib/components/home-card.dart b/lib/components/home_card.dart similarity index 86% rename from lib/components/home-card.dart rename to lib/components/home_card.dart index 906d3c6..18421a5 100644 --- a/lib/components/home-card.dart +++ b/lib/components/home_card.dart @@ -3,13 +3,15 @@ import 'package:flutter/material.dart'; class HomeCard extends StatelessWidget { const HomeCard({ + Key? key, required this.image, required this.page, required this.tag, - }); + }) : super(key: key); final String image; final Widget page; final String tag; + @override Widget build(BuildContext context) { return OpenContainer( @@ -18,25 +20,25 @@ class HomeCard extends StatelessWidget { return page; }, openShape: const RoundedRectangleBorder( - borderRadius: const BorderRadius.all( + borderRadius: BorderRadius.all( Radius.circular(20), ), ), tappable: false, closedColor: Colors.transparent, closedShape: const RoundedRectangleBorder( - borderRadius: const BorderRadius.all( + borderRadius: BorderRadius.all( Radius.circular(20), ), ), closedElevation: 0.0, openElevation: 0.0, - transitionDuration: Duration(milliseconds: 400), + transitionDuration: const Duration(milliseconds: 400), closedBuilder: (BuildContext _, VoidCallback openContainer) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), - boxShadow: [ + boxShadow: const [ BoxShadow( blurRadius: 8, color: Colors.black12, @@ -44,7 +46,7 @@ class HomeCard extends StatelessWidget { ], ), width: double.infinity, - margin: EdgeInsets.symmetric(horizontal: 20, vertical: 8), + margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: GestureDetector( child: SizedBox( height: MediaQuery.of(context).size.height * 0.25, diff --git a/lib/components/image_button.dart b/lib/components/image_button.dart index 38fdc13..85da6bd 100644 --- a/lib/components/image_button.dart +++ b/lib/components/image_button.dart @@ -23,8 +23,7 @@ class ImageButton extends StatelessWidget { height: height, child: Material( type: MaterialType.transparency, - shape: - RoundedRectangleBorder(borderRadius: new BorderRadius.circular(25)), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)), child: SizedBox( child: InkWell( highlightColor: Colors.black.withOpacity(0.1), diff --git a/lib/components/list_item.dart b/lib/components/list_item.dart index 78f9ab7..c045222 100644 --- a/lib/components/list_item.dart +++ b/lib/components/list_item.dart @@ -18,7 +18,7 @@ class ListItem extends StatelessWidget { child: Material( color: Theme.of(context).brightness == Brightness.light ? Colors.grey[100] - : Color(0xff33477f).withOpacity(0.2), + : const Color(0xff33477f).withOpacity(0.2), borderRadius: BorderRadius.circular(8), clipBehavior: Clip.hardEdge, child: ListTile( @@ -33,7 +33,7 @@ class ListItem extends StatelessWidget { size: 22, color: Theme.of(context).brightness == Brightness.light ? Colors.grey[300] - : Color(0xff33477f), + : const Color(0xff33477f), ), leading: Image.asset( icon, @@ -41,14 +41,14 @@ class ListItem extends StatelessWidget { ), ), ), - margin: EdgeInsets.only( + margin: const EdgeInsets.only( top: 5.0, bottom: 5.0, left: 10.0, right: 10.0, ), ), - Padding( + const Padding( child: Divider(), padding: EdgeInsets.symmetric(horizontal: 30.0), ) diff --git a/lib/components/logo.dart b/lib/components/logo.dart index a54fb9c..0542638 100644 --- a/lib/components/logo.dart +++ b/lib/components/logo.dart @@ -4,7 +4,7 @@ import 'package:noor/exports/controllers.dart' show ThemeModel; import 'package:provider/provider.dart'; class NoorLogo extends StatelessWidget { - const NoorLogo(this.size); + const NoorLogo({Key? key, required this.size}) : super(key: key); final double size; @override diff --git a/lib/components/noor_icons.dart b/lib/components/noor_icons.dart index 8257912..15da3bd 100644 --- a/lib/components/noor_icons.dart +++ b/lib/components/noor_icons.dart @@ -11,7 +11,7 @@ /// fonts: /// - asset: fonts/NoorIcons.ttf /// -/// +/// /// import 'package:flutter/widgets.dart'; @@ -24,22 +24,22 @@ class NoorIcons { static const IconData fav = IconData(0xe801, fontFamily: _kFontFam); static const IconData settings = IconData(0xe802, fontFamily: _kFontFam); static const IconData counter = IconData(0xe803, fontFamily: _kFontFam); - static const IconData font_type = IconData(0xe804, fontFamily: _kFontFam); - static const IconData erase_counter = IconData(0xe805, fontFamily: _kFontFam); - static const IconData font_size = IconData(0xe807, fontFamily: _kFontFam); + static const IconData fontType = IconData(0xe804, fontFamily: _kFontFam); + static const IconData eraseCounter = IconData(0xe805, fontFamily: _kFontFam); + static const IconData fontSize = IconData(0xe807, fontFamily: _kFontFam); static const IconData tashkeel = IconData(0xe808, fontFamily: _kFontFam); - static const IconData auto_jump = IconData(0xe809, fontFamily: _kFontFam); + static const IconData autoJump = IconData(0xe809, fontFamily: _kFontFam); static const IconData vibrate = IconData(0xe80a, fontFamily: _kFontFam); static const IconData press = IconData(0xe80b, fontFamily: _kFontFam); static const IconData done = IconData(0xe80c, fontFamily: _kFontFam); - static const IconData morning_noti = IconData(0xe80d, fontFamily: _kFontFam); - static const IconData night_noti = IconData(0xe80e, fontFamily: _kFontFam); - static const IconData night_mode = IconData(0xe80f, fontFamily: _kFontFam); - static const IconData light_mode = IconData(0xe810, fontFamily: _kFontFam); + static const IconData morningNoti = IconData(0xe80d, fontFamily: _kFontFam); + static const IconData nightNoti = IconData(0xe80e, fontFamily: _kFontFam); + static const IconData nightMode = IconData(0xe80f, fontFamily: _kFontFam); + static const IconData lightMode = IconData(0xe810, fontFamily: _kFontFam); static const IconData share = IconData(0xe812, fontFamily: _kFontFam); static const IconData rate = IconData(0xe813, fontFamily: _kFontFam); static const IconData email = IconData(0xe815, fontFamily: _kFontFam); - static const IconData system_mode = IconData(0xe816, fontFamily: _kFontFam); + static const IconData systemMode = IconData(0xe816, fontFamily: _kFontFam); static const IconData back = IconData(0xe817, fontFamily: _kFontFam); static const IconData all = IconData(0xe818, fontFamily: _kFontFam); static const IconData sunnah = IconData(0xe819, fontFamily: _kFontFam); diff --git a/lib/components/noor_settings_icons.dart b/lib/components/noor_settings_icons.dart index 5e02ce9..b98c75e 100644 --- a/lib/components/noor_settings_icons.dart +++ b/lib/components/noor_settings_icons.dart @@ -13,6 +13,8 @@ /// /// /// +// ignore_for_file: constant_identifier_names + import 'package:flutter/widgets.dart'; class NoorSettingsIcons { diff --git a/lib/constants/categories.dart b/lib/constants/categories.dart index e3fcd26..3f25565 100644 --- a/lib/constants/categories.dart +++ b/lib/constants/categories.dart @@ -1,10 +1,10 @@ -enum NoorCategory { ATHKAR, QURAAN, SUNNAH, RUQIYA, MYAD3YAH, ALLAHNAME } +enum NoorCategory { athkar, quraan, sunnah, ruqiya, myad3yah, allahname } const Map categoryTitle = { - NoorCategory.ATHKAR: 'الأذكار', - NoorCategory.QURAAN: 'أدعية من القرآن الكريم', - NoorCategory.SUNNAH: 'أدعية من السنة النبوية', - NoorCategory.RUQIYA: 'الرقية الشرعية', - NoorCategory.MYAD3YAH: 'أدعيتي', - NoorCategory.ALLAHNAME: 'أسماء الله الحسنى', + NoorCategory.athkar: 'الأذكار', + NoorCategory.quraan: 'أدعية من القرآن الكريم', + NoorCategory.sunnah: 'أدعية من السنة النبوية', + NoorCategory.ruqiya: 'الرقية الشرعية', + NoorCategory.myad3yah: 'أدعيتي', + NoorCategory.allahname: 'أسماء الله الحسنى', }; diff --git a/lib/constants/colors.dart b/lib/constants/colors.dart new file mode 100644 index 0000000..ed22c18 --- /dev/null +++ b/lib/constants/colors.dart @@ -0,0 +1,22 @@ +class NoorColors { + static LightModeColors get light => LightModeColors(); + static DarkModeColors get dark => DarkModeColors(); + + final int primary = 0xff6db7e5; + final int subhaListItemBg = 0; + final int subhaLockBg = 0; +} + +class DarkModeColors extends NoorColors { + @override + int get subhaListItemBg => 0xff1D274C; + @override + int get subhaLockBg => 0xff375089; +} + +class LightModeColors extends NoorColors { + @override + int get subhaListItemBg => 0xffffffff; + @override + int get subhaLockBg => 0xffb3b3b3; +} diff --git a/lib/constants/icons.dart b/lib/constants/icons.dart index 3ac4451..ced0a39 100644 --- a/lib/constants/icons.dart +++ b/lib/constants/icons.dart @@ -6,6 +6,12 @@ class NoorIcons { static String subha = 'assets/icons/bottom-nav/${prefix}subha.svg'; static String settings = 'assets/icons/bottom-nav/${prefix}settings.svg'; + // Subha + static String subhaList = 'assets/icons/${prefix}subha_list.svg'; + static String subhaReset = 'assets/icons/${prefix}subha_reset.svg'; + static String subhaLock = 'assets/icons/${prefix}subha_lock.svg'; + + // Settings static String fontType = 'assets/icons/settings/${prefix}font-type.svg'; static String fontSize = 'assets/icons/settings/${prefix}font-size.svg'; static String tashkeel = 'assets/icons/settings/${prefix}tashkeel.svg'; diff --git a/lib/constants/images.dart b/lib/constants/images.dart index 8a4acd0..43dff83 100644 --- a/lib/constants/images.dart +++ b/lib/constants/images.dart @@ -1,6 +1,6 @@ import 'package:noor/env_config.dart'; -class Images { +abstract class Images { static LightAppImages get light => LightAppImages(); static DarkAppImages get dark => DarkAppImages(); @@ -21,6 +21,7 @@ class Images { final String igButton = ''; final String myAd3yahBg = ''; final String addMyAd3yah = ''; + final String subhaBg = ''; // Bellow images aren't tied to theme mode // So they can be static and accessed directly @@ -73,60 +74,98 @@ class Images { } } -class LightAppImages extends Images { - final String logo = 'assets/images/${prefix}logo-light.svg'; +class LightAppImages implements Images { + @override + String get logo => 'assets/images/${prefix}logo-light.svg'; // Categories icons - final String athkarTitleIcon = 'assets/icons/titles/${prefix}athkar.png'; - final String quraanTitleIcon = 'assets/icons/titles/${prefix}quraan.png'; - final String sunnahTitleIcon = 'assets/icons/titles/${prefix}sunnah.png'; - final String ruqyaTitleIcon = 'assets/icons/titles/${prefix}ruqiya.png'; - final String myAd3yahTitleIcon = 'assets/icons/titles/${prefix}myAd3yah.png'; - final String allahNamesTitleIcon = + @override + String get athkarTitleIcon => 'assets/icons/titles/${prefix}athkar.png'; + @override + String get quraanTitleIcon => 'assets/icons/titles/${prefix}quraan.png'; + @override + String get sunnahTitleIcon => 'assets/icons/titles/${prefix}sunnah.png'; + @override + String get ruqyaTitleIcon => 'assets/icons/titles/${prefix}ruqiya.png'; + @override + String get myAd3yahTitleIcon => 'assets/icons/titles/${prefix}myAd3yah.png'; + @override + String get allahNamesTitleIcon => 'assets/icons/titles/${prefix}allah-names.png'; // Home assets - final String homeHeader = 'assets/images/home-header/header-light.png'; - final String athkarCard = - 'assets/images/home-cards/light/${prefix}Athkar.png'; - final String ad3yahCard = - 'assets/images/home-cards/light/${prefix}Ad3yah.png'; - final String allahNamesCard = + @override + String get homeHeader => 'assets/images/home-header/header-light.png'; + @override + String get athkarCard => 'assets/images/home-cards/light/${prefix}Athkar.png'; + @override + String get ad3yahCard => 'assets/images/home-cards/light/${prefix}Ad3yah.png'; + @override + String get allahNamesCard => 'assets/images/home-cards/light/${prefix}AllahNames.png'; // Backgrounds - final String noAd3yah = 'assets/images/backgrounds/NoAd3yah.png'; - final String noAd3yahFav = 'assets/images/backgrounds/NoAd3yahFav.png'; - final String myAd3yahBg = 'assets/images/backgrounds/${prefix}myAd3yahBg.svg'; + @override + String get noAd3yah => 'assets/images/backgrounds/NoAd3yah.png'; + @override + String get noAd3yahFav => 'assets/images/backgrounds/NoAd3yahFav.png'; + @override + String get myAd3yahBg => 'assets/images/backgrounds/${prefix}myAd3yahBg.svg'; // Buttons - final String twitterButton = 'assets/images/social-buttons/twitter-light.png'; - final String igButton = 'assets/images/social-buttons/ig-light.png'; - final String addMyAd3yah = 'assets/icons/${prefix}addDo3aa.png'; + @override + String get twitterButton => 'assets/images/social-buttons/twitter-light.png'; + @override + String get igButton => 'assets/images/social-buttons/ig-light.png'; + @override + String get addMyAd3yah => 'assets/icons/${prefix}addDo3aa.png'; + + @override + String get subhaBg => 'assets/images/backgrounds/SubhaLightBg.svg'; } -class DarkAppImages extends Images { - final String logo = 'assets/images/${prefix}logo-dark.svg'; +class DarkAppImages implements Images { + @override + String get logo => 'assets/images/${prefix}logo-dark.svg'; // Categories icons - final String athkarTitleIcon = 'assets/icons/titles/${prefix}athkar-dark.png'; - final String quraanTitleIcon = 'assets/icons/titles/${prefix}quraan-dark.png'; - final String sunnahTitleIcon = 'assets/icons/titles/${prefix}sunnah-dark.png'; - final String ruqyaTitleIcon = 'assets/icons/titles/${prefix}ruqiya-dark.png'; - final String myAd3yahTitleIcon = + @override + String get athkarTitleIcon => 'assets/icons/titles/${prefix}athkar-dark.png'; + @override + String get quraanTitleIcon => 'assets/icons/titles/${prefix}quraan-dark.png'; + @override + String get sunnahTitleIcon => 'assets/icons/titles/${prefix}sunnah-dark.png'; + @override + String get ruqyaTitleIcon => 'assets/icons/titles/${prefix}ruqiya-dark.png'; + @override + String get myAd3yahTitleIcon => 'assets/icons/titles/${prefix}myAd3yah-dark.png'; - final String allahNamesTitleIcon = + @override + String get allahNamesTitleIcon => 'assets/icons/titles/${prefix}allah-names-dark.png'; // Home assets - final String homeHeader = 'assets/images/home-header/header-dark.png'; - final String athkarCard = 'assets/images/home-cards/dark/${prefix}Athkar.png'; - final String ad3yahCard = 'assets/images/home-cards/dark/${prefix}Ad3yah.png'; - final String allahNamesCard = + @override + String get homeHeader => 'assets/images/home-header/header-dark.png'; + @override + String get athkarCard => 'assets/images/home-cards/dark/${prefix}Athkar.png'; + @override + String get ad3yahCard => 'assets/images/home-cards/dark/${prefix}Ad3yah.png'; + @override + String get allahNamesCard => 'assets/images/home-cards/dark/${prefix}AllahNames.png'; // Backgrounds - final String noAd3yah = 'assets/images/backgrounds/NoAd3yahNight.png'; - final String noAd3yahFav = 'assets/images/backgrounds/NoAd3yahFavNight.png'; - final String myAd3yahBg = + @override + String get noAd3yah => 'assets/images/backgrounds/NoAd3yahNight.png'; + @override + String get noAd3yahFav => 'assets/images/backgrounds/NoAd3yahFavNight.png'; + @override + String get myAd3yahBg => 'assets/images/backgrounds/${prefix}myAd3yahBgDark.svg'; // Buttons - final String twitterButton = 'assets/images/social-buttons/twitter-dark.png'; - final String igButton = 'assets/images/social-buttons/ig-dark.png'; - final String addMyAd3yah = 'assets/icons/${prefix}addDo3aa.png'; + @override + String get twitterButton => 'assets/images/social-buttons/twitter-dark.png'; + @override + String get igButton => 'assets/images/social-buttons/ig-dark.png'; + @override + String get addMyAd3yah => 'assets/icons/${prefix}addDo3aa.png'; + + @override + String get subhaBg => 'assets/images/backgrounds/SubhaDarkBg.svg'; } diff --git a/lib/constants/strings.dart b/lib/constants/strings.dart new file mode 100644 index 0000000..a5ddbaa --- /dev/null +++ b/lib/constants/strings.dart @@ -0,0 +1,11 @@ +class Strings { + static const shareText = + 'يضمُّ تطبيق نُور العديد من الأذكار والأدعية الواردة في كتاب حصن المسلم.' + ' كما يحتوي التطبيق على أدعية من القرآن الكريم والسنة النبوية. والعديد من المميزات. ' + '\n https://play.google.com/store/apps/details?id=com.noor.sa'; + static const shareSubject = 'تطبيق نُور'; + static const noorThekrDefault = + 'قال تعالى: ﴿فَاذكُروني أَذكُركُم ﴾ [البقرة: ١٥٢]'; + static const confirmDeleteContent = 'هل أنت مُتأكد من رغبتك في الحذف؟'; + static const confirmDeleteTitle = 'تأكيد الحذف'; +} diff --git a/lib/constants/theme.dart b/lib/constants/theme.dart index 9e509aa..4e3d215 100644 --- a/lib/constants/theme.dart +++ b/lib/constants/theme.dart @@ -1,13 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:noor/constants/colors.dart'; -const double kContentFontSize = 16.0; +const kContentFontSize = 16.0; +const viewPadding = 15.0; ThemeData lightTheme() => ThemeData( brightness: Brightness.light, - appBarTheme: AppBarTheme(brightness: Brightness.light), + appBarTheme: + const AppBarTheme(systemOverlayStyle: SystemUiOverlayStyle.light), fontFamily: 'SST Arabic', - primaryColor: Color(0xff6db7e5), - pageTransitionsTheme: PageTransitionsTheme( + primaryColor: Color(NoorColors.light.primary), + pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), }, @@ -15,73 +19,114 @@ ThemeData lightTheme() => ThemeData( unselectedWidgetColor: Colors.grey[300], canvasColor: Colors.white, dividerColor: Colors.grey[300], - accentColor: Color(0xff6f85d5), - dialogTheme: DialogTheme(backgroundColor: Colors.white), + dialogTheme: const DialogTheme(backgroundColor: Colors.white), highlightColor: Colors.black.withOpacity(0.1), splashColor: Colors.black.withOpacity(0.1), + selectedRowColor: const Color(0xffB3B3FF), textTheme: TextTheme( - bodyText1: TextStyle( + bodyText1: const TextStyle( fontSize: 16, height: 1.6, color: Colors.black, fontWeight: FontWeight.normal, ), - subtitle1: TextStyle( + subtitle1: const TextStyle( color: Color(0xff6f85d5), fontWeight: FontWeight.bold, fontSize: 14, ), - headline1: TextStyle( + subtitle2: TextStyle( + fontSize: 16, + height: 1.5, + color: Colors.lightBlue[100], + fontWeight: FontWeight.bold, + ), + headline1: const TextStyle( fontSize: 16, height: 1.5, color: Color(0xff6f85d5), fontWeight: FontWeight.bold, ), - headline2: TextStyle( + headline2: const TextStyle( fontSize: 16, height: 1.5, color: Colors.white, ), - button: TextStyle( + button: const TextStyle( fontSize: 14, color: Color(0xff6f85d5), height: 1, ), ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0.0), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.disabled)) { + return const Color(0xff6f85d5).withOpacity(0.7); + } + return const Color(0xff6f85d5); + }, + ), + foregroundColor: MaterialStateProperty.resolveWith( + (Set states) { + return (states.contains(MaterialState.disabled)) + ? Colors.white30 + : Colors.lightBlue[100]; + }, + ), + textStyle: MaterialStateProperty.all( + const TextStyle( + fontFamily: 'SST Arabic', + fontWeight: FontWeight.w600, + fontSize: 12, + height: 1, + ), + ), + overlayColor: MaterialStateProperty.all(Colors.white24), + ), + ), scrollbarTheme: ScrollbarThemeData( - radius: Radius.circular(5), + radius: const Radius.circular(5), thumbColor: MaterialStateProperty.all(Colors.grey[300]), ), iconTheme: IconThemeData(color: Colors.grey[400], size: 30), inputDecorationTheme: InputDecorationTheme( fillColor: Colors.grey[300], filled: true, - hintStyle: TextStyle(color: Colors.grey), + hintStyle: const TextStyle(color: Colors.grey), ), - buttonColor: Color(0xff6f85d5), cardColor: Colors.grey[200], + colorScheme: ColorScheme.fromSwatch().copyWith( + secondary: const Color(0xff6f85d5), + brightness: Brightness.light, + outline: const Color(0xff6f85d5), + ), ); ThemeData darkTheme() => ThemeData( brightness: Brightness.dark, - appBarTheme: AppBarTheme(brightness: Brightness.dark), + appBarTheme: + const AppBarTheme(systemOverlayStyle: SystemUiOverlayStyle.dark), fontFamily: 'SST Arabic', - primaryColor: Color(0xff6db7e5), - pageTransitionsTheme: PageTransitionsTheme( + primaryColor: Color(NoorColors.dark.primary), + selectedRowColor: const Color(0xff33477F), + pageTransitionsTheme: const PageTransitionsTheme( builders: { TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), }, ), - canvasColor: Color(0xff10122C), - dividerColor: Color(0xff3C387B), - iconTheme: IconThemeData(color: Color(0xff3C387B), size: 30), + canvasColor: const Color(0xff10122C), + dividerColor: const Color(0xff3C387B), + iconTheme: const IconThemeData(color: Color(0xff3C387B), size: 30), unselectedWidgetColor: Colors.grey[300], - inputDecorationTheme: InputDecorationTheme( + inputDecorationTheme: const InputDecorationTheme( fillColor: Colors.white24, filled: true, hintStyle: TextStyle(color: Colors.white38), ), - textTheme: TextTheme( + textTheme: const TextTheme( bodyText1: TextStyle( fontSize: 16, height: 1.6, @@ -93,6 +138,12 @@ ThemeData darkTheme() => ThemeData( fontWeight: FontWeight.bold, fontSize: 14, ), + subtitle2: TextStyle( + fontSize: 16, + height: 1.5, + color: Color(0xff6f85d5), + fontWeight: FontWeight.bold, + ), headline1: TextStyle( fontSize: 16, height: 1.5, @@ -110,16 +161,48 @@ ThemeData darkTheme() => ThemeData( height: 1, ), ), - accentColor: Colors.white, - buttonColor: Color(0xff6f85d5), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0.0), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.disabled)) { + return const Color(0xff6f85d5).withOpacity(0.6); + } + return const Color(0xff6f85d5); + }, + ), + foregroundColor: MaterialStateProperty.resolveWith( + (Set states) { + return (states.contains(MaterialState.disabled)) + ? Colors.white30 + : Colors.lightBlue[100]; + }, + ), + textStyle: MaterialStateProperty.all( + const TextStyle( + fontFamily: 'SST Arabic', + fontWeight: FontWeight.w600, + fontSize: 12, + height: 1, + ), + ), + overlayColor: MaterialStateProperty.all(Colors.white10), + ), + ), splashColor: Colors.black.withOpacity(0.1), - highlightColor: Color(0xff3C387B).withOpacity(0.5), - dialogTheme: DialogTheme(backgroundColor: Color(0xff1B2349)), - cardColor: Color(0xff10122C), + highlightColor: const Color(0xff3C387B).withOpacity(0.5), + dialogTheme: const DialogTheme(backgroundColor: Color(0xff1B2349)), + cardColor: const Color(0xff10122C), scrollbarTheme: ScrollbarThemeData( - radius: Radius.circular(5), + radius: const Radius.circular(5), thumbColor: MaterialStateProperty.all( - Color(0xff3C387B).withOpacity(0.5), + const Color(0xff3C387B).withOpacity(0.5), ), ), + colorScheme: ColorScheme.fromSwatch().copyWith( + secondary: Colors.white, + brightness: Brightness.dark, + outline: const Color(0xff6f85d5), + ), ); diff --git a/lib/constants/titles.dart b/lib/constants/titles.dart index ca61193..aba9cad 100644 --- a/lib/constants/titles.dart +++ b/lib/constants/titles.dart @@ -4,5 +4,3 @@ class Titles { static const String ruqya = 'الرقية الشرعية'; static const String myAd3yah = 'أدعيتي'; } - - diff --git a/lib/controllers/data_controller.dart b/lib/controllers/data_controller.dart index b54b746..b1382e6 100644 --- a/lib/controllers/data_controller.dart +++ b/lib/controllers/data_controller.dart @@ -10,7 +10,7 @@ import 'package:noor/exports/services.dart' class DataController { static Future init() async { - final List data = await JsonService.init(); + final List data = await JsonService.instance.init(); final DataModel dataModel = GetIt.I(); if (dataModel.athkar.isEmpty) { int section = 0; @@ -44,9 +44,9 @@ class DataController { if (dataModel.quraan.isEmpty) { dataModel.quraan = data[1] .map((dynamic e) { - e['category'] = NoorCategory.QURAAN; + e['category'] = NoorCategory.quraan; e['ribbon'] = Ribbon.ribbon2; - e['sectionName'] = categoryTitle[NoorCategory.QURAAN]; + e['sectionName'] = categoryTitle[NoorCategory.quraan]; return Doaa.fromMap(e); }) @@ -56,9 +56,9 @@ class DataController { if (dataModel.sunnah.isEmpty) { dataModel.sunnah = data[2] .map((dynamic e) { - e['category'] = NoorCategory.SUNNAH; + e['category'] = NoorCategory.sunnah; e['ribbon'] = Ribbon.ribbon3; - e['sectionName'] = categoryTitle[NoorCategory.SUNNAH]; + e['sectionName'] = categoryTitle[NoorCategory.sunnah]; return Doaa.fromMap(e); }) @@ -69,8 +69,8 @@ class DataController { dataModel.ruqiya = data[3] .map((dynamic e) { e['ribbon'] = Ribbon.ribbon4; - e['category'] = NoorCategory.RUQIYA; - e['sectionName'] = categoryTitle[NoorCategory.RUQIYA]; + e['category'] = NoorCategory.ruqiya; + e['sectionName'] = categoryTitle[NoorCategory.ruqiya]; return Doaa.fromMap(e); }) @@ -101,7 +101,7 @@ class DataController { updateFavList(); } - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); return DataController(); } @@ -178,7 +178,7 @@ class DataController { // Update underlying fav prefs list for persistency SharedPrefsService.putStringList('fav', favPrefs); - if (element.category == NoorCategory.MYAD3YAH) { + if (element.category == NoorCategory.myad3yah) { await DBService.db.update(element); } @@ -195,7 +195,7 @@ class DataController { // Update underlying fav prefs list for persistency SharedPrefsService.putStringList('fav', favPrefs); - if (element.category == NoorCategory.MYAD3YAH) { + if (element.category == NoorCategory.myad3yah) { await DBService.db.update(element); } diff --git a/lib/exports/components.dart b/lib/exports/components.dart index bf3941d..ffe5632 100644 --- a/lib/exports/components.dart +++ b/lib/exports/components.dart @@ -3,7 +3,7 @@ export 'package:noor/components/allah_names_title.dart'; export 'package:noor/components/athkar_card.dart'; export 'package:noor/components/athkar_title.dart'; export 'package:noor/components/bottom_nav.dart'; -export 'package:noor/components/home-card.dart'; +export 'package:noor/components/home_card.dart'; export 'package:noor/components/close_button.dart'; export 'package:noor/components/delete_dialog.dart'; export 'package:noor/components/custom_scroll_bhaviour.dart'; diff --git a/lib/exports/constants.dart b/lib/exports/constants.dart index 5e7674e..aeb300b 100644 --- a/lib/exports/constants.dart +++ b/lib/exports/constants.dart @@ -6,3 +6,4 @@ export 'package:noor/constants/theme.dart'; export 'package:noor/constants/titles.dart'; export 'package:noor/constants/categories.dart'; export 'package:noor/constants/icons.dart'; +export 'package:noor/constants/strings.dart'; diff --git a/lib/exports/controllers.dart b/lib/exports/controllers.dart index c1d4217..b8498f9 100644 --- a/lib/exports/controllers.dart +++ b/lib/exports/controllers.dart @@ -1,3 +1,3 @@ export 'package:noor/controllers/data_controller.dart'; export 'package:noor/models/settings.dart'; -export 'package:noor/models/theme_model.dart'; +export 'package:noor/models/theme.dart'; diff --git a/lib/exports/pages.dart b/lib/exports/pages.dart index 9e96db4..8aa0af3 100644 --- a/lib/exports/pages.dart +++ b/lib/exports/pages.dart @@ -1,19 +1,21 @@ export 'package:noor/pages/root.dart'; export 'package:noor/pages/splash.dart'; -export 'package:noor/pages/tabs/1_home/home.dart'; +export 'package:noor/pages/tabs/page_1_home/home.dart'; -export 'package:noor/pages/tabs/1_home/athkar.dart'; -export 'package:noor/pages/tabs/1_home/athkar_expanded.dart'; +export 'package:noor/pages/tabs/page_1_home/athkar.dart'; +export 'package:noor/pages/tabs/page_1_home/athkar_expanded.dart'; -export 'package:noor/pages/tabs/1_home/ad3yah.dart'; -export 'package:noor/pages/tabs/1_home/ad3yah_expanded.dart'; +export 'package:noor/pages/tabs/page_1_home/ad3yah.dart'; +export 'package:noor/pages/tabs/page_1_home/ad3yah_expanded.dart'; -export 'package:noor/pages/tabs/1_home/allah_names.dart'; -export 'package:noor/pages/tabs/1_home/allah_names_expanded.dart'; +export 'package:noor/pages/tabs/page_1_home/allah_names.dart'; +export 'package:noor/pages/tabs/page_1_home/allah_names_expanded.dart'; -export 'package:noor/pages/tabs/1_home/my_ad3yah.dart'; +export 'package:noor/pages/tabs/page_1_home/my_ad3yah.dart'; -export 'package:noor/pages/tabs/2_fav.dart'; -export 'package:noor/pages/tabs/3_counter.dart'; -export 'package:noor/pages/tabs/4_settings.dart'; \ No newline at end of file +export 'package:noor/pages/tabs/page_2_fav.dart'; + +export 'package:noor/pages/tabs/page_3_counter/counter_main_view.dart'; + +export 'package:noor/pages/tabs/page_4_settings.dart'; diff --git a/lib/exports/services.dart b/lib/exports/services.dart index 824859f..34e4008 100644 --- a/lib/exports/services.dart +++ b/lib/exports/services.dart @@ -1,4 +1,4 @@ export 'package:noor/services/db.dart'; export 'package:noor/services/fcm.dart'; export 'package:noor/services/json.dart'; -export 'package:noor/services/prefs.dart'; \ No newline at end of file +export 'package:noor/services/prefs.dart'; diff --git a/lib/main.dart b/lib/main.dart index fdec655..cd04f7f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:noor/exports/models.dart' show DataModel; import 'package:noor/exports/services.dart' show DBService, SharedPrefsService, FCMService; import 'package:noor/exports/models.dart' show SettingsModel; +import 'package:noor/pages/tabs/page_3_counter/counter_view_model.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -20,6 +21,8 @@ void main() async { GetIt.I.registerSingleton(DataModel()); GetIt.I.registerSingleton(SettingsModel()); GetIt.I.registerSingletonAsync(() => DataController.init()); + GetIt.I + .registerSingletonAsync(() => CounterViewModel.init()); - runApp(NoorApp()); + runApp(const NoorApp()); } diff --git a/lib/models/allah_name.dart b/lib/models/allah_name.dart index d77e0a7..54906d0 100644 --- a/lib/models/allah_name.dart +++ b/lib/models/allah_name.dart @@ -6,7 +6,7 @@ class AllahName { late final String text; final String sectionName = 'أسماء الله الحسنى'; final String ribbon = Ribbon.ribbon6; - final NoorCategory category = NoorCategory.ALLAHNAME; + final NoorCategory category = NoorCategory.allahname; late bool isFav; late final int section; late final bool inApp; diff --git a/lib/models/data.dart b/lib/models/data.dart index e02785f..bd8028b 100644 --- a/lib/models/data.dart +++ b/lib/models/data.dart @@ -4,7 +4,6 @@ import 'package:noor/models/doaa.dart'; import 'package:noor/models/thekr.dart'; class DataModel extends ChangeNotifier { - List _athkar = []; List _quraan = []; List _sunnah = []; diff --git a/lib/models/thekr.dart b/lib/models/thekr.dart index a96f4f7..e3307b7 100644 --- a/lib/models/thekr.dart +++ b/lib/models/thekr.dart @@ -11,7 +11,7 @@ class Thekr { late final int counter; - final NoorCategory category = NoorCategory.ATHKAR; + final NoorCategory category = NoorCategory.athkar; final String ribbon = Ribbon.ribbon1; Thekr._( diff --git a/lib/models/theme_model.dart b/lib/models/theme.dart similarity index 71% rename from lib/models/theme_model.dart rename to lib/models/theme.dart index fc14cf1..e506942 100644 --- a/lib/models/theme_model.dart +++ b/lib/models/theme.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:noor/exports/constants.dart' show Images; import 'package:noor/exports/services.dart' show SharedPrefsService; +import '../constants/colors.dart'; + class ThemeModel with ChangeNotifier { String _userTheme = SharedPrefsService.getString('theme'); @@ -42,4 +44,22 @@ class ThemeModel with ChangeNotifier { } } } + + NoorColors get colors { + switch (userTheme) { + case 'dark_theme': + return NoorColors.dark; + case 'light_theme': + return NoorColors.light; + default: + { + switch (brightness) { + case Brightness.dark: + return NoorColors.dark; + default: + return NoorColors.light; + } + } + } + } } diff --git a/lib/pages/root.dart b/lib/pages/root.dart index 3afd4e0..8b9f18c 100644 --- a/lib/pages/root.dart +++ b/lib/pages/root.dart @@ -4,27 +4,28 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:noor/exports/components.dart' show BottomNav; import 'package:noor/exports/pages.dart' - show Home, Favorite, CounterPage, Settings, AthkarList; + show Home, Favorite, CounterView, Settings, AthkarList; class RootHome extends StatefulWidget { - RootHome({Key? key}) : super(key: key); + const RootHome({Key? key}) : super(key: key); + @override _RootHomeState createState() => _RootHomeState(); } class _RootHomeState extends State with TickerProviderStateMixin, WidgetsBindingObserver { FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - new FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); String? payload; @override void initState() { super.initState(); AndroidInitializationSettings initializationSettingsAndroid = - new AndroidInitializationSettings('ic_notification'); + const AndroidInitializationSettings('ic_notification'); IOSInitializationSettings initializationSettingsIOS = - IOSInitializationSettings(); + const IOSInitializationSettings(); InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS); flutterLocalNotificationsPlugin.initialize(initializationSettings, @@ -35,11 +36,11 @@ class _RootHomeState extends State setState(() { this.payload = payload; }); - print(payload); + Navigator.push( context, - new MaterialPageRoute( - builder: (BuildContext context) => new AthkarList( + MaterialPageRoute( + builder: (BuildContext context) => AthkarList( index: payload == 'الصباح' ? 0 : 26, ), ), @@ -57,12 +58,12 @@ class _RootHomeState extends State }, ), body: PageView( - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), controller: controller, - children: [ + children: const [ Home(), Favorite(), - CounterPage(), + CounterView(), Settings(), ], ), diff --git a/lib/pages/splash.dart b/lib/pages/splash.dart index 824ec11..6c129e5 100644 --- a/lib/pages/splash.dart +++ b/lib/pages/splash.dart @@ -5,7 +5,7 @@ import 'package:noor/exports/pages.dart' show RootHome; import 'package:noor/exports/components.dart' show NoorLogo; class SplashScreen extends StatefulWidget { - SplashScreen({Key? key}) : super(key: key); + const SplashScreen({Key? key}) : super(key: key); @override _SplashScreenState createState() => _SplashScreenState(); @@ -15,7 +15,7 @@ class _SplashScreenState extends State { double logoOpacity = 0.0; @override void initState() { - Future.delayed(Duration(milliseconds: 300), () { + Future.delayed(const Duration(milliseconds: 300), () { setState(() { logoOpacity = 1.0; }); @@ -26,10 +26,10 @@ class _SplashScreenState extends State { @override void didChangeDependencies() async { await Future.value(GetIt.I.allReady()); - await Future.delayed(Duration(milliseconds: 500)); + await Future.delayed(const Duration(milliseconds: 500)); Navigator.of(context).pushReplacement( MaterialPageRoute( - builder: (_) => RootHome(), + builder: (_) => const RootHome(), ), ); @@ -41,9 +41,9 @@ class _SplashScreenState extends State { return Material( child: Center( child: AnimatedOpacity( - duration: Duration(milliseconds: 500), + duration: const Duration(milliseconds: 500), opacity: logoOpacity, - child: NoorLogo(80), + child: const NoorLogo(size: 80), ), ), ); diff --git a/lib/pages/tabs/3_counter.dart b/lib/pages/tabs/3_counter.dart deleted file mode 100644 index baabf47..0000000 --- a/lib/pages/tabs/3_counter.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:provider/provider.dart'; - -import 'package:noor/exports/components.dart' show NoorIcons; -import 'package:noor/exports/services.dart' show SharedPrefsService; -import 'package:noor/exports/utils.dart' show ToArabicNumbers; -import 'package:noor/exports/controllers.dart' show SettingsModel; - -class CounterPage extends StatefulWidget { - const CounterPage({Key? key}) : super(key: key); - @override - _CounterPageState createState() => _CounterPageState(); -} - -class _CounterPageState extends State { - int counter = SharedPrefsService.getInt('counter'); - - void incrementCounter() { - final SettingsModel settings = context.read(); - - setState(() { - counter++; - }); - - SharedPrefsService.putInt('counter', counter); - - if (settings.vibrateCounter) { - if (counter % 100 == 0) { - switch (settings.vibrationHunderds) { - case 'strong': - HapticFeedback.lightImpact(); - break; - case 'light': - HapticFeedback.heavyImpact(); - break; - case 'none': - break; - default: - HapticFeedback.lightImpact(); - } - } else { - switch (settings.vibrationClickCounter) { - case 'strong': - HapticFeedback.lightImpact(); - break; - case 'light': - HapticFeedback.heavyImpact(); - break; - case 'none': - break; - default: - HapticFeedback.lightImpact(); - } - } - } - } - - void resetCounter() { - setState(() { - counter = 0; - }); - SharedPrefsService.putInt('counter', counter); - } - - LinearGradient lightModeBG = LinearGradient( - colors: [ - Color(0xff5554B0), - Color(0xff6F86D6), - ], - stops: [0.1, 0.9], - begin: Alignment.bottomRight, - end: Alignment.topLeft, - ); - - LinearGradient darkModeBG = LinearGradient( - colors: [ - Color(0xff161A3A), - Color(0xff161A3A), - ], - stops: [0.7, 0.3], - begin: Alignment.bottomRight, - end: Alignment.topLeft, - ); - @override - Widget build(BuildContext context) { - return Scaffold( - body: GestureDetector( - onTap: incrementCounter, - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: Theme.of(context).brightness == Brightness.dark - ? darkModeBG - : lightModeBG), - child: Stack( - children: [ - Positioned( - top: 40, - left: 20, - child: IconButton( - icon: Icon( - NoorIcons.erase_counter, - color: Colors.white, - size: 40, - ), - onPressed: resetCounter, - ), - ), - Align( - alignment: Alignment.center, - child: AnimatedSwitcher( - transitionBuilder: - (Widget child, Animation animation) { - return FadeTransition(child: child, opacity: animation); - }, - duration: Duration(milliseconds: 250), - child: Text( - '$counter'.arabicDigit(), - key: ValueKey(counter), - style: TextStyle( - color: Colors.white, - fontSize: 55, - fontWeight: FontWeight.bold, - ), - ), - ), - ) - ], - ), - ), - ), - ); - } -} diff --git a/lib/pages/tabs/1_home/ad3yah.dart b/lib/pages/tabs/page_1_home/ad3yah.dart similarity index 87% rename from lib/pages/tabs/1_home/ad3yah.dart rename to lib/pages/tabs/page_1_home/ad3yah.dart index c84126e..d62e49e 100644 --- a/lib/pages/tabs/1_home/ad3yah.dart +++ b/lib/pages/tabs/page_1_home/ad3yah.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:provider/provider.dart'; import 'package:noor/exports/pages.dart' show Ad3yahList, MyAd3yah; @@ -8,18 +7,20 @@ import 'package:noor/exports/components.dart' show NoorCloseButton, ListItem; import 'package:noor/exports/controllers.dart' show ThemeModel; class Ad3yah extends StatefulWidget { - const Ad3yah(); + const Ad3yah({Key? key}) : super(key: key); + @override _Ad3yahState createState() => _Ad3yahState(); } class _Ad3yahState extends State with SingleTickerProviderStateMixin { - ScrollController scrollController = new ScrollController(); + ScrollController scrollController = ScrollController(); int index = 0; double currentScroll = 0; double maxHeight = 180; late Animation animation; late AnimationController controller; + @override initState() { super.initState(); @@ -67,7 +68,7 @@ class _Ad3yahState extends State with SingleTickerProviderStateMixin { }, ), ), - Positioned( + const Positioned( left: 10.0, top: 40.0, child: NoorCloseButton(size: 35)), ], ), @@ -75,29 +76,29 @@ class _Ad3yahState extends State with SingleTickerProviderStateMixin { builder: (_, ThemeModel theme, __) { return Expanded( child: ListView( - padding: EdgeInsets.only(top: 15.0), - physics: AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.only(top: 15.0), + physics: const AlwaysScrollableScrollPhysics(), controller: scrollController, children: [ Ad3yahTitleCard( title: Titles.quraan, icon: theme.images.quraanTitleIcon, - category: NoorCategory.QURAAN, + category: NoorCategory.quraan, ), Ad3yahTitleCard( title: Titles.sunnah, icon: theme.images.sunnahTitleIcon, - category: NoorCategory.SUNNAH, + category: NoorCategory.sunnah, ), Ad3yahTitleCard( title: Titles.ruqya, icon: theme.images.ruqyaTitleIcon, - category: NoorCategory.RUQIYA, + category: NoorCategory.ruqiya, ), Ad3yahTitleCard( title: Titles.myAd3yah, icon: theme.images.myAd3yahTitleIcon, - category: NoorCategory.MYAD3YAH, + category: NoorCategory.myad3yah, ), ], ), @@ -128,10 +129,10 @@ class Ad3yahTitleCard extends StatelessWidget { icon: icon, title: title, onTap: () { - if (category == NoorCategory.MYAD3YAH) { + if (category == NoorCategory.myad3yah) { Navigator.of(context).push( MaterialPageRoute( - builder: (_) => MyAd3yah(), + builder: (_) => const MyAd3yah(), fullscreenDialog: true, ), ); diff --git a/lib/pages/tabs/1_home/ad3yah_expanded.dart b/lib/pages/tabs/page_1_home/ad3yah_expanded.dart similarity index 91% rename from lib/pages/tabs/1_home/ad3yah_expanded.dart rename to lib/pages/tabs/page_1_home/ad3yah_expanded.dart index ef06df3..aa8d9fd 100644 --- a/lib/pages/tabs/1_home/ad3yah_expanded.dart +++ b/lib/pages/tabs/page_1_home/ad3yah_expanded.dart @@ -15,6 +15,7 @@ class Ad3yahList extends StatefulWidget { }) : super(key: key); final int index; final NoorCategory category; + @override _Ad3yahListState createState() => _Ad3yahListState(); } @@ -45,7 +46,7 @@ class _Ad3yahListState extends State { bottom: false, child: Container( width: double.infinity, - margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -55,7 +56,8 @@ class _Ad3yahListState extends State { textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline1, ), - NoorCloseButton(color: Theme.of(context).accentColor), + NoorCloseButton( + color: Theme.of(context).colorScheme.secondary), ], ), ), @@ -63,7 +65,7 @@ class _Ad3yahListState extends State { Expanded( child: Scrollbar( child: ScrollablePositionedList.builder( - physics: AlwaysScrollableScrollPhysics(), + physics: const AlwaysScrollableScrollPhysics(), itemCount: data[widget.category.index - 1].length, initialScrollIndex: widget.index, itemScrollController: controller, diff --git a/lib/pages/tabs/1_home/allah_names.dart b/lib/pages/tabs/page_1_home/allah_names.dart similarity index 92% rename from lib/pages/tabs/1_home/allah_names.dart rename to lib/pages/tabs/page_1_home/allah_names.dart index 36396ab..1d5c361 100644 --- a/lib/pages/tabs/1_home/allah_names.dart +++ b/lib/pages/tabs/page_1_home/allah_names.dart @@ -16,7 +16,7 @@ class AllahNames extends StatefulWidget { class _AllahNamesState extends State with SingleTickerProviderStateMixin { - ScrollController scrollController = new ScrollController(); + final scrollController = ScrollController(); double currentScroll = 0; double maxHeight = 180; @@ -25,6 +25,7 @@ class _AllahNamesState extends State int index = 0; + @override initState() { super.initState(); @@ -73,7 +74,7 @@ class _AllahNamesState extends State }, ), ), - Positioned( + const Positioned( left: 10.0, top: 40.0, child: NoorCloseButton(size: 35)), ], ), @@ -83,14 +84,14 @@ class _AllahNamesState extends State return Expanded( child: Scrollbar( child: ListView.builder( - padding: EdgeInsets.symmetric(vertical: 10), - physics: AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 10), + physics: const AlwaysScrollableScrollPhysics(), itemCount: allahNames.length, controller: scrollController, itemBuilder: (BuildContext context, int index) { final AllahName title = allahNames[index]; return ListItem( - title: '${title.name}', + title: title.name, icon: images.allahNamesTitleIcon, onTap: () { Navigator.of(context).push( diff --git a/lib/pages/tabs/1_home/allah_names_expanded.dart b/lib/pages/tabs/page_1_home/allah_names_expanded.dart similarity index 91% rename from lib/pages/tabs/1_home/allah_names_expanded.dart rename to lib/pages/tabs/page_1_home/allah_names_expanded.dart index f8ed7ce..31e3cc3 100644 --- a/lib/pages/tabs/1_home/allah_names_expanded.dart +++ b/lib/pages/tabs/page_1_home/allah_names_expanded.dart @@ -8,7 +8,6 @@ import 'package:noor/exports/components.dart' show NoorCloseButton, CardTemplate, - NoorIcons, NameTitleCard, FavAction, CopyAction, @@ -20,6 +19,7 @@ class AllahNamesList extends StatefulWidget { this.index = 0, }) : super(key: key); final int index; + @override _AllahNamesListState createState() => _AllahNamesListState(); } @@ -41,11 +41,11 @@ class _AllahNamesListState extends State listener.itemPositions.addListener(changeAppBar); }); - animationController = new AnimationController(vsync: this); + animationController = AnimationController(vsync: this); animation = Tween(begin: 0.0, end: 0.1).animate( CurvedAnimation(parent: animationController, curve: Curves.elasticIn), ); - controller = new ItemScrollController(); + controller = ItemScrollController(); } @override @@ -94,11 +94,11 @@ class _AllahNamesListState extends State bottom: false, child: Container( width: double.infinity, - margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox(width: 45), + const SizedBox(width: 45), ChangeNotifierProvider>.value( value: pagePosition, child: Consumer>( @@ -118,7 +118,8 @@ class _AllahNamesListState extends State }, ), ), - NoorCloseButton(color: Theme.of(context).accentColor), + NoorCloseButton( + color: Theme.of(context).colorScheme.secondary), ], ), ), @@ -126,13 +127,13 @@ class _AllahNamesListState extends State Expanded( child: Scrollbar( child: ScrollablePositionedList.builder( - physics: AlwaysScrollableScrollPhysics(), + physics: const AlwaysScrollableScrollPhysics(), itemScrollController: controller, itemPositionsListener: listener, itemCount: allahNames.length, addAutomaticKeepAlives: true, initialScrollIndex: widget.index, - padding: EdgeInsets.only(bottom: 20), + padding: const EdgeInsets.only(bottom: 20), itemBuilder: (_, int index) { final AllahName name = allahNames[index]; @@ -150,7 +151,7 @@ class _AllahNamesListState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ CardText(text: textList[0] + '.'), - SizedBox(height: 10), + const SizedBox(height: 10), CardText(text: textList[1] + '.'), ], ), @@ -213,17 +214,18 @@ class ReferenceList extends StatelessWidget { bottom: false, child: Container( width: double.infinity, - margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox(width: 45), + const SizedBox(width: 45), Text( name.name, textAlign: TextAlign.center, style: Theme.of(context).textTheme.headline1, ), - NoorCloseButton(color: Theme.of(context).accentColor), + NoorCloseButton( + color: Theme.of(context).colorScheme.secondary), ], ), ), diff --git a/lib/pages/tabs/1_home/athkar.dart b/lib/pages/tabs/page_1_home/athkar.dart similarity index 90% rename from lib/pages/tabs/1_home/athkar.dart rename to lib/pages/tabs/page_1_home/athkar.dart index 66568a9..da07cf0 100644 --- a/lib/pages/tabs/1_home/athkar.dart +++ b/lib/pages/tabs/page_1_home/athkar.dart @@ -9,18 +9,21 @@ import 'package:noor/exports/pages.dart' show AthkarList; import 'package:noor/exports/components.dart' show ListItem, NoorCloseButton; class AthkarPage extends StatefulWidget { - const AthkarPage(); + const AthkarPage({Key? key}) : super(key: key); + + @override _AthkarPageState createState() => _AthkarPageState(); } class _AthkarPageState extends State with SingleTickerProviderStateMixin { - ScrollController scrollController = new ScrollController(); + ScrollController scrollController = ScrollController(); double currentScroll = 0; double maxHeight = 180; late Animation animation; late AnimationController controller; + @override initState() { super.initState(); DBService.db.initDB(); @@ -70,7 +73,8 @@ class _AthkarPageState extends State }, ), ), - Positioned(left: 10.0, top: 40.0, child: NoorCloseButton(size: 35)), + const Positioned( + left: 10.0, top: 40.0, child: NoorCloseButton(size: 35)), ], ), Expanded( @@ -83,17 +87,17 @@ class _AthkarPageState extends State return Scrollbar( controller: scrollController, child: ListView.builder( - physics: AlwaysScrollableScrollPhysics(), + physics: const AlwaysScrollableScrollPhysics(), itemCount: athkarTitles.length, controller: scrollController, - padding: EdgeInsets.only(top: 10), + padding: const EdgeInsets.only(top: 10), itemBuilder: (BuildContext context, int index) { final Thekr title = athkarTitles[index]; final int position = model.athkar.indexOf(title); return title.isTitle ? ListItem( - title: '${title.text}', + title: title.text, icon: images.athkarTitleIcon, onTap: () { Navigator.push( diff --git a/lib/pages/tabs/1_home/athkar_expanded.dart b/lib/pages/tabs/page_1_home/athkar_expanded.dart similarity index 91% rename from lib/pages/tabs/1_home/athkar_expanded.dart rename to lib/pages/tabs/page_1_home/athkar_expanded.dart index 2ec78bd..54957de 100644 --- a/lib/pages/tabs/1_home/athkar_expanded.dart +++ b/lib/pages/tabs/page_1_home/athkar_expanded.dart @@ -11,6 +11,7 @@ import 'package:noor/exports/components.dart' class AthkarList extends StatefulWidget { const AthkarList({Key? key, required this.index}) : super(key: key); final int index; + @override _AthkarListState createState() => _AthkarListState(); } @@ -30,14 +31,14 @@ class _AthkarListState extends State void initState() { super.initState(); pagePosition = widget.index; - animationController = new AnimationController(vsync: this); + animationController = AnimationController(vsync: this); animation = Tween(begin: 0.0, end: 0.1).animate( CurvedAnimation( parent: animationController, curve: Curves.elasticIn, ), ); - controller = new ItemScrollController(); + controller = ItemScrollController(); WidgetsBinding.instance!.addPostFrameCallback((_) { listener.itemPositions.addListener(changeAppBar); }); @@ -90,10 +91,10 @@ class _AthkarListState extends State settings.autoJump && counter.position == 0 && !context.read().athkar[index + 1].isTitle) { - Future.delayed(Duration(milliseconds: 500)).then( + Future.delayed(const Duration(milliseconds: 500)).then( (_) { controller.scrollTo( - duration: Duration(milliseconds: 800), + duration: const Duration(milliseconds: 800), curve: Curves.easeInOutCubic, index: index + 1); }, @@ -112,7 +113,7 @@ class _AthkarListState extends State bottom: false, child: Container( width: double.infinity, - margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -134,7 +135,8 @@ class _AthkarListState extends State }, ), ), - NoorCloseButton(color: Theme.of(context).accentColor), + NoorCloseButton( + color: Theme.of(context).colorScheme.secondary), ], ), ), @@ -155,15 +157,15 @@ class _AthkarListState extends State builder: (_, List countersList, __) { return Scrollbar( child: ScrollablePositionedList.builder( - key: ValueKey('list'), - physics: AlwaysScrollableScrollPhysics(), + key: const ValueKey('list'), + physics: const AlwaysScrollableScrollPhysics(), itemScrollController: controller, itemPositionsListener: listener, itemCount: athkar.length, addAutomaticKeepAlives: true, initialScrollIndex: widget.index, minCacheExtent: 900, - padding: EdgeInsets.only(bottom: 20), + padding: const EdgeInsets.only(bottom: 20), itemBuilder: (_, int index) { final Thekr thekr = athkar[index]; if (thekr.isTitle) { diff --git a/lib/pages/tabs/1_home/home.dart b/lib/pages/tabs/page_1_home/home.dart similarity index 60% rename from lib/pages/tabs/1_home/home.dart rename to lib/pages/tabs/page_1_home/home.dart index ef2c5a7..d363eb1 100644 --- a/lib/pages/tabs/1_home/home.dart +++ b/lib/pages/tabs/page_1_home/home.dart @@ -17,19 +17,24 @@ import 'package:noor/exports/controllers.dart' show ThemeModel; import 'package:noor/exports/services.dart' show SharedPrefsService; import 'package:noor/exports/models.dart' show AllahName, DataModel; +export 'package:noor/pages/tabs/page_1_home/ad3yah_expanded.dart'; +export 'package:noor/pages/tabs/page_1_home/allah_names_expanded.dart'; +export 'package:noor/pages/tabs/page_1_home/athkar_expanded.dart'; +export 'package:noor/pages/tabs/page_1_home/my_ad3yah.dart'; + class Home extends StatefulWidget { - Home({ + const Home({ Key? key, }) : super(key: key); + @override _HomeState createState() => _HomeState(); } -class _HomeState extends State - with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { +class _HomeState extends State with AutomaticKeepAliveClientMixin { bool isWriting = false; - FocusNode _focusNode = new FocusNode(); - TextEditingController _searchController = new TextEditingController(); + final _focusNode = FocusNode(); + final _searchController = TextEditingController(); List results = []; List title = []; @@ -53,12 +58,11 @@ class _HomeState extends State Future displayNotification( String title, String body, String payload) async { FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - new FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); AndroidNotificationDetails androidPlatformChannelSpecifics = - AndroidNotificationDetails( + const AndroidNotificationDetails( 'RC', 'RC notification', - 'RC channel', importance: Importance.max, priority: Priority.high, ); @@ -144,86 +148,83 @@ class _HomeState extends State }, child: Scaffold( resizeToAvoidBottomInset: false, - body: SafeArea( - top: false, - child: Column( - children: [ - AnimatedHeader( - focusNode: _focusNode, - isWriting: isWriting, - ), - Expanded( - flex: isWriting ? 0 : 1, - child: SingleChildScrollView( - child: Column( - children: [ - if (_focusNode.hasFocus) const SizedBox(height: 10), - searchBar(), - const SizedBox(height: 10), - if (!isWriting) - Stack( - children: [ - if (!isWriting) - Column( - children: [ - HomeCard( - page: const AthkarPage(), - image: images.athkarCard, - tag: 'athkar', - ), - HomeCard( - page: const Ad3yah(), - image: images.ad3yahCard, - tag: 'ad3yah', - ), - HomeCard( - page: const AllahNames(), - image: images.allahNamesCard, - tag: 'allah names', - ), - ], - ), - Visibility( - visible: !isWriting && _focusNode.hasFocus, - child: AnimatedOpacity( - duration: Duration(milliseconds: 400), - opacity: _focusNode.hasFocus ? 1.0 : 0.0, - child: GestureDetector( - onTap: () { - if (_focusNode.hasFocus) { - _searchController.clear(); - FocusScope.of(context) - .requestFocus(new FocusNode()); - setState(() { - isWriting = false; - }); - } - }, - child: Container( - color: Colors.transparent, - height: _focusNode.hasFocus && !isWriting - ? MediaQuery.of(context).size.height - : 0, - ), + body: Column( + children: [ + AnimatedHeader( + focusNode: _focusNode, + isWriting: isWriting, + ), + Expanded( + flex: isWriting ? 0 : 1, + child: SingleChildScrollView( + child: Column( + children: [ + if (_focusNode.hasFocus) const SizedBox(height: 10), + searchBar(), + const SizedBox(height: 10), + if (!isWriting) + Stack( + children: [ + if (!isWriting) + Column( + children: [ + HomeCard( + page: const AthkarPage(), + image: images.athkarCard, + tag: 'athkar', + ), + HomeCard( + page: const Ad3yah(), + image: images.ad3yahCard, + tag: 'ad3yah', + ), + HomeCard( + page: const AllahNames(), + image: images.allahNamesCard, + tag: 'allah names', + ), + ], + ), + Visibility( + visible: !isWriting && _focusNode.hasFocus, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 400), + opacity: _focusNode.hasFocus ? 1.0 : 0.0, + child: GestureDetector( + onTap: () { + if (_focusNode.hasFocus) { + _searchController.clear(); + FocusScope.of(context) + .requestFocus(FocusNode()); + setState(() { + isWriting = false; + }); + } + }, + child: Container( + color: Colors.transparent, + height: _focusNode.hasFocus && !isWriting + ? MediaQuery.of(context).size.height + : 0, ), ), ), - ], - ), - ], - ), + ), + ], + ), + ], ), ), - if (isWriting) - Expanded( - child: SearchResults( - query: searchWord, - results: results, - title: title, - ), + ), + if (isWriting) + Expanded( + child: SearchResults( + query: searchWord, + results: results, + title: title, ), - ], - ), + ), + ], ), ), ); @@ -231,65 +232,64 @@ class _HomeState extends State /// Build the search bar widget Widget searchBar() { - return SafeArea( - child: Container( - padding: EdgeInsets.only(right: 25.0, left: 25.0), - child: TextField( - controller: _searchController, - focusNode: _focusNode, - onTap: () { - if (!_focusNode.hasFocus) { - setState(() {}); - } - }, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).textTheme.bodyText1!.color, + return Container( + padding: const EdgeInsets.only(right: 25.0, left: 25.0), + child: TextField( + controller: _searchController, + focusNode: _focusNode, + onTap: () { + if (!_focusNode.hasFocus) { + setState(() {}); + } + }, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).textTheme.bodyText1!.color, + fontWeight: FontWeight.normal, + ), + decoration: InputDecoration( + contentPadding: + const EdgeInsets.symmetric(vertical: 0, horizontal: 15), + hintText: 'ابحث عن ذكر أو دعاء', + hintStyle: const TextStyle( + color: Colors.grey, fontWeight: FontWeight.normal, ), - decoration: InputDecoration( - contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 15), - hintText: 'ابحث عن ذكر أو دعاء', - hintStyle: TextStyle( - color: Colors.grey, - fontWeight: FontWeight.normal, - ), - prefixIcon: Icon( - Icons.search, - color: Theme.of(context).brightness == Brightness.light - ? Colors.grey - : Colors.white, - ), - suffixIcon: isWriting - ? IconButton( - icon: Icon( - Icons.close, - color: Theme.of(context).brightness == Brightness.light - ? Colors.grey - : Colors.white, - ), - onPressed: () { - _searchController.clear(); - //FocusScope.of(context).requestFocus(new FocusNode()); - // setState(() { - // isWriting = false; - // }); - }, - ) - : null, - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent, width: 2.0), - borderRadius: BorderRadius.circular(16.0), - ), - filled: true, - fillColor: Theme.of(context).brightness == Brightness.light - ? Colors.grey[100] - : Colors.white12, - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16.0), - borderSide: BorderSide( - color: Colors.transparent, - ), + prefixIcon: Icon( + Icons.search, + color: Theme.of(context).brightness == Brightness.light + ? Colors.grey + : Colors.white, + ), + suffixIcon: isWriting + ? IconButton( + icon: Icon( + Icons.close, + color: Theme.of(context).brightness == Brightness.light + ? Colors.grey + : Colors.white, + ), + onPressed: () { + _searchController.clear(); + //FocusScope.of(context).requestFocus(new FocusNode()); + // setState(() { + // isWriting = false; + // }); + }, + ) + : null, + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide(color: Colors.transparent, width: 2.0), + borderRadius: BorderRadius.circular(16.0), + ), + filled: true, + fillColor: Theme.of(context).brightness == Brightness.light + ? Colors.grey[100] + : Colors.white12, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16.0), + borderSide: const BorderSide( + color: Colors.transparent, ), ), ), @@ -301,13 +301,11 @@ class _HomeState extends State class AnimatedHeader extends StatefulWidget { const AnimatedHeader({ Key? key, - required FocusNode focusNode, required this.isWriting, - }) : focusNode = focusNode, - super(key: key); + required this.focusNode, + }) : super(key: key); final FocusNode focusNode; - final bool isWriting; @override @@ -340,17 +338,17 @@ class _AnimatedHeaderState extends State _setupCloudAnimation() { cloudController = - AnimationController(duration: Duration(seconds: 15), vsync: this) + AnimationController(duration: const Duration(seconds: 15), vsync: this) ..forward() ..reverse() ..repeat(); - _topCloudAnim = - Tween(begin: Offset(3.0, 0.0), end: Offset(-5.5, 0.0)) - .animate(cloudController); - _bottomCloudAnim = - Tween(begin: Offset(-5.5, 0.0), end: Offset(3.0, 0.0)) - .animate(cloudController); + _topCloudAnim = Tween( + begin: const Offset(3.0, 0.0), end: const Offset(-5.5, 0.0)) + .animate(cloudController); + _bottomCloudAnim = Tween( + begin: const Offset(-5.5, 0.0), end: const Offset(3.0, 0.0)) + .animate(cloudController); } Future _loadRemoteConfig() async { @@ -371,8 +369,7 @@ class _AnimatedHeaderState extends State Provider.of(context, listen: false).images; return AnimatedSize( curve: Curves.easeInOutCirc, - duration: Duration(milliseconds: 300), - vsync: this, + duration: const Duration(milliseconds: 300), child: Container( decoration: BoxDecoration( image: DecorationImage( @@ -384,7 +381,12 @@ class _AnimatedHeaderState extends State width: MediaQuery.of(context).size.width, height: widget.focusNode.hasFocus || widget.isWriting ? 0 : 170, padding: const EdgeInsets.symmetric(horizontal: 20), + margin: EdgeInsets.only( + bottom: widget.focusNode.hasFocus + ? MediaQuery.of(context).viewPadding.top + : 15.0), child: Stack( + alignment: Alignment.center, children: [ if (Theme.of(context).brightness == Brightness.light) Positioned( @@ -407,49 +409,47 @@ class _AnimatedHeaderState extends State ), ), if (Theme.of(context).brightness == Brightness.dark) - Align( + const Align( alignment: Alignment.center, child: GlowingStars(), ), SafeArea( - child: Align( - alignment: Alignment.topCenter, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 5), - SvgPicture.asset( - 'assets/images/${prefix}logo-dark.svg', - width: 60, - ), - const SizedBox(height: 15), - ValueListenableBuilder( - valueListenable: remoteConfigNotifier, - builder: (_, String value, Widget? child) { - return AnimatedSwitcher( - duration: Duration(milliseconds: 500), - transitionBuilder: - (Widget child, Animation animation) { - return FadeTransition( - child: child, opacity: animation); - }, - child: Text( - value, - key: ValueKey(value), - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - fontSize: 15, - height: 1.5, - ), + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: 120, + child: SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 5), + SvgPicture.asset( + 'assets/images/${prefix}logo-dark.svg', + width: 60, + ), + const SizedBox(height: 15), + ValueListenableBuilder( + valueListenable: remoteConfigNotifier, + builder: (_, String value, Widget? child) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + transitionBuilder: + (Widget child, Animation animation) { + return FadeTransition( + child: child, opacity: animation); + }, + child: Text( + value, + key: ValueKey(value), + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontSize: 15, + height: 1.5, ), - ); - }, - ), - ], - ), + ), + ); + }, + ), + ], ), ), ), @@ -476,7 +476,7 @@ class SearchResults extends StatefulWidget { } String mask(string) { - var tmp; + String tmp; // for each أ، إ، آ in string, replace with ا tmp = string.replaceAll( RegExp( @@ -548,10 +548,10 @@ class _SearchResultsState extends State { @override Widget build(BuildContext context) { return SizedBox( - child: widget.results.length == 0 - ? Text('لا توجد نتائج') + child: widget.results.isEmpty + ? const Text('لا توجد نتائج') : ListView.builder( - physics: AlwaysScrollableScrollPhysics(), + physics: const AlwaysScrollableScrollPhysics(), padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom), itemCount: widget.results.length, @@ -583,7 +583,7 @@ class _SearchResultsState extends State { ), ], ), - padding: EdgeInsets.symmetric( + padding: const EdgeInsets.symmetric( horizontal: 30.0, ), ), @@ -601,11 +601,11 @@ class _SearchResultsState extends State { fontWeight: FontWeight.bold, fontSize: 12), ), - padding: EdgeInsets.symmetric( + padding: const EdgeInsets.symmetric( horizontal: 30.0, ), ), - Divider(), + const Divider(), ], ), ); diff --git a/lib/pages/tabs/1_home/my_ad3yah.dart b/lib/pages/tabs/page_1_home/my_ad3yah.dart similarity index 84% rename from lib/pages/tabs/1_home/my_ad3yah.dart rename to lib/pages/tabs/page_1_home/my_ad3yah.dart index df6aad5..d6b4784 100644 --- a/lib/pages/tabs/1_home/my_ad3yah.dart +++ b/lib/pages/tabs/page_1_home/my_ad3yah.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; +import 'package:noor/components/dialog_button.dart'; import 'package:provider/provider.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:reorderables/reorderables.dart'; @@ -18,18 +19,20 @@ import 'package:noor/exports/components.dart' FavAction; class MyAd3yah extends StatefulWidget { - MyAd3yah({Key? key, this.index = 0}) : super(key: key); + const MyAd3yah({Key? key, this.index = 0}) : super(key: key); final int index; + @override _MyAd3yahState createState() => _MyAd3yahState(); } class _MyAd3yahState extends State with TickerProviderStateMixin { - TextEditingController _firstController = new TextEditingController(); - TextEditingController _secondController = new TextEditingController(); + final _firstController = TextEditingController(); + final _secondController = TextEditingController(); ScrollController? controller; Widget? animatedWidget; final GlobalKey listKey = GlobalKey(); + @override void initState() { super.initState(); controller = ScrollController(initialScrollOffset: widget.index * 380.0); @@ -50,11 +53,11 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { child: SingleChildScrollView( child: TextField( controller: controller, - style: Theme.of(context).textTheme.body1, + style: Theme.of(context).textTheme.bodyText1, decoration: InputDecoration( filled: false, contentPadding: - EdgeInsets.symmetric(vertical: 12, horizontal: 10.0), + const EdgeInsets.symmetric(vertical: 12, horizontal: 10.0), border: InputBorder.none, hintText: text, ), @@ -67,30 +70,13 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { } //button design (used in dialoges) - button({required text, border, required radius, textColor, onPress}) { - return Expanded( - flex: 1, - child: Container( - decoration: BoxDecoration(border: border), - child: RaisedButton( - shape: RoundedRectangleBorder(borderRadius: radius), - elevation: 0.0, - focusElevation: 0.0, - highlightElevation: 0.0, - hoverElevation: 0.0, - splashColor: Colors.white24, - highlightColor: Colors.white24, - onPressed: onPress, - child: Text( - text, - style: TextStyle( - color: textColor, - fontWeight: FontWeight.w300, - fontSize: 12, - ), - ), - ), - ), + button({required text, border, required radius, textColor, onPressed}) { + return DialogButton( + label: text, + border: border, + onPressed: onPressed, + radius: radius, + textColor: textColor, ); } @@ -101,7 +87,7 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { _secondController.text = data.info; } showGeneralDialog( - transitionDuration: Duration(milliseconds: 600), + transitionDuration: const Duration(milliseconds: 600), barrierDismissible: false, barrierColor: Colors.black.withOpacity(0.75), barrierLabel: '', @@ -116,9 +102,9 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { decoration: BoxDecoration( color: Theme.of(context).brightness == Brightness.light ? Colors.white - : Color(0xff1B2349), + : const Color(0xff1B2349), borderRadius: BorderRadius.circular(15.0)), - constraints: BoxConstraints(maxHeight: 360), + constraints: const BoxConstraints(maxHeight: 360), child: Stack( children: [ SingleChildScrollView( @@ -134,30 +120,30 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { Align( alignment: Alignment.bottomCenter, child: Container( - constraints: BoxConstraints.expand(height: 40), + constraints: const BoxConstraints.expand(height: 40), child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ button( text: 'حفظ', - border: Border( + border: const Border( left: BorderSide( width: 0.5, color: Colors.white)), - radius: BorderRadius.only( + radius: const BorderRadius.only( bottomRight: Radius.circular(15)), textColor: Colors.lightBlue[100], - onPress: () => onSave(prevDoaa: data), + onPressed: () => onSave(prevDoaa: data), ), button( text: 'إلغاء', - border: Border( + border: const Border( right: BorderSide( width: 0.5, color: Colors.white)), - radius: BorderRadius.only( + radius: const BorderRadius.only( bottomLeft: Radius.circular(15)), textColor: Colors.white, - onPress: onCancel, + onPressed: onCancel, ), ], ), @@ -170,7 +156,7 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { ), ); }, - pageBuilder: (_, __, ___) => SizedBox(), + pageBuilder: (_, __, ___) => const SizedBox(), ); } @@ -218,7 +204,7 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { width: 45, height: 45, ), - Text( + const Text( 'أدعيتي', style: TextStyle( color: Colors.white, @@ -226,14 +212,14 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { fontSize: 20, ), ), - NoorCloseButton(size: 35) + const NoorCloseButton(size: 35) ], ), ), const SizedBox(height: 35), Expanded( child: AnimatedSwitcher( - duration: Duration(milliseconds: 400), + duration: const Duration(milliseconds: 400), child: myAd3yah.isNotEmpty ? Scrollbar( controller: controller, @@ -241,7 +227,7 @@ class _MyAd3yahState extends State with TickerProviderStateMixin { controller: controller, slivers: [ ReorderableSliverList( - key: ValueKey('list'), + key: const ValueKey('list'), controller: controller, buildDraggableFeedback: (_, BoxConstraints constraints, diff --git a/lib/pages/tabs/2_fav.dart b/lib/pages/tabs/page_2_fav.dart similarity index 93% rename from lib/pages/tabs/2_fav.dart rename to lib/pages/tabs/page_2_fav.dart index 0c00f3a..fd65f49 100644 --- a/lib/pages/tabs/2_fav.dart +++ b/lib/pages/tabs/page_2_fav.dart @@ -1,18 +1,11 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:noor/models/data.dart'; import 'package:provider/provider.dart'; import 'package:reorderables/reorderables.dart'; -import 'package:noor/main.dart'; import 'package:noor/exports/components.dart' - show - CardTemplate, - DeleteConfirmationDialog, - ImageButton, - NoorIcons, - NoorSettingsIcons; + show CardTemplate, DeleteConfirmationDialog, ImageButton; import 'package:noor/exports/models.dart' show AllahName; import 'package:noor/exports/constants.dart' show Images, NoorCategory; import 'package:noor/exports/pages.dart' @@ -22,9 +15,11 @@ import 'package:noor/exports/utils.dart' show backToExactLocation; import 'package:noor/exports/components.dart' show CardText; class Favorite extends StatefulWidget { - Favorite({ + const Favorite({ Key? key, }) : super(key: key); + + @override _FavoriteState createState() => _FavoriteState(); } @@ -33,7 +28,7 @@ class _FavoriteState extends State @override bool get wantKeepAlive => true; - ScrollController _scrollController = ScrollController(); + final _scrollController = ScrollController(); List sectionList = []; List favList = []; @@ -98,24 +93,24 @@ class _FavoriteState extends State final int index = tmpList.indexWhere( (dynamic element) => element.sectionName == item.sectionName); switch (item.category) { - case NoorCategory.ATHKAR: + case NoorCategory.athkar: Navigator.of(context).push( MaterialPageRoute( builder: (_) => AthkarList(index: index), ), ); break; - case NoorCategory.MYAD3YAH: + case NoorCategory.myad3yah: Navigator.of(context).push( MaterialPageRoute( - builder: (_) => MyAd3yah(), + builder: (_) => const MyAd3yah(), ), ); break; - case NoorCategory.ALLAHNAME: + case NoorCategory.allahname: Navigator.of(context).push( MaterialPageRoute( - builder: (_) => AllahNamesList(), + builder: (_) => const AllahNamesList(), ), ); break; @@ -161,7 +156,7 @@ class _FavoriteState extends State margin: const EdgeInsets.symmetric(vertical: 22), height: 45, child: ListView.separated( - padding: EdgeInsets.symmetric(horizontal: 20.0), + padding: const EdgeInsets.symmetric(horizontal: 20.0), separatorBuilder: (_, __) => const SizedBox(width: 10.0), scrollDirection: Axis.horizontal, itemCount: Images.favButtonsList.length, @@ -176,7 +171,7 @@ class _FavoriteState extends State ), Expanded( child: AnimatedSwitcher( - duration: Duration(milliseconds: 300), + duration: const Duration(milliseconds: 300), child: sectionList.isEmpty ? Container( key: ValueKey(images.noAd3yahFav), @@ -194,7 +189,7 @@ class _FavoriteState extends State controller: _scrollController, slivers: [ ReorderableSliverList( - key: ValueKey('List'), + key: const ValueKey('List'), controller: _scrollController, buildDraggableFeedback: (_, BoxConstraints constraints, @@ -273,7 +268,7 @@ class _FavoriteState extends State } class FavCard extends StatelessWidget { - FavCard({ + const FavCard({ Key? key, required this.item, required this.icon, @@ -320,7 +315,7 @@ class FavCard extends StatelessWidget { onTap: remove, ), ], - additionalContent: item.category == NoorCategory.MYAD3YAH + additionalContent: item.category == NoorCategory.myad3yah ? CardText( text: item.info, color: Theme.of(context).primaryColor, @@ -336,7 +331,7 @@ class FavCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ CardText(text: textList[0].trim()), - SizedBox(height: 10), + const SizedBox(height: 10), CardText(text: textList[1].trim()), ], ); diff --git a/lib/pages/tabs/page_3_counter/counter_list_view.dart b/lib/pages/tabs/page_3_counter/counter_list_view.dart new file mode 100644 index 0000000..6a5cdab --- /dev/null +++ b/lib/pages/tabs/page_3_counter/counter_list_view.dart @@ -0,0 +1,316 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:noor/components/add_dialog.dart'; +import 'package:noor/components/alert_dialog.dart'; +import 'package:noor/components/delete_dialog.dart'; +import 'package:noor/exports/constants.dart'; +import 'package:noor/exports/controllers.dart'; +import 'package:noor/exports/pages.dart'; +import 'package:noor/pages/tabs/page_3_counter/counter_view_model.dart'; +import 'package:provider/provider.dart'; +import 'package:noor/utils/to_arabic.dart'; + +const kMaxLength = 75; + +LinearGradient lightModeCounterBG = const LinearGradient( + colors: [ + Color(0xff9AE2F6), + Color(0xff48C6EF), + ], + begin: Alignment.bottomRight, + end: Alignment.topLeft, +); + +LinearGradient darkModeCounterBG = const LinearGradient( + colors: [ + Color(0xff9FB6FF), + Color(0xff6C6CD3), + ], + begin: Alignment.bottomRight, + end: Alignment.topLeft, +); + +class CounterListView extends StatefulWidget { + const CounterListView({Key? key}) : super(key: key); + + @override + _CounterListViewState createState() => _CounterListViewState(); +} + +class _CounterListViewState extends State { + bool isEditMode = false; + final scrollController = ScrollController(); + + addDialog() async { + final counterModel = context.read(); + + await AddDialog.of(context).show( + enableSecondaryContent: false, + mainContentMaxLength: kMaxLength, + onSave: (content) { + counterModel.addSubhaItem( + SubhaItem(counter: 0, key: content, selected: false), + ); + + Navigator.of(context).pop(); + + scrollController.animateTo(0.0, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut); + }, + onCancel: () { + Navigator.of(context).pop(); + }, + ); + } + + onTap(SubhaItem item) { + final counterModel = context.read(); + counterModel.setSelectedItem = item; + + Navigator.of(context).pop(); + } + + onDelete(SubhaItem item) async { + final counterModel = context.read(); + + if (!item.locked && isEditMode) { + final confirm = await DeleteConfirmationDialog.of(context).show(); + if (confirm ?? false) { + counterModel.deleteSubhaItem(item); + } + } else if (item.locked) { + NoorAlertDialog.of(context) + .show(title: 'عُــذراً', content: 'لا يمكن حذف الأذكار المُقترحة'); + } + } + + @override + Widget build(BuildContext context) { + final counterModel = context.watch(); + return Scaffold( + body: Stack( + children: [ + SvgPicture.asset( + context.read().images.subhaBg, + fit: BoxFit.fill, + ), + Column( + children: [ + appBar(), + Expanded( + child: ListView.separated( + controller: scrollController, + separatorBuilder: (_, __) => const SizedBox(height: 10), + padding: const EdgeInsets.symmetric( + horizontal: 20.0, + vertical: 10.0, + ), + itemCount: counterModel.subhaList.length, + itemBuilder: (_, int index) { + SubhaItem item = counterModel.subhaList[index]; + return SubhaListItem( + item: item, + isEditMode: isEditMode, + onDelete: onDelete, + onTap: onTap, + ); + }, + ), + ) + ], + ), + ], + ), + ); + } + + Widget appBar() { + return SafeArea( + bottom: false, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: viewPadding, + vertical: 10.0, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isEditMode + ? NoorIconButton( + key: ValueKey(isEditMode), + icon: context.read().images.addMyAd3yah, + onPressed: addDialog, + ) + : NoorIconButton( + key: ValueKey(isEditMode), + icon: Images.editeIcon, + onPressed: () { + setState(() { + isEditMode = true; + }); + }, + ), + ), + Text( + 'قائمة الأذكار', + style: Theme.of(context) + .textTheme + .headline2! + .copyWith(fontWeight: FontWeight.bold), + ), + NoorIconButton( + icon: Icons.close_rounded, + onPressed: () { + if (isEditMode) { + setState(() { + isEditMode = false; + }); + } else { + Navigator.of(context).pop(); + } + }, + ) + ], + ), + ), + ); + } +} + +class SubhaListItem extends StatelessWidget { + const SubhaListItem({ + Key? key, + required this.item, + required this.onTap, + required this.onDelete, + this.isEditMode = false, + }) : super(key: key); + + final SubhaItem item; + final bool isEditMode; + final Function(SubhaItem) onTap; + final Function(SubhaItem) onDelete; + + @override + Widget build(BuildContext context) { + final borderRadius = BorderRadius.circular(50); + final settings = context.watch(); + final theme = context.watch(); + + return Material( + color: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + child: InkWell( + onTap: isEditMode ? null : () => onTap(item), + borderRadius: BorderRadius.circular(8.0), + splashColor: Colors.transparent, + highlightColor: Theme.of(context).selectedRowColor, + child: Container( + constraints: const BoxConstraints(minHeight: 80), + margin: const EdgeInsets.all(5.0), + padding: const EdgeInsets.all(viewPadding), + alignment: Alignment.centerRight, + decoration: BoxDecoration( + color: Color(theme.colors.subhaListItemBg), + borderRadius: BorderRadius.circular(8.0), + boxShadow: [ + BoxShadow( + color: item.selected + ? Theme.of(context).selectedRowColor + : Colors.transparent, + spreadRadius: 6, + ) + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + item.key, + textScaleFactor: settings.fontSize, + style: Theme.of(context).textTheme.bodyText1?.copyWith( + fontFamily: context.read().fontType, + height: 1.2), + ), + ), + Align( + alignment: AlignmentDirectional.centerEnd, + child: SizedBox( + height: 40, + child: AnimatedContainer( + width: isEditMode ? 40 : 90, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + decoration: BoxDecoration( + gradient: isEditMode + ? LinearGradient( + colors: item.locked + ? [ + Color(theme.colors.subhaLockBg), + Color(theme.colors.subhaLockBg) + ] + : [Colors.red, Colors.red], + ) + : Theme.of(context).brightness == Brightness.dark + ? darkModeCounterBG + : lightModeCounterBG, + borderRadius: borderRadius, + ), + child: Material( + color: Colors.transparent, + child: InkWell( + splashColor: Colors.transparent, + borderRadius: borderRadius, + onTap: !isEditMode ? null : () => onDelete(item), + child: Container( + alignment: AlignmentDirectional.center, + decoration: BoxDecoration( + borderRadius: borderRadius, + ), + child: Align( + alignment: AlignmentDirectional.centerEnd, + child: Center( + child: isEditMode + ? item.locked + ? SvgPicture.asset( + NoorIcons.subhaLock, + width: 40, + ) + : Image.asset( + Images.eraseIcon, + height: 40, + ) + : Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: Text( + '${item.counter}'.arabicDigit(), + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .headline2 + ?.copyWith( + fontWeight: FontWeight.bold), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/tabs/page_3_counter/counter_main_view.dart b/lib/pages/tabs/page_3_counter/counter_main_view.dart new file mode 100644 index 0000000..4950ce0 --- /dev/null +++ b/lib/pages/tabs/page_3_counter/counter_main_view.dart @@ -0,0 +1,196 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:provider/provider.dart'; + +import 'package:noor/exports/constants.dart'; +import 'package:noor/pages/tabs/page_3_counter/counter_view_model.dart'; +import 'package:noor/pages/tabs/page_3_counter/counter_list_view.dart'; + +import 'package:noor/exports/utils.dart' show ToArabicNumbers; +import 'package:noor/exports/controllers.dart' show SettingsModel, ThemeModel; + +class CounterView extends StatefulWidget { + const CounterView({Key? key}) : super(key: key); + @override + _CounterViewState createState() => _CounterViewState(); +} + +class _CounterViewState extends State { + void incrementCounter() { + final settings = context.read(); + final counterModel = context.read(); + + counterModel.incrementSelectedItem(); + + if (settings.vibrateCounter) { + if (counterModel.selectedItem!.counter % 100 == 0) { + switch (settings.vibrationHunderds) { + case 'strong': + HapticFeedback.lightImpact(); + break; + case 'light': + HapticFeedback.heavyImpact(); + break; + case 'none': + break; + default: + HapticFeedback.lightImpact(); + } + } else { + switch (settings.vibrationClickCounter) { + case 'strong': + HapticFeedback.lightImpact(); + break; + case 'light': + HapticFeedback.heavyImpact(); + break; + case 'none': + break; + default: + HapticFeedback.lightImpact(); + } + } + } + } + + /// Push the [CounterListView] view. + void navigateToCounterList() { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => const CounterListView(), + ), + ); + } + + @override + Widget build(BuildContext context) { + final counterModel = context.watch(); + final settings = context.watch(); + + return Scaffold( + body: GestureDetector( + onTap: incrementCounter, + child: Stack( + children: [ + SvgPicture.asset( + context.read().images.subhaBg, + fit: BoxFit.fill, + ), + SafeArea( + child: Padding( + padding: const EdgeInsets.all(viewPadding), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + NoorIconButton( + icon: NoorIcons.subhaList, + onPressed: navigateToCounterList, + ), + NoorIconButton( + icon: NoorIcons.subhaReset, + onPressed: counterModel.resetSelectedItemCounter, + ) + ], + ), + ), + ), + Center( + child: Stack( + alignment: Alignment.center, + fit: StackFit.expand, + children: [ + Positioned.fill( + top: MediaQuery.of(context).size.height * .18, + bottom: MediaQuery.of(context).size.height * .5, + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.symmetric( + horizontal: viewPadding * 2), + child: Text( + counterModel.selectedItem?.key ?? '', + key: ValueKey( + counterModel.selectedItem?.counter ?? 0, + ), + textAlign: TextAlign.center, + textScaleFactor: settings.fontSize, + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + fontFamily: settings.fontType, + ), + ), + ), + ), + const SizedBox(height: 40), + AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + child: Text( + '${counterModel.selectedItem!.counter}'.arabicDigit(), + key: ValueKey(counterModel.selectedItem!.counter), + style: Theme.of(context) + .textTheme + .subtitle2 + ?.copyWith(fontSize: 30, fontWeight: FontWeight.bold), + textScaleFactor: settings.fontSize, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + +class NoorIconButton extends StatelessWidget { + const NoorIconButton({ + Key? key, + this.onPressed, + this.color = Colors.white, + required this.icon, + }) : super(key: key); + final Function()? onPressed; + final dynamic icon; + final Color color; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 50, + height: 50, + child: Material( + type: MaterialType.transparency, + borderRadius: BorderRadius.circular(50), + child: InkWell( + borderRadius: BorderRadius.circular(50), + onTap: onPressed, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: icon is String + ? icon.contains('png') + ? Image.asset( + icon, + width: 30, + height: 30, + ) + : SvgPicture.asset( + icon, + width: 30, + height: 30, + color: Colors.white, + ) + : Icon( + icon, + color: color, + size: 30, + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/tabs/page_3_counter/counter_view_model.dart b/lib/pages/tabs/page_3_counter/counter_view_model.dart new file mode 100644 index 0000000..ab6dc49 --- /dev/null +++ b/lib/pages/tabs/page_3_counter/counter_view_model.dart @@ -0,0 +1,172 @@ +import 'dart:convert'; +import 'package:collection/collection.dart'; + +import 'package:flutter/foundation.dart'; + +import 'package:noor/exports/services.dart' show SharedPrefsService; +import 'package:noor/services/json.dart'; + +class CounterViewModel extends ChangeNotifier { + CounterViewModel._(this._subhaList, this._selectedItem); + + static Future init() async { + List _subhaList = SharedPrefsService.getStringList('subha') + .map((String e) => SubhaItem.fromJson(json.decode(e))) + .toList() + .cast(); + + final List _default = + await JsonService.instance.loadDefaultSubhaList(); + + // Check in case the stored Subha list doesn't have equal keys to the default Json list. + final List temp = + _subhaList.where((item) => item.locked).map((e) => e.key).toList(); + + bool missingDefault = + !const IterableEquality().equals(temp, _default.toList()); + + // Read from the default Json list if it's missing from prefs + if (_subhaList.isEmpty || missingDefault) { + final _mappedDefault = _default + .map((item) => SubhaItem( + counter: _subhaList.map((e) => e.key).contains(item) + ? _subhaList.firstWhere((e) => e.key == item).counter + : 0, + key: item, + selected: _subhaList.map((e) => e.key).contains(item) + ? _subhaList.firstWhere((e) => e.key == item).selected + : false, + locked: true, + )) + .toList(); + + _subhaList.removeWhere((item) => item.locked); + _subhaList.addAll(_mappedDefault); + + if (!_subhaList.any((element) => element.selected)) { + // If none in the final list is selected, default to select the first item. + _subhaList[0].selected = true; + } + + // Finally make sure to update the stored list again. + SharedPrefsService.putStringList( + 'subha', + _subhaList + .map((SubhaItem e) => json.encode(e.toJson())) + .toList() + .cast(), + ); + } + + // Set the selected item. + final _selectedItem = + _subhaList.firstWhere((SubhaItem item) => item.selected); + + return CounterViewModel._(_subhaList, _selectedItem); + } + + SubhaItem? _selectedItem; + SubhaItem? get selectedItem => _selectedItem; + set setSelectedItem(SubhaItem item) { + // Clear all selections. + for (var item in _subhaList) { + item.setSelected = false; + } + + _selectedItem = item; + _selectedItem!.setSelected = true; + + setSubhaList = _subhaList; + + notifyListeners(); + } + + incrementSelectedItem() { + _selectedItem!.setCounter = ++_selectedItem!.counter; + setSubhaList = _subhaList; + } + + resetSelectedItemCounter() { + _selectedItem!.setCounter = 0; + setSubhaList = _subhaList; + } + + late List _subhaList; + List get subhaList => _subhaList; + set setSubhaList(List list) { + _subhaList = list; + + SharedPrefsService.putStringList( + 'subha', + _subhaList + .map((SubhaItem e) => json.encode(e.toJson())) + .toList() + .cast(), + ); + notifyListeners(); + } + + void addSubhaItem(SubhaItem item) { + _subhaList.insert(0, item); + + setSubhaList = _subhaList; + + notifyListeners(); + } + + void deleteSubhaItem(SubhaItem item) { + _subhaList.remove(item); + if (item.selected) { + _selectedItem = _subhaList.first; + _selectedItem!.setSelected = true; + } + + setSubhaList = _subhaList; + + notifyListeners(); + } +} + +class SubhaItem extends ChangeNotifier { + int counter; + String key; + bool selected; + bool locked; + + set setCounter(int c) { + counter = c; + + notifyListeners(); + } + + set setSelected(bool value) { + selected = value; + + notifyListeners(); + } + + SubhaItem({ + required this.counter, + required this.key, + required this.selected, + this.locked = false, + }); + + toJson() { + return { + 'counter': counter, + 'key': key, + 'selected': selected, + 'locked': locked, + }; + } + + static fromJson(Map json) { + return SubhaItem( + counter: json['counter'], + key: json['key'], + selected: json['selected'], + locked: json['locked'], + ); + } +} diff --git a/lib/pages/tabs/4_settings.dart b/lib/pages/tabs/page_4_settings.dart similarity index 93% rename from lib/pages/tabs/4_settings.dart rename to lib/pages/tabs/page_4_settings.dart index 6e75015..4c6f644 100644 --- a/lib/pages/tabs/4_settings.dart +++ b/lib/pages/tabs/page_4_settings.dart @@ -15,22 +15,23 @@ import 'package:timezone/timezone.dart' as tz; import 'package:noor/exports/components.dart' show CardTemplate, CardText; import 'package:noor/exports/services.dart' show SharedPrefsService; import 'package:noor/exports/controllers.dart' show ThemeModel, SettingsModel; -import 'package:noor/exports/constants.dart' show Images, Links, NoorIcons; +import '../../exports/constants.dart' show Images, Links, NoorIcons, Strings; class Settings extends StatefulWidget { - Settings({ + const Settings({ Key? key, }) : super(key: key); + @override _SettingsState createState() => _SettingsState(); } class _SettingsState extends State with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { - var morningNotiEnabled; - var nightNotiEnabled; - var morningNotiTime; - var nightNotiTime; + late bool morningNotiEnabled; + late bool nightNotiEnabled; + DateTime? morningNotiTime; + DateTime? nightNotiTime; @override get wantKeepAlive => true; @@ -52,8 +53,8 @@ class _SettingsState extends State super.initState(); } - var morningTemp; - var nightTemp; + DateTime? morningTemp; + DateTime? nightTemp; List fonts = ['١٦', '١٨', '٢٠', '٢٢']; TextStyle activeLabelStyle = const TextStyle( color: Color(0xff6db7e5), @@ -129,7 +130,7 @@ class _SettingsState extends State children: [ subtitleWithIcon(title, icon), Container( - margin: EdgeInsets.only(left: 20.0), + margin: const EdgeInsets.only(left: 20.0), child: Directionality( textDirection: TextDirection.ltr, child: Switch( @@ -157,9 +158,9 @@ class _SettingsState extends State fit: FlexFit.loose, child: Container( width: 130, - margin: EdgeInsets.symmetric(horizontal: 30.0), + margin: const EdgeInsets.symmetric(horizontal: 30.0), child: CupertinoSlidingSegmentedControl( - padding: EdgeInsets.all(3), + padding: const EdgeInsets.all(3), thumbColor: Theme.of(context).primaryColor, children: { 'strong': Text( @@ -206,7 +207,7 @@ class _SettingsState extends State Widget radioBtn(icon, title, value) { return ListTileTheme( - contentPadding: EdgeInsets.only(right: 0, left: 20), + contentPadding: const EdgeInsets.only(right: 0, left: 20), dense: false, child: RadioListTile( controlAffinity: ListTileControlAffinity.trailing, @@ -220,19 +221,16 @@ class _SettingsState extends State } void share() { - Share.share( - 'يضمُّ تطبيق نُور العديد من الأذكار والأدعية الواردة في كتاب حصن المسلم. كما يحتوي التطبيق على أدعية من القرآن الكريم والسنة النبوية. والعديد من المميزات. \n https://play.google.com/store/apps/details?id=com.noor.sa', - subject: 'تطبيق نُور'); + Share.share(Strings.shareText, subject: Strings.shareSubject); } void setDailyNotification(DateTime dateTime, period, id) async { FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - new FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); - var androidPlatformChannelSpecifics = new AndroidNotificationDetails( + var androidPlatformChannelSpecifics = AndroidNotificationDetails( '$id', '$period', - 'repeatDailyAtTime $dateTime', showWhen: true, ); var platformChannelSpecifics = @@ -241,7 +239,7 @@ class _SettingsState extends State id, 'أذكار $period', '', - tz.TZDateTime.from(dateTime, tz.local).add(Duration(days: 10000)), + tz.TZDateTime.from(dateTime, tz.local).add(const Duration(days: 10000)), platformChannelSpecifics, payload: period, matchDateTimeComponents: DateTimeComponents.time, @@ -253,7 +251,7 @@ class _SettingsState extends State void cancelNotification(id) async { FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - new FlutterLocalNotificationsPlugin(); + FlutterLocalNotificationsPlugin(); await flutterLocalNotificationsPlugin.cancel(id); } @@ -261,7 +259,7 @@ class _SettingsState extends State await launch(url); } - ScrollController _scrollController = ScrollController(); + final ScrollController _scrollController = ScrollController(); @override Widget build(BuildContext context) { @@ -282,7 +280,7 @@ class _SettingsState extends State children: [ title('الخط'), const Divider(), - Card(), + const Card(), const Divider(), subtitleWithIcon( 'حجم الخط', @@ -363,7 +361,7 @@ class _SettingsState extends State onChanged: (bool value) => settings.tashkeel = value, ), const Divider(), - VerticalSpace(), + const VerticalSpace(), title('العداد'), const Divider(), switcherOption( @@ -373,14 +371,14 @@ class _SettingsState extends State onChanged: (value) => settings.autoJump = value, ), const Divider(), - VerticalSpace(), + const VerticalSpace(), title('إظهار العداد'), const Divider(), Container( - margin: EdgeInsets.symmetric(horizontal: 30.0), + margin: const EdgeInsets.symmetric(horizontal: 30.0), alignment: Alignment.center, child: CupertinoSlidingSegmentedControl( - padding: EdgeInsets.all(3), + padding: const EdgeInsets.all(3), thumbColor: Theme.of(context).primaryColor, children: { 0: Text( @@ -408,7 +406,7 @@ class _SettingsState extends State ), ), const Divider(), - VerticalSpace(), + const VerticalSpace(), title('الهزاز'), const Divider(), switcherOption( @@ -428,7 +426,7 @@ class _SettingsState extends State 'نوع الهزاز لصفحة الأذكار', ), ), - VerticalSpace(), + const VerticalSpace(), segmentedControlOption( icon: NoorIcons.click, title: 'لكل ضغطة', @@ -443,7 +441,7 @@ class _SettingsState extends State } }, ), - VerticalSpace(), + const VerticalSpace(), segmentedControlOption( icon: NoorIcons.done, title: 'عند اكتمال العد', @@ -458,15 +456,15 @@ class _SettingsState extends State } }, ), - VerticalSpace(), - Divider(), + const VerticalSpace(), + const Divider(), ], ), secondChild: Container(), crossFadeState: settings.vibrate ? CrossFadeState.showFirst : CrossFadeState.showSecond, - duration: Duration(milliseconds: 300), + duration: const Duration(milliseconds: 300), ), switcherOption( icon: NoorIcons.vibrate, @@ -474,7 +472,7 @@ class _SettingsState extends State value: settings.vibrateCounter, onChanged: (bool value) => settings.vibrateCounter = value, ), - Divider(), + const Divider(), AnimatedCrossFade( firstChild: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -485,7 +483,7 @@ class _SettingsState extends State 'نوع الهزاز لصفحة السبحة', ), ), - VerticalSpace(), + const VerticalSpace(), segmentedControlOption( icon: NoorIcons.click, title: 'لكل ضغطة', @@ -499,7 +497,7 @@ class _SettingsState extends State HapticFeedback.heavyImpact(); } }), - VerticalSpace(), + const VerticalSpace(), segmentedControlOption( icon: NoorIcons.done, title: 'عند مضاعفات المئة', @@ -514,19 +512,19 @@ class _SettingsState extends State } }, ), - VerticalSpace(), - Divider(), + const VerticalSpace(), + const Divider(), ], ), secondChild: Container(), crossFadeState: settings.vibrateCounter ? CrossFadeState.showFirst : CrossFadeState.showSecond, - duration: Duration(milliseconds: 300), + duration: const Duration(milliseconds: 300), ), - VerticalSpace(), + const VerticalSpace(), title('التنبيهات'), - Divider(), + const Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -566,7 +564,8 @@ class _SettingsState extends State .bold, color: Theme.of( context) - .accentColor, + .colorScheme + .secondary, fontSize: 12), ), onPressed: () async { @@ -585,7 +584,7 @@ class _SettingsState extends State .toString()); setDailyNotification( - morningNotiTime, + morningNotiTime!, 'الصباح', 0); } @@ -601,7 +600,7 @@ class _SettingsState extends State morningNotiTime .toString()); setDailyNotification( - morningNotiTime, + morningNotiTime!, 'الصباح', 0); } @@ -622,7 +621,8 @@ class _SettingsState extends State .bold, color: Theme.of( context) - .accentColor, + .colorScheme + .secondary, fontSize: 12), ), onPressed: () { @@ -696,7 +696,7 @@ class _SettingsState extends State ) : Container(), Container( - margin: EdgeInsets.only(left: 20.0), + margin: const EdgeInsets.only(left: 20.0), child: Directionality( textDirection: TextDirection.ltr, child: Switch( @@ -712,7 +712,7 @@ class _SettingsState extends State if (morningNotiEnabled == true && morningNotiTime != null) { setDailyNotification( - morningNotiTime, 'الصباح', 0); + morningNotiTime!, 'الصباح', 0); } setState(() {}); }, @@ -721,7 +721,7 @@ class _SettingsState extends State ), ], ), - Divider(), + const Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -761,7 +761,8 @@ class _SettingsState extends State .bold, color: Theme.of( context) - .accentColor, + .colorScheme + .secondary, fontSize: 12), ), onPressed: () async { @@ -779,7 +780,7 @@ class _SettingsState extends State .toString()); setDailyNotification( - nightNotiTime, + nightNotiTime!, 'المساء', 1); } @@ -795,7 +796,7 @@ class _SettingsState extends State nightNotiTime .toString()); setDailyNotification( - nightNotiTime, + nightNotiTime!, 'المساء', 1); } @@ -816,7 +817,8 @@ class _SettingsState extends State .bold, color: Theme.of( context) - .accentColor, + .colorScheme + .secondary, fontSize: 12), ), onPressed: () { @@ -840,12 +842,13 @@ class _SettingsState extends State CupertinoTextThemeData( dateTimePickerTextStyle: TextStyle( - locale: Locale('ar'), + locale: + const Locale('ar'), fontFamily: 'SST Arabic', fontSize: 16, color: Theme.of(context) .textTheme - .body1! + .bodyText1! .color, ), ), @@ -895,7 +898,7 @@ class _SettingsState extends State ) : Container(), Container( - margin: EdgeInsets.only(left: 20.0), + margin: const EdgeInsets.only(left: 20.0), child: Directionality( textDirection: TextDirection.ltr, child: Switch( @@ -912,7 +915,7 @@ class _SettingsState extends State if (nightNotiEnabled == true && nightNotiTime != null) { setDailyNotification( - nightNotiTime, 'المساء', 1); + nightNotiTime!, 'المساء', 1); } setState(() {}); }, @@ -921,7 +924,7 @@ class _SettingsState extends State ), ], ), - Divider(), + const Divider(), switcherOption( icon: NoorIcons.notifications, title: 'إشعارات عامة', @@ -934,29 +937,29 @@ class _SettingsState extends State } }, ), - Divider(), - VerticalSpace(), + const Divider(), + const VerticalSpace(), title('المظهر'), - Divider(), + const Divider(), radioBtn( NoorIcons.lightMode, 'الوضع النهاري', 'light_theme', ), - Divider(), + const Divider(), radioBtn( NoorIcons.darkMode, 'الوضع الليلي', 'dark_theme', ), - Divider(), + const Divider(), radioBtn( NoorIcons.systemMode, 'وضع النظام', 'system_theme', ), const Divider(), - VerticalSpace(), + const VerticalSpace(), title('المصادر'), const Divider(), InkWell( @@ -1049,12 +1052,12 @@ class _SettingsState extends State ? activeLabelStyle : inactiveLabelStyle, ), - vsync: this, - duration: Duration(milliseconds: 500), + duration: const Duration(milliseconds: 500), ); } Widget fontTypeButton(String font) { + // ignore: deprecated_member_use return FlatButton( onPressed: () => context.read().fontType = font, child: AnimatedDefaultTextStyle( @@ -1065,8 +1068,8 @@ class _SettingsState extends State : Colors.grey[400], height: 1, fontSize: context.read().fontType == font ? 20 : 16), - duration: Duration(milliseconds: 200), - child: Text('الحمدلله'), + duration: const Duration(milliseconds: 200), + child: const Text('الحمدلله'), ), ); } @@ -1084,6 +1087,8 @@ class VerticalSpace extends StatelessWidget { } class Card extends StatelessWidget { + const Card({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return CardTemplate( diff --git a/lib/services/db.dart b/lib/services/db.dart index 3739502..7845708 100644 --- a/lib/services/db.dart +++ b/lib/services/db.dart @@ -80,7 +80,7 @@ class DBService with ChangeNotifier { 'text': e['text'], 'info': e['info'], 'ribbon': Ribbon.ribbon5, - 'category': NoorCategory.MYAD3YAH, + 'category': NoorCategory.myad3yah, 'sectionName': 'أدعيتي', 'isFav': e['isFav'] == 1 ? true : false, }; diff --git a/lib/services/json.dart b/lib/services/json.dart index c6303e0..5186596 100644 --- a/lib/services/json.dart +++ b/lib/services/json.dart @@ -3,7 +3,10 @@ import 'dart:convert'; import 'package:flutter/services.dart'; class JsonService { - static Future> init() async { + JsonService._(); + static JsonService instance = JsonService._(); + + Future> init() async { List _files = [ 'assets/json/1_athkar.json', 'assets/json/2_quraan_ad3yah.json', @@ -21,4 +24,12 @@ class JsonService { return _content; } + + Future> loadDefaultSubhaList() async { + return json + .decode(await rootBundle + .loadString('assets/json/default_subha_list.json'))['default'] + .cast() + .toList(); + } } diff --git a/lib/services/prefs.dart b/lib/services/prefs.dart index bd71003..aa1334c 100644 --- a/lib/services/prefs.dart +++ b/lib/services/prefs.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_function_type_syntax_for_parameters + import 'dart:async'; import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; @@ -27,7 +29,7 @@ class SharedPrefsService { // put object static Future? putObject(String key, Object value) { if (_prefs == null) return null; - return _prefs!.setString(key, value == null ? '' : json.encode(value)); + return _prefs!.setString(key, json.encode(value)); } // get obj diff --git a/lib/services/remote_config.dart b/lib/services/remote_config.dart index 84de1fa..100a325 100644 --- a/lib/services/remote_config.dart +++ b/lib/services/remote_config.dart @@ -1,10 +1,9 @@ -import 'dart:io'; - import 'package:firebase_remote_config/firebase_remote_config.dart'; +import 'package:noor/constants/strings.dart'; import 'package:noor/services/prefs.dart'; class RemoteConfigService { - final RemoteConfig _remoteConfig = RemoteConfig.instance; + final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance; Duration minimumFetchInterval = const Duration(hours: 1); Duration timeout = const Duration(seconds: 20); @@ -21,7 +20,7 @@ class RemoteConfigService { ); _remoteConfig.setDefaults({ - 'noorThker': 'قال تعالى: ﴿فَاذكُروني أَذكُركُم ﴾ [البقرة: ١٥٢]', + 'noorThker': Strings.noorThekrDefault, }); } @@ -30,13 +29,11 @@ class RemoteConfigService { SharedPrefsService.putBool('CONFIG_STATE', false); try { - // For some reasons, it doesn't fetch - // without pinging internet first on iOS - // TODO(Mais): investigate more - await InternetAddress.lookup('google.com'); + // Issue open in remote_config plugin: https://github.com/FirebaseExtended/flutterfire/issues/6196 + await Future.delayed(const Duration(seconds: 1)); await _remoteConfig.fetchAndActivate(); } catch (e) { - print(e); + rethrow; } } diff --git a/lib/utils/back_to_location.dart b/lib/utils/back_to_location.dart index 41d5334..ad74d6d 100644 --- a/lib/utils/back_to_location.dart +++ b/lib/utils/back_to_location.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; import 'package:noor/constants/categories.dart'; import 'package:noor/models/data.dart'; -import 'package:noor/pages/tabs/1_home/ad3yah_expanded.dart'; -import 'package:noor/pages/tabs/1_home/allah_names_expanded.dart'; -import 'package:noor/pages/tabs/1_home/athkar_expanded.dart'; -import 'package:noor/pages/tabs/1_home/my_ad3yah.dart'; +import 'package:noor/exports/pages.dart' + show AthkarList, MyAd3yah, AllahNamesList, Ad3yahList; import 'package:provider/provider.dart'; backToExactLocation(dynamic item, BuildContext context) async { @@ -23,14 +21,14 @@ backToExactLocation(dynamic item, BuildContext context) async { final int index = tmpList.indexOf(item); switch (item.category) { - case NoorCategory.ATHKAR: + case NoorCategory.athkar: Navigator.of(context).push( MaterialPageRoute( builder: (_) => AthkarList(index: index), ), ); break; - case NoorCategory.MYAD3YAH: + case NoorCategory.myad3yah: final int index = tmpList.indexWhere((dynamic element) => element == item); @@ -40,7 +38,7 @@ backToExactLocation(dynamic item, BuildContext context) async { ), ); break; - case NoorCategory.ALLAHNAME: + case NoorCategory.allahname: Navigator.of(context).push( MaterialPageRoute( builder: (_) => AllahNamesList( diff --git a/lib/utils/to_arabic.dart b/lib/utils/to_arabic.dart index 2da5265..3a57e90 100644 --- a/lib/utils/to_arabic.dart +++ b/lib/utils/to_arabic.dart @@ -2,10 +2,8 @@ extension ToArabicNumbers on String { String arabicDigit() { const int latinArabicUtfDistance = 1584; - final List arabicCodeUnits = '$this' - .codeUnits - .map((int unit) => unit + latinArabicUtfDistance) - .toList(); + final List arabicCodeUnits = + codeUnits.map((int unit) => unit + latinArabicUtfDistance).toList(); //final sign = i.isNegative ? '−' : ''; return String.fromCharCodes(arabicCodeUnits); } diff --git a/pubspec.lock b/pubspec.lock index 8c7e142..958a001 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,28 +7,28 @@ packages: name: animations url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" archive: dependency: transitive description: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.1.2" + version: "3.2.0" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.3.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -42,14 +42,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -71,6 +71,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + dbus: + dependency: transitive + description: + name: dbus + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.8" fake_async: dependency: transitive description: @@ -84,70 +91,77 @@ packages: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.2" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" + version: "6.1.2" firebase_core: dependency: "direct main" description: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.12.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "4.2.4" firebase_core_web: dependency: transitive description: name: firebase_core_web url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.5.4" firebase_messaging: dependency: "direct main" description: name: firebase_messaging url: "https://pub.dartlang.org" source: hosted - version: "9.1.4" + version: "11.2.6" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "3.1.6" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web url: "https://pub.dartlang.org" source: hosted - version: "1.0.7" + version: "2.2.7" firebase_remote_config: dependency: "direct main" description: name: firebase_remote_config url: "https://pub.dartlang.org" source: hosted - version: "0.10.0-dev.3" + version: "2.0.0" firebase_remote_config_platform_interface: dependency: transitive description: name: firebase_remote_config_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "0.3.0-dev.3" + version: "1.0.5" + firebase_remote_config_web: + dependency: transitive + description: + name: firebase_remote_config_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" flutter: dependency: "direct main" description: flutter @@ -166,21 +180,35 @@ packages: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.9.0" + version: "0.9.2" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications url: "https://pub.dartlang.org" source: hosted - version: "5.0.0+4" + version: "9.3.1" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.1+1" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "5.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -192,7 +220,7 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.22.0" + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -209,14 +237,14 @@ packages: name: get_it url: "https://pub.dartlang.org" source: hosted - version: "7.1.3" + version: "7.2.0" image: dependency: transitive description: name: image url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.1.1" intl: dependency: "direct main" description: @@ -231,27 +259,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" mime: dependency: transitive description: name: mime url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" nested: dependency: transitive description: @@ -272,77 +314,91 @@ packages: name: path_drawing url: "https://pub.dartlang.org" source: hosted - version: "0.5.1" + version: "1.0.0" path_parsing: dependency: transitive description: name: path_parsing url: "https://pub.dartlang.org" source: hosted - version: "0.2.1" + version: "1.0.0" path_provider: dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.9" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.5" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.5" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.3" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.5" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.4.0" platform: dependency: transitive description: name: platform url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.2" process: dependency: transitive description: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.2.1" + version: "4.2.4" provider: dependency: "direct main" description: @@ -356,42 +412,56 @@ packages: name: reorderables url: "https://pub.dartlang.org" source: hosted - version: "0.4.1" + version: "0.4.2" scrollable_positioned_list: dependency: "direct main" description: name: scrollable_positioned_list url: "https://pub.dartlang.org" source: hosted - version: "0.2.0-nullsafety.0" + version: "0.2.3" share: dependency: "direct main" description: name: share url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.4" shared_preferences: dependency: "direct main" description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.13" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.10" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.3" shared_preferences_platform_interface: dependency: transitive description: @@ -405,14 +475,14 @@ packages: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.3" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" sky_engine: dependency: transitive description: flutter @@ -431,14 +501,14 @@ packages: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "2.0.0+3" + version: "2.0.2" sqflite_common: dependency: transitive description: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "2.0.0+2" + version: "2.2.0" stack_trace: dependency: transitive description: @@ -480,14 +550,14 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.8" timezone: dependency: transitive description: name: timezone url: "https://pub.dartlang.org" source: hosted - version: "0.7.0" + version: "0.8.0" typed_data: dependency: transitive description: @@ -501,70 +571,84 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.3" + version: "6.0.20" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.15" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.15" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "3.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "3.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.5" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.8" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "3.0.0" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.3.11" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.0+1" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "5.1.0" + version: "5.3.1" yaml: dependency: transitive description: @@ -573,5 +657,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.24.0-10" + dart: ">=2.15.0 <3.0.0" + flutter: ">=2.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 09a7a46..df88a3a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,44 +1,39 @@ name: noor description: أذكار وأدعية من كتاب حصن المسلم -version: 2.4.0+42 +version: 2.5.0+25000 environment: sdk: '>=2.12.0 <3.0.0' dependencies: + animations: ^2.0.2 + firebase_core: ^1.12.0 + firebase_messaging: ^11.2.6 + firebase_remote_config: ^2.0.0 flutter: sdk: flutter + flutter_datetime_picker: ^1.5.1 + flutter_local_notifications: ^9.0.2 flutter_localizations: sdk: flutter - - # Firebase - firebase_core: ^1.1.1 - firebase_messaging: ^9.1.4 - firebase_remote_config: ^0.10.0-dev.3 - - # Data + flutter_svg: ^1.0.3 get_it: ^7.1.3 intl: ^0.17.0 - share: ^2.0.1 - sqflite: ^2.0.0+3 + path_provider: ^2.0.6 provider: ^5.0.0 - url_launcher: ^6.0.3 - path_provider: ^2.0.1 - shared_preferences: ^2.0.5 - flutter_local_notifications: ^5.0.0+4 - - # Look - animations: ^2.0.0 - flutter_svg: ^0.22.0 reorderables: ^0.4.1 - flutter_datetime_picker: ^1.5.1 scrollable_positioned_list: ^0.2.0-nullsafety.0 + share: ^2.0.1 + shared_preferences: ^2.0.5 + sqflite: ^2.0.0+3 + url_launcher: ^6.0.3 dev_dependencies: + flutter_launcher_icons: ^0.9.0 + flutter_lints: ^1.0.4 flutter_test: sdk: flutter - flutter_launcher_icons: ^0.9.0 flutter_icons: android: true diff --git a/test/noor_test.dart b/test/noor_test.dart new file mode 100644 index 0000000..dc09dc5 --- /dev/null +++ b/test/noor_test.dart @@ -0,0 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Test', () {}); +}