~repos /only-bible-app
GIT_CONFIG_PARAMETERS="'http.version=HTTP/1.1'" git clone
https://git.pyrossh.dev/only-bible-app.git
Discussions:
https://groups.google.com/g/rust-embed-devs
The only bible app you will ever need. No ads. No in-app purchases. No distractions.
a3ef5901
—
pyrossh 1 month ago
refactor state management
- .flutter-plugins-dependencies +1 -1
- ios/Podfile.lock +6 -0
- lib/app.dart +126 -89
- lib/main.dart +3 -3
- lib/navigation.dart +28 -40
- lib/screens/bible_select_screen.dart +9 -7
- lib/screens/book_select_screen.dart +1 -2
- lib/screens/chapter_select_screen.dart +1 -2
- lib/screens/chapter_view_screen.dart +4 -5
- lib/sheets/actions_sheet.dart +12 -6
- lib/sheets/highlight_sheet.dart +26 -8
- lib/sheets/settings_sheet.dart +62 -25
- lib/store/actions.dart +105 -23
- lib/store/app_persistor.dart +49 -0
- lib/store/app_state.dart +182 -0
- lib/store/buffer_audio_source.dart +25 -0
- lib/store/state.dart +0 -307
- lib/store/storage.dart +0 -25
- lib/utils.dart +9 -6
- lib/widgets/verses_view.dart +97 -80
- macos/Flutter/GeneratedPluginRegistrant.swift +2 -0
- pubspec.lock +84 -20
- pubspec.yaml +1 -2
- windows/flutter/generated_plugin_registrant.cc +3 -0
- windows/flutter/generated_plugins.cmake +1 -0
.flutter-plugins-dependencies
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"app_review","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/app_review-3.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_native_splash","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_native_splash-2.4.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"integration_test","path":"/opt/homebrew/share/flutter/packages/integration_test/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"just_audio","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio-0.10.5/","shared_darwin_source":true,"native_build":true,"dependencies":["audio_session"],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_ios-6.4.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"webview_flutter_wkwebview","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.24.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"app_review","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/app_review-3.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_native_splash","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_native_splash-2.4.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"integration_test","path":"/opt/homebrew/share/flutter/packages/integration_test/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"just_audio","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio-0.10.5/","native_build":true,"dependencies":["audio_session"],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_android-2.2.22/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.21/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.28/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"webview_flutter_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/webview_flutter_android-4.10.13/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"app_review","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/app_review-3.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"just_audio","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio-0.10.5/","shared_darwin_source":true,"native_build":true,"dependencies":["audio_session"],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"webview_flutter_wkwebview","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.24.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","dependencies":[],"dev_dependency":false},{"name":"flutter_native_splash","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_native_splash-2.4.7/","dependencies":[],"dev_dependency":false},{"name":"just_audio_web","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio_web-0.4.16/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","dependencies":["url_launcher_web"],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.2/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"app_review","dependencies":[]},{"name":"audio_session","dependencies":[]},{"name":"flutter_native_splash","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"just_audio","dependencies":["just_audio_web","audio_session","path_provider"]},{"name":"just_audio_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]},{"name":"webview_flutter","dependencies":["webview_flutter_android","webview_flutter_wkwebview"]},{"name":"webview_flutter_android","dependencies":[]},{"name":"webview_flutter_wkwebview","dependencies":[]}],"date_created":"2026-03-21 18:21:22.875489","version":"3.41.4","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
|
1
|
+
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"app_review","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/app_review-3.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"connectivity_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_native_splash","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_native_splash-2.4.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"integration_test","path":"/opt/homebrew/share/flutter/packages/integration_test/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"just_audio","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio-0.10.5/","shared_darwin_source":true,"native_build":true,"dependencies":["audio_session"],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_ios-6.4.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"webview_flutter_wkwebview","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.24.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"app_review","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/app_review-3.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"connectivity_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_native_splash","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_native_splash-2.4.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"integration_test","path":"/opt/homebrew/share/flutter/packages/integration_test/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"just_audio","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio-0.10.5/","native_build":true,"dependencies":["audio_session"],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_android-2.2.22/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.21/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.28/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"webview_flutter_android","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/webview_flutter_android-4.10.13/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"app_review","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/app_review-3.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"connectivity_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"just_audio","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio-0.10.5/","shared_darwin_source":true,"native_build":true,"dependencies":["audio_session"],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"webview_flutter_wkwebview","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.24.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"audio_session","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/audio_session-0.2.3/","dependencies":[],"dev_dependency":false},{"name":"connectivity_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/connectivity_plus-7.0.0/","dependencies":[],"dev_dependency":false},{"name":"flutter_native_splash","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/flutter_native_splash-2.4.7/","dependencies":[],"dev_dependency":false},{"name":"just_audio_web","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/just_audio_web-0.4.16/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/package_info_plus-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/share_plus-12.0.1/","dependencies":["url_launcher_web"],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/pyrossh/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.2/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"app_review","dependencies":[]},{"name":"audio_session","dependencies":[]},{"name":"connectivity_plus","dependencies":[]},{"name":"flutter_native_splash","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"just_audio","dependencies":["just_audio_web","audio_session","path_provider"]},{"name":"just_audio_web","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]},{"name":"webview_flutter","dependencies":["webview_flutter_android","webview_flutter_wkwebview"]},{"name":"webview_flutter_android","dependencies":[]},{"name":"webview_flutter_wkwebview","dependencies":[]}],"date_created":"2026-03-22 09:38:40.134140","version":"3.41.4","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
ios/Podfile.lock
CHANGED
|
@@ -3,6 +3,8 @@ PODS:
|
|
|
3
3
|
- Flutter
|
|
4
4
|
- audio_session (0.0.1):
|
|
5
5
|
- Flutter
|
|
6
|
+
- connectivity_plus (0.0.1):
|
|
7
|
+
- Flutter
|
|
6
8
|
- Flutter (1.0.0)
|
|
7
9
|
- flutter_native_splash (2.4.3):
|
|
8
10
|
- Flutter
|
|
@@ -27,6 +29,7 @@ PODS:
|
|
|
27
29
|
DEPENDENCIES:
|
|
28
30
|
- app_review (from `.symlinks/plugins/app_review/ios`)
|
|
29
31
|
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
|
32
|
+
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
|
30
33
|
- Flutter (from `Flutter`)
|
|
31
34
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
|
32
35
|
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
|
@@ -42,6 +45,8 @@ EXTERNAL SOURCES:
|
|
|
42
45
|
:path: ".symlinks/plugins/app_review/ios"
|
|
43
46
|
audio_session:
|
|
44
47
|
:path: ".symlinks/plugins/audio_session/ios"
|
|
48
|
+
connectivity_plus:
|
|
49
|
+
:path: ".symlinks/plugins/connectivity_plus/ios"
|
|
45
50
|
Flutter:
|
|
46
51
|
:path: Flutter
|
|
47
52
|
flutter_native_splash:
|
|
@@ -64,6 +69,7 @@ EXTERNAL SOURCES:
|
|
|
64
69
|
SPEC CHECKSUMS:
|
|
65
70
|
app_review: f75e3250ef5694e328a5ecb3f4c445bc7b32d821
|
|
66
71
|
audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
|
|
72
|
+
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
|
67
73
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
|
68
74
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
|
69
75
|
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
lib/app.dart
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "package:async_redux/async_redux.dart";
|
|
1
2
|
import "package:flutter/material.dart";
|
|
2
3
|
import "package:go_router/go_router.dart";
|
|
3
4
|
import "package:only_bible_app/l10n/app_localizations.dart";
|
|
@@ -6,114 +7,150 @@ import "package:only_bible_app/screens/book_select_screen.dart";
|
|
|
6
7
|
import "package:only_bible_app/screens/chapter_select_screen.dart";
|
|
7
8
|
import "package:only_bible_app/screens/chapter_view_screen.dart";
|
|
8
9
|
import "package:only_bible_app/screens/webview_screen.dart";
|
|
9
|
-
import "package:only_bible_app/store/
|
|
10
|
+
import "package:only_bible_app/store/app_state.dart";
|
|
11
|
+
import "package:only_bible_app/store/app_persistor.dart";
|
|
10
12
|
import "package:only_bible_app/theme.dart";
|
|
11
13
|
import "package:only_bible_app/utils.dart";
|
|
12
14
|
|
|
13
15
|
final globalNavigatorKey = GlobalKey<NavigatorState>();
|
|
16
|
+
final persistor = AppPersistor();
|
|
17
|
+
final Store<AppState> store = Store<AppState>(
|
|
18
|
+
initialState: const AppState(),
|
|
19
|
+
persistor: persistor,
|
|
20
|
+
);
|
|
21
|
+
final GoRouter router = buildRouter();
|
|
14
22
|
|
|
23
|
+
GoRouter buildRouter() {
|
|
24
|
+
final s = store.state;
|
|
15
|
-
|
|
25
|
+
return GoRouter(
|
|
16
|
-
|
|
26
|
+
navigatorKey: globalNavigatorKey,
|
|
17
|
-
|
|
27
|
+
initialLocation: s.firstOpen
|
|
18
|
-
|
|
28
|
+
? "/bible"
|
|
19
|
-
|
|
29
|
+
: "/chapter/${Uri.encodeComponent(s.bibleName)}/${s.savedBook}/${s.savedChapter}",
|
|
20
|
-
|
|
30
|
+
routes: [
|
|
21
|
-
|
|
31
|
+
GoRoute(
|
|
22
|
-
|
|
32
|
+
path: "/bible",
|
|
23
|
-
|
|
33
|
+
pageBuilder: (context, state) => const NoTransitionPage(
|
|
24
|
-
|
|
34
|
+
child: BibleSelectScreen(),
|
|
35
|
+
),
|
|
25
36
|
),
|
|
37
|
+
GoRoute(
|
|
38
|
+
path: "/chapter/:bibleName/:bookIndex/:chapterIndex",
|
|
39
|
+
pageBuilder: (context, state) {
|
|
40
|
+
final bibleName =
|
|
41
|
+
Uri.decodeComponent(state.pathParameters["bibleName"]!);
|
|
42
|
+
final bookIndex = int.parse(state.pathParameters["bookIndex"]!);
|
|
43
|
+
final chapterIndex = int.parse(state.pathParameters["chapterIndex"]!);
|
|
44
|
+
final slideDir = state.extra as TextDirection?;
|
|
45
|
+
if (slideDir != null) {
|
|
46
|
+
return CustomTransitionPage(
|
|
47
|
+
key: state.pageKey,
|
|
48
|
+
child: ChapterViewScreen(
|
|
49
|
+
bibleName: bibleName,
|
|
50
|
+
bookIndex: bookIndex,
|
|
51
|
+
chapterIndex: chapterIndex,
|
|
26
|
-
|
|
52
|
+
),
|
|
27
|
-
|
|
53
|
+
transitionsBuilder:
|
|
28
|
-
path: "/chapter/:bibleName/:bookIndex/:chapterIndex",
|
|
29
|
-
|
|
54
|
+
(context, animation, secondaryAnimation, child) {
|
|
55
|
+
const begin = Offset(1.0, 0.0);
|
|
30
|
-
|
|
56
|
+
const end = Offset.zero;
|
|
31
|
-
|
|
57
|
+
const curve = Curves.ease;
|
|
32
|
-
final bookIndex = int.parse(state.pathParameters["bookIndex"]!);
|
|
33
|
-
final chapterIndex = int.parse(state.pathParameters["chapterIndex"]!);
|
|
34
|
-
|
|
58
|
+
final tween = Tween(begin: begin, end: end)
|
|
59
|
+
.chain(CurveTween(curve: curve));
|
|
35
|
-
|
|
60
|
+
return SlideTransition(
|
|
61
|
+
textDirection: slideDir,
|
|
62
|
+
position: animation.drive(tween),
|
|
63
|
+
child: child,
|
|
64
|
+
);
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
}
|
|
36
|
-
return
|
|
68
|
+
return NoTransitionPage(
|
|
37
69
|
key: state.pageKey,
|
|
38
70
|
child: ChapterViewScreen(
|
|
39
71
|
bibleName: bibleName,
|
|
40
72
|
bookIndex: bookIndex,
|
|
41
73
|
chapterIndex: chapterIndex,
|
|
42
74
|
),
|
|
43
|
-
transitionsBuilder:
|
|
44
|
-
(context, animation, secondaryAnimation, child) {
|
|
45
|
-
const begin = Offset(1.0, 0.0);
|
|
46
|
-
const end = Offset.zero;
|
|
47
|
-
const curve = Curves.ease;
|
|
48
|
-
final tween =
|
|
49
|
-
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
|
|
50
|
-
return SlideTransition(
|
|
51
|
-
textDirection: slideDir,
|
|
52
|
-
position: animation.drive(tween),
|
|
53
|
-
child: child,
|
|
54
|
-
);
|
|
55
|
-
},
|
|
56
75
|
);
|
|
76
|
+
},
|
|
77
|
+
),
|
|
78
|
+
GoRoute(
|
|
79
|
+
path: "/books/:bibleName",
|
|
80
|
+
pageBuilder: (context, state) {
|
|
81
|
+
final bibleName =
|
|
82
|
+
Uri.decodeComponent(state.pathParameters["bibleName"]!);
|
|
83
|
+
return NoTransitionPage(
|
|
84
|
+
child: BookSelectScreen(bibleName: bibleName),
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
),
|
|
88
|
+
GoRoute(
|
|
89
|
+
path: "/chapters/:bibleName/:bookIndex",
|
|
90
|
+
pageBuilder: (context, state) {
|
|
91
|
+
final bibleName =
|
|
92
|
+
Uri.decodeComponent(state.pathParameters["bibleName"]!);
|
|
93
|
+
final bookIndex = int.parse(state.pathParameters["bookIndex"]!);
|
|
94
|
+
return NoTransitionPage(
|
|
95
|
+
child:
|
|
96
|
+
ChapterSelectScreen(bibleName: bibleName, bookIndex: bookIndex),
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
),
|
|
100
|
+
GoRoute(
|
|
101
|
+
path: "/webview",
|
|
102
|
+
pageBuilder: (context, state) {
|
|
103
|
+
final url = state.extra as String;
|
|
104
|
+
return NoTransitionPage(
|
|
105
|
+
child: WebViewScreen(url: url),
|
|
106
|
+
);
|
|
107
|
+
},
|
|
108
|
+
),
|
|
109
|
+
],
|
|
110
|
+
);
|
|
57
|
-
|
|
111
|
+
}
|
|
58
|
-
return NoTransitionPage(
|
|
59
|
-
key: state.pageKey,
|
|
60
|
-
child: ChapterViewScreen(
|
|
61
|
-
bibleName: bibleName,
|
|
62
|
-
bookIndex: bookIndex,
|
|
63
|
-
chapterIndex: chapterIndex,
|
|
64
|
-
),
|
|
65
|
-
);
|
|
66
|
-
},
|
|
67
|
-
),
|
|
68
|
-
GoRoute(
|
|
69
|
-
path: "/books/:bibleName",
|
|
70
|
-
pageBuilder: (context, state) {
|
|
71
|
-
final bibleName =
|
|
72
|
-
Uri.decodeComponent(state.pathParameters["bibleName"]!);
|
|
73
|
-
return NoTransitionPage(
|
|
74
|
-
child: BookSelectScreen(bibleName: bibleName),
|
|
75
|
-
);
|
|
76
|
-
},
|
|
77
|
-
),
|
|
78
|
-
GoRoute(
|
|
79
|
-
path: "/chapters/:bibleName/:bookIndex",
|
|
80
|
-
pageBuilder: (context, state) {
|
|
81
|
-
final bibleName =
|
|
82
|
-
Uri.decodeComponent(state.pathParameters["bibleName"]!);
|
|
83
|
-
final bookIndex = int.parse(state.pathParameters["bookIndex"]!);
|
|
84
|
-
return NoTransitionPage(
|
|
85
|
-
child:
|
|
86
|
-
ChapterSelectScreen(bibleName: bibleName, bookIndex: bookIndex),
|
|
87
|
-
);
|
|
88
|
-
},
|
|
89
|
-
),
|
|
90
|
-
GoRoute(
|
|
91
|
-
path: "/webview",
|
|
92
|
-
pageBuilder: (context, state) {
|
|
93
|
-
final url = state.extra as String;
|
|
94
|
-
return NoTransitionPage(
|
|
95
|
-
child: WebViewScreen(url: url),
|
|
96
|
-
);
|
|
97
|
-
},
|
|
98
|
-
),
|
|
99
|
-
],
|
|
100
|
-
);
|
|
101
112
|
|
|
102
113
|
class App extends StatelessWidget {
|
|
103
114
|
const App({super.key});
|
|
104
115
|
|
|
105
116
|
@override
|
|
106
117
|
Widget build(BuildContext context) {
|
|
107
|
-
return
|
|
118
|
+
return StoreProvider<AppState>(
|
|
119
|
+
store: store,
|
|
120
|
+
child: StoreConnector<AppState, _AppVm>(
|
|
121
|
+
vm: () => _AppVmFactory(),
|
|
122
|
+
builder: (context, vm) => MaterialApp.router(
|
|
108
|
-
|
|
123
|
+
routerConfig: router,
|
|
109
|
-
|
|
124
|
+
onGenerateTitle: (context) => context.l.title,
|
|
110
|
-
|
|
125
|
+
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
111
|
-
|
|
126
|
+
supportedLocales: AppLocalizations.supportedLocales,
|
|
112
|
-
|
|
127
|
+
debugShowCheckedModeBanner: false,
|
|
113
|
-
|
|
128
|
+
themeMode: vm.darkMode ? ThemeMode.dark : ThemeMode.light,
|
|
114
|
-
|
|
129
|
+
theme: lightTheme,
|
|
115
|
-
|
|
130
|
+
darkTheme: darkTheme,
|
|
116
|
-
|
|
131
|
+
locale: Locale(vm.languageCode),
|
|
132
|
+
),
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class _AppVm extends Vm {
|
|
139
|
+
final bool darkMode;
|
|
140
|
+
final String languageCode;
|
|
141
|
+
|
|
142
|
+
_AppVm({
|
|
143
|
+
required this.darkMode,
|
|
144
|
+
required this.languageCode,
|
|
145
|
+
}) : super(equals: [darkMode, languageCode]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class _AppVmFactory extends VmFactory<AppState, App, _AppVm> {
|
|
149
|
+
@override
|
|
150
|
+
_AppVm fromStore() {
|
|
151
|
+
return _AppVm(
|
|
152
|
+
darkMode: state.darkMode,
|
|
153
|
+
languageCode: state.languageCode,
|
|
117
154
|
);
|
|
118
155
|
}
|
|
119
156
|
}
|
lib/main.dart
CHANGED
|
@@ -8,9 +8,9 @@ import "package:only_bible_app/app.dart";
|
|
|
8
8
|
import "package:only_bible_app/dialog.dart";
|
|
9
9
|
import "package:only_bible_app/env.dart";
|
|
10
10
|
import "package:only_bible_app/navigation.dart";
|
|
11
|
-
import "package:only_bible_app/store/state.dart";
|
|
12
11
|
|
|
13
12
|
void main() async {
|
|
13
|
+
// await store.persistor.readState();
|
|
14
14
|
// FlutterError.onError = (errorDetails) {
|
|
15
15
|
// SchedulerBinding.instance.addPostFrameCallback((d) {
|
|
16
16
|
// showReportError(
|
|
@@ -34,13 +34,13 @@ void main() async {
|
|
|
34
34
|
// widgetsBinding: WidgetsFlutterBinding.ensureInitialized(),
|
|
35
35
|
// );
|
|
36
36
|
usePathUrlStrategy();
|
|
37
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
37
38
|
FlutterAzureTts.init(
|
|
38
39
|
subscriptionKey: "a9d2d78796924a2a9df2b6d5c1c4a576",
|
|
39
40
|
region: "centralindia",
|
|
40
41
|
withLogs: true,
|
|
41
42
|
);
|
|
42
|
-
// await initState();
|
|
43
|
-
|
|
43
|
+
updateStatusBar(store.state.darkMode);
|
|
44
44
|
runApp(const App());
|
|
45
45
|
// FlutterNativeSplash.remove();
|
|
46
46
|
}
|
lib/navigation.dart
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import "package:atoms_state/atoms_state.dart";
|
|
2
1
|
import "package:flutter/material.dart";
|
|
3
2
|
import "package:flutter/services.dart";
|
|
4
3
|
import "package:app_review/app_review.dart";
|
|
@@ -6,32 +5,11 @@ import "package:go_router/go_router.dart";
|
|
|
6
5
|
import "package:only_bible_app/models.dart";
|
|
7
6
|
import "package:only_bible_app/sheets/settings_sheet.dart";
|
|
8
7
|
import "package:only_bible_app/store/actions.dart";
|
|
9
|
-
import "package:only_bible_app/store/
|
|
8
|
+
import "package:only_bible_app/store/app_state.dart";
|
|
9
|
+
import "package:only_bible_app/app.dart";
|
|
10
10
|
import "package:only_bible_app/utils.dart";
|
|
11
11
|
import "package:share_plus/share_plus.dart";
|
|
12
12
|
|
|
13
|
-
final actionsShownAtom = Atom(
|
|
14
|
-
key: "actionsShown",
|
|
15
|
-
initialState: false,
|
|
16
|
-
reducer: (state, action) {
|
|
17
|
-
if (action is SetActionsShown) {
|
|
18
|
-
return action.value;
|
|
19
|
-
}
|
|
20
|
-
return state;
|
|
21
|
-
},
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
final highlightsShownAtom = Atom(
|
|
25
|
-
key: "highlightsShown",
|
|
26
|
-
initialState: false,
|
|
27
|
-
reducer: (state, action) {
|
|
28
|
-
if (action is SetHighlightsShown) {
|
|
29
|
-
return action.value;
|
|
30
|
-
}
|
|
31
|
-
return state;
|
|
32
|
-
},
|
|
33
|
-
);
|
|
34
|
-
|
|
35
13
|
String _chapterPath(String bibleName, int book, int chapter) {
|
|
36
14
|
return "/chapter/${Uri.encodeComponent(bibleName)}/$book/$chapter";
|
|
37
15
|
}
|
|
@@ -65,8 +43,12 @@ void pushBookChapter(
|
|
|
65
43
|
int chapter,
|
|
66
44
|
TextDirection? dir,
|
|
67
45
|
) {
|
|
68
|
-
dispatch(
|
|
46
|
+
store.dispatch(UpdateChapterAction(book, chapter));
|
|
47
|
+
if (store.state.isPlaying) {
|
|
48
|
+
AppState.player.pause();
|
|
49
|
+
store.dispatch(SetPlayingAction(false));
|
|
50
|
+
}
|
|
69
|
-
|
|
51
|
+
hideActions(context);
|
|
70
52
|
context.push(_chapterPath(bibleName, book, chapter), extra: dir);
|
|
71
53
|
}
|
|
72
54
|
|
|
@@ -76,8 +58,12 @@ void replaceBookChapter(
|
|
|
76
58
|
int book,
|
|
77
59
|
int chapter,
|
|
78
60
|
) {
|
|
79
|
-
dispatch(
|
|
61
|
+
store.dispatch(UpdateChapterAction(book, chapter));
|
|
62
|
+
if (store.state.isPlaying) {
|
|
63
|
+
AppState.player.pause();
|
|
64
|
+
store.dispatch(SetPlayingAction(false));
|
|
65
|
+
}
|
|
80
|
-
|
|
66
|
+
hideActions(context);
|
|
81
67
|
context.go(_chapterPath(bibleName, book, chapter));
|
|
82
68
|
}
|
|
83
69
|
|
|
@@ -169,7 +155,7 @@ Future<void> updateCurrentBible(
|
|
|
169
155
|
int chapter,
|
|
170
156
|
) async {
|
|
171
157
|
hideActions(context);
|
|
172
|
-
dispatch(
|
|
158
|
+
store.dispatch(UpdateBibleAction(name, code));
|
|
173
159
|
context.go(_chapterPath(name, book, chapter));
|
|
174
160
|
}
|
|
175
161
|
|
|
@@ -201,7 +187,13 @@ Future<void> shareVerses(
|
|
|
201
187
|
final version = context.currentLang.languageCode == "en" ? "KJV" : "";
|
|
202
188
|
final title = "$name $chapter:$versesThrough $version";
|
|
203
189
|
final text = verses.map((e) => e.text).join("\n");
|
|
190
|
+
await SharePlus.instance.share(
|
|
191
|
+
ShareParams(
|
|
192
|
+
title: title,
|
|
193
|
+
subject: title,
|
|
204
|
-
|
|
194
|
+
text: "$title\n$text",
|
|
195
|
+
),
|
|
196
|
+
);
|
|
205
197
|
hideActions(context);
|
|
206
198
|
}
|
|
207
199
|
|
|
@@ -217,24 +209,20 @@ void showSettings(BuildContext context, Bible bible) {
|
|
|
217
209
|
}
|
|
218
210
|
|
|
219
211
|
void showActions(BuildContext context, Bible bible) {
|
|
220
|
-
if (!actionsShownAtom.value) {
|
|
221
|
-
|
|
212
|
+
store.dispatch(SetActionsShownAction(true));
|
|
222
|
-
}
|
|
223
213
|
}
|
|
224
214
|
|
|
225
215
|
void hideActions(BuildContext context) {
|
|
226
|
-
if (actionsShownAtom.value) {
|
|
227
|
-
|
|
216
|
+
store.dispatch(SetActionsShownAction(false));
|
|
228
|
-
|
|
217
|
+
store.dispatch(ClearSelectedVersesAction());
|
|
229
|
-
}
|
|
230
218
|
}
|
|
231
219
|
|
|
232
220
|
void showHighlights(BuildContext context) {
|
|
233
|
-
dispatch(
|
|
221
|
+
store.dispatch(SetHighlightsShownAction(true));
|
|
234
222
|
}
|
|
235
223
|
|
|
236
224
|
void hideHighlights(BuildContext context) {
|
|
237
|
-
if (
|
|
225
|
+
if (store.state.highlightsShown) {
|
|
238
|
-
dispatch(
|
|
226
|
+
store.dispatch(SetHighlightsShownAction(false));
|
|
239
227
|
}
|
|
240
228
|
}
|
lib/screens/bible_select_screen.dart
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import "package:atoms_state/atoms_state.dart";
|
|
2
1
|
import "package:flutter/material.dart";
|
|
3
2
|
import "package:only_bible_app/navigation.dart";
|
|
4
3
|
import "package:only_bible_app/store/actions.dart";
|
|
5
|
-
import "package:only_bible_app/
|
|
4
|
+
import "package:only_bible_app/app.dart";
|
|
6
5
|
import "package:only_bible_app/utils.dart";
|
|
7
6
|
import "package:only_bible_app/widgets/scaffold_menu.dart";
|
|
8
7
|
import "package:only_bible_app/widgets/sliver_heading.dart";
|
|
@@ -17,7 +16,10 @@ class BibleSelectScreen extends StatelessWidget {
|
|
|
17
16
|
child: CustomScrollView(
|
|
18
17
|
physics: const BouncingScrollPhysics(),
|
|
19
18
|
slivers: [
|
|
19
|
+
SliverHeading(
|
|
20
|
-
|
|
20
|
+
title: context.l.bibleSelectTitle,
|
|
21
|
+
showClose: !store.state.firstOpen,
|
|
22
|
+
),
|
|
21
23
|
SliverTileGrid(
|
|
22
24
|
listType: ListType.extraLarge,
|
|
23
25
|
children: List.of(
|
|
@@ -33,15 +35,15 @@ class BibleSelectScreen extends StatelessWidget {
|
|
|
33
35
|
)
|
|
34
36
|
: Text(l.languageTitle, textScaleFactor: 1.1),
|
|
35
37
|
onPressed: () {
|
|
36
|
-
if (
|
|
38
|
+
if (store.state.firstOpen) {
|
|
37
|
-
dispatch(
|
|
39
|
+
store.dispatch(FirstOpenDoneAction());
|
|
38
40
|
}
|
|
39
41
|
updateCurrentBible(
|
|
40
42
|
context,
|
|
41
43
|
l.languageTitle,
|
|
42
44
|
l.localeName,
|
|
43
|
-
|
|
45
|
+
store.state.savedBook,
|
|
44
|
-
|
|
46
|
+
store.state.savedChapter,
|
|
45
47
|
);
|
|
46
48
|
},
|
|
47
49
|
);
|
lib/screens/book_select_screen.dart
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
2
|
import "package:go_router/go_router.dart";
|
|
3
3
|
import "package:only_bible_app/navigation.dart";
|
|
4
|
-
import "package:only_bible_app/store/state.dart";
|
|
5
4
|
import "package:only_bible_app/utils.dart";
|
|
6
5
|
import "package:only_bible_app/widgets/scaffold_menu.dart";
|
|
7
6
|
import "package:only_bible_app/widgets/sliver_heading.dart";
|
|
@@ -24,7 +23,7 @@ class BookSelectScreen extends StatelessWidget {
|
|
|
24
23
|
@override
|
|
25
24
|
Widget build(BuildContext context) {
|
|
26
25
|
return FutureBuilder(
|
|
27
|
-
future:
|
|
26
|
+
future: loadBible(bibleName),
|
|
28
27
|
builder: (context, state) {
|
|
29
28
|
return state.when(
|
|
30
29
|
loading: () => ColoredBox(
|
lib/screens/chapter_select_screen.dart
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
2
|
import "package:only_bible_app/models.dart";
|
|
3
3
|
import "package:only_bible_app/navigation.dart";
|
|
4
|
-
import "package:only_bible_app/store/state.dart";
|
|
5
4
|
import "package:only_bible_app/utils.dart";
|
|
6
5
|
import "package:only_bible_app/widgets/scaffold_menu.dart";
|
|
7
6
|
import "package:only_bible_app/widgets/sliver_tile_grid.dart";
|
|
@@ -17,7 +16,7 @@ class ChapterSelectScreen extends StatelessWidget {
|
|
|
17
16
|
@override
|
|
18
17
|
Widget build(BuildContext context) {
|
|
19
18
|
return FutureBuilder(
|
|
20
|
-
future:
|
|
19
|
+
future: loadBible(bibleName),
|
|
21
20
|
builder: (context, state) {
|
|
22
21
|
return state.when(
|
|
23
22
|
loading: () => ColoredBox(
|
lib/screens/chapter_view_screen.dart
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
2
|
import "package:only_bible_app/models.dart";
|
|
3
|
-
import "package:only_bible_app/navigation.dart";
|
|
4
3
|
import "package:only_bible_app/sheets/actions_sheet.dart";
|
|
5
4
|
import "package:only_bible_app/sheets/highlight_sheet.dart";
|
|
6
|
-
import "package:only_bible_app/store/
|
|
5
|
+
import "package:only_bible_app/store/actions.dart";
|
|
7
6
|
import "package:only_bible_app/utils.dart";
|
|
8
7
|
import "package:only_bible_app/widgets/chapter_app_bar.dart";
|
|
9
8
|
import "package:only_bible_app/widgets/verses_view.dart";
|
|
@@ -23,7 +22,7 @@ class ChapterViewScreen extends StatelessWidget {
|
|
|
23
22
|
@override
|
|
24
23
|
Widget build(BuildContext context) {
|
|
25
24
|
return FutureBuilder(
|
|
26
|
-
future:
|
|
25
|
+
future: loadBible(bibleName),
|
|
27
26
|
builder: (context, state) {
|
|
28
27
|
return state.when(
|
|
29
28
|
loading: () => ColoredBox(
|
|
@@ -42,9 +41,9 @@ class ChapterViewScreen extends StatelessWidget {
|
|
|
42
41
|
child: Column(
|
|
43
42
|
children: [
|
|
44
43
|
Expanded(child: VersesView(bible: bible, chapter: chapter)),
|
|
45
|
-
if (
|
|
44
|
+
if (context.state.highlightsShown)
|
|
46
45
|
const HighlightSheet()
|
|
47
|
-
else if (
|
|
46
|
+
else if (context.state.actionsShown)
|
|
48
47
|
ActionsSheet(bible: bible),
|
|
49
48
|
],
|
|
50
49
|
),
|
lib/sheets/actions_sheet.dart
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
2
|
import "package:only_bible_app/models.dart";
|
|
3
3
|
import "package:only_bible_app/navigation.dart";
|
|
4
|
+
import "package:only_bible_app/app.dart";
|
|
4
|
-
import "package:only_bible_app/store/
|
|
5
|
+
import "package:only_bible_app/store/actions.dart";
|
|
5
6
|
import "package:only_bible_app/utils.dart";
|
|
6
7
|
|
|
7
8
|
class ActionsSheet extends StatelessWidget {
|
|
@@ -11,10 +12,10 @@ class ActionsSheet extends StatelessWidget {
|
|
|
11
12
|
|
|
12
13
|
@override
|
|
13
14
|
Widget build(BuildContext context) {
|
|
14
|
-
final iconColor =
|
|
15
|
+
final iconColor = store.state.darkMode
|
|
15
16
|
? Colors.white.withOpacity(0.9)
|
|
16
17
|
: Colors.black.withOpacity(0.9);
|
|
17
|
-
final audioIcon =
|
|
18
|
+
final audioIcon = context.state.isPlaying
|
|
18
19
|
? Icons.pause_circle_outline
|
|
19
20
|
: Icons.play_circle_outline;
|
|
20
21
|
return Container(
|
|
@@ -26,7 +27,12 @@ class ActionsSheet extends StatelessWidget {
|
|
|
26
27
|
children: [
|
|
27
28
|
IconButton(
|
|
28
29
|
padding: EdgeInsets.zero,
|
|
30
|
+
onPressed: () {
|
|
29
|
-
|
|
31
|
+
store.dispatch(RemoveHighlightAction(
|
|
32
|
+
List<Verse>.from(store.state.selectedVerses),
|
|
33
|
+
));
|
|
34
|
+
hideActions(context);
|
|
35
|
+
},
|
|
30
36
|
icon: Icon(Icons.cancel_outlined, size: 28, color: iconColor),
|
|
31
37
|
),
|
|
32
38
|
IconButton(
|
|
@@ -37,14 +43,14 @@ class ActionsSheet extends StatelessWidget {
|
|
|
37
43
|
IconButton(
|
|
38
44
|
padding: EdgeInsets.zero,
|
|
39
45
|
onPressed: () {
|
|
40
|
-
onPlay(context, bible);
|
|
46
|
+
store.state.onPlay(context, bible);
|
|
41
47
|
},
|
|
42
48
|
icon: Icon(audioIcon, size: 34, color: iconColor),
|
|
43
49
|
),
|
|
44
50
|
IconButton(
|
|
45
51
|
padding: EdgeInsets.zero,
|
|
46
52
|
onPressed: () =>
|
|
47
|
-
shareVerses(context, bible,
|
|
53
|
+
shareVerses(context, bible, store.state.selectedVerses),
|
|
48
54
|
icon: Icon(Icons.share_outlined, size: 34, color: iconColor),
|
|
49
55
|
),
|
|
50
56
|
],
|
lib/sheets/highlight_sheet.dart
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
|
+
import "package:only_bible_app/app.dart";
|
|
3
|
+
import "package:only_bible_app/models.dart";
|
|
4
|
+
import "package:only_bible_app/navigation.dart";
|
|
2
|
-
import "package:only_bible_app/store/
|
|
5
|
+
import "package:only_bible_app/store/actions.dart";
|
|
3
6
|
import "package:only_bible_app/theme.dart";
|
|
4
7
|
import "package:only_bible_app/utils.dart";
|
|
5
8
|
import "package:only_bible_app/widgets/highlight_button.dart";
|
|
@@ -9,11 +12,15 @@ class HighlightSheet extends StatelessWidget {
|
|
|
9
12
|
|
|
10
13
|
@override
|
|
11
14
|
Widget build(BuildContext context) {
|
|
12
|
-
final iconColor =
|
|
15
|
+
final iconColor = store.state.darkMode
|
|
13
16
|
? Colors.white.withOpacity(0.9)
|
|
14
17
|
: Colors.black.withOpacity(0.9);
|
|
15
18
|
void onHighlight(int index) {
|
|
19
|
+
store.dispatch(SetHighlightAction(
|
|
20
|
+
List<Verse>.from(store.state.selectedVerses),
|
|
21
|
+
index,
|
|
22
|
+
));
|
|
16
|
-
|
|
23
|
+
hideActions(context);
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
return Container(
|
|
@@ -25,27 +32,38 @@ class HighlightSheet extends StatelessWidget {
|
|
|
25
32
|
children: [
|
|
26
33
|
IconButton(
|
|
27
34
|
padding: EdgeInsets.zero,
|
|
35
|
+
onPressed: () {
|
|
36
|
+
store.dispatch(
|
|
37
|
+
RemoveHighlightAction(
|
|
28
|
-
|
|
38
|
+
List<Verse>.from(store.state.selectedVerses),
|
|
39
|
+
),
|
|
40
|
+
);
|
|
41
|
+
hideActions(context);
|
|
42
|
+
},
|
|
29
43
|
icon: Icon(Icons.cancel_outlined, size: 28, color: iconColor),
|
|
30
44
|
),
|
|
31
45
|
HighlightButton(
|
|
32
46
|
index: 0,
|
|
47
|
+
color:
|
|
33
|
-
|
|
48
|
+
store.state.darkMode ? darkHighlights[0] : lightHighlights[0],
|
|
34
49
|
onHighlightSelected: onHighlight,
|
|
35
50
|
),
|
|
36
51
|
HighlightButton(
|
|
37
52
|
index: 1,
|
|
53
|
+
color:
|
|
38
|
-
|
|
54
|
+
store.state.darkMode ? darkHighlights[1] : lightHighlights[1],
|
|
39
55
|
onHighlightSelected: onHighlight,
|
|
40
56
|
),
|
|
41
57
|
HighlightButton(
|
|
42
58
|
index: 2,
|
|
59
|
+
color:
|
|
43
|
-
|
|
60
|
+
store.state.darkMode ? darkHighlights[2] : lightHighlights[2],
|
|
44
61
|
onHighlightSelected: onHighlight,
|
|
45
62
|
),
|
|
46
63
|
HighlightButton(
|
|
47
64
|
index: 3,
|
|
65
|
+
color:
|
|
48
|
-
|
|
66
|
+
store.state.darkMode ? darkHighlights[3] : lightHighlights[3],
|
|
49
67
|
onHighlightSelected: onHighlight,
|
|
50
68
|
),
|
|
51
69
|
],
|
lib/sheets/settings_sheet.dart
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import "package:atoms_state/atoms_state.dart";
|
|
2
1
|
import "package:flutter/material.dart";
|
|
3
2
|
import "package:only_bible_app/models.dart";
|
|
4
3
|
import "package:only_bible_app/navigation.dart";
|
|
5
4
|
import "package:only_bible_app/store/actions.dart";
|
|
6
|
-
import "package:only_bible_app/
|
|
5
|
+
import "package:only_bible_app/app.dart";
|
|
7
6
|
import "package:only_bible_app/utils.dart";
|
|
8
7
|
import "package:settings_ui/settings_ui.dart";
|
|
9
8
|
|
|
@@ -25,26 +24,33 @@ class SettingsSheet extends StatelessWidget {
|
|
|
25
24
|
),
|
|
26
25
|
sections: [
|
|
27
26
|
SettingsSection(
|
|
27
|
+
title: Text(
|
|
28
|
+
context.l.settingsTitle,
|
|
28
|
-
|
|
29
|
+
style: context.theme.textTheme.headlineMedium,
|
|
30
|
+
),
|
|
29
31
|
margin: const EdgeInsetsDirectional.symmetric(horizontal: 20),
|
|
30
32
|
tiles: [
|
|
31
33
|
SettingsTile.navigation(
|
|
34
|
+
leading:
|
|
32
|
-
|
|
35
|
+
const Icon(Icons.book_outlined, color: Colors.blueAccent),
|
|
33
36
|
title: Text(context.l.bibleTitle),
|
|
34
37
|
value: Text(bible.name),
|
|
35
38
|
onPressed: changeBible,
|
|
36
39
|
),
|
|
37
40
|
SettingsTile.navigation(
|
|
41
|
+
leading:
|
|
38
|
-
|
|
42
|
+
const Icon(Icons.color_lens_outlined, color: Colors.green),
|
|
39
43
|
title: Text(context.l.themeTitle),
|
|
40
44
|
trailing: ToggleButtons(
|
|
41
45
|
onPressed: (int index) {
|
|
42
|
-
dispatch(
|
|
46
|
+
store.dispatch(ToggleDarkModeAction());
|
|
43
47
|
},
|
|
44
48
|
highlightColor: Colors.transparent,
|
|
45
49
|
borderColor: Colors.grey,
|
|
46
50
|
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
|
51
|
+
selectedColor: store.state.darkMode
|
|
52
|
+
? Colors.lightBlue.shade300
|
|
47
|
-
|
|
53
|
+
: Colors.yellowAccent.shade700,
|
|
48
54
|
selectedBorderColor: Colors.grey,
|
|
49
55
|
color: Colors.grey,
|
|
50
56
|
fillColor: Colors.transparent,
|
|
@@ -52,7 +58,7 @@ class SettingsSheet extends StatelessWidget {
|
|
|
52
58
|
minHeight: 36.0,
|
|
53
59
|
minWidth: 50.0,
|
|
54
60
|
),
|
|
55
|
-
isSelected: [!
|
|
61
|
+
isSelected: [!store.state.darkMode, store.state.darkMode],
|
|
56
62
|
children: const [
|
|
57
63
|
Icon(Icons.light_mode),
|
|
58
64
|
Icon(Icons.dark_mode),
|
|
@@ -61,37 +67,61 @@ class SettingsSheet extends StatelessWidget {
|
|
|
61
67
|
),
|
|
62
68
|
SettingsTile(
|
|
63
69
|
title: Text(context.l.incrementFontTitle),
|
|
70
|
+
leading: Icon(
|
|
71
|
+
Icons.font_download,
|
|
64
|
-
|
|
72
|
+
color: context.theme.colorScheme.onSurface,
|
|
73
|
+
),
|
|
65
74
|
trailing: IconButton(
|
|
66
|
-
onPressed: () => dispatch(
|
|
75
|
+
onPressed: () => store.dispatch(UpdateTextScaleAction(0.1)),
|
|
76
|
+
icon: const Icon(
|
|
67
|
-
|
|
77
|
+
Icons.add_circle_outline,
|
|
78
|
+
size: 32,
|
|
79
|
+
color: Colors.redAccent,
|
|
80
|
+
),
|
|
68
81
|
),
|
|
69
82
|
),
|
|
70
83
|
SettingsTile(
|
|
71
84
|
title: Text(context.l.decrementFontTitle),
|
|
85
|
+
leading: Icon(
|
|
86
|
+
Icons.font_download,
|
|
72
|
-
|
|
87
|
+
color: context.theme.colorScheme.onSurface,
|
|
88
|
+
),
|
|
73
89
|
trailing: IconButton(
|
|
74
|
-
onPressed: () => dispatch(
|
|
90
|
+
onPressed: () => store.dispatch(UpdateTextScaleAction(-0.1)),
|
|
91
|
+
icon: const Icon(
|
|
75
|
-
|
|
92
|
+
Icons.remove_circle_outline,
|
|
93
|
+
size: 32,
|
|
94
|
+
color: Colors.blueAccent,
|
|
95
|
+
),
|
|
76
96
|
),
|
|
77
97
|
),
|
|
78
98
|
SettingsTile.switchTile(
|
|
79
|
-
initialValue:
|
|
99
|
+
initialValue: context.state.boldFont,
|
|
100
|
+
leading: Icon(
|
|
101
|
+
Icons.format_bold,
|
|
80
|
-
|
|
102
|
+
color: context.theme.colorScheme.onSurface,
|
|
103
|
+
),
|
|
81
104
|
title: Text(context.l.boldFontTitle),
|
|
82
|
-
onToggle: (value) => dispatch(
|
|
105
|
+
onToggle: (value) => store.dispatch(ToggleBoldFontAction()),
|
|
83
106
|
),
|
|
84
107
|
SettingsTile.switchTile(
|
|
85
|
-
initialValue:
|
|
108
|
+
initialValue: context.state.engTitles,
|
|
109
|
+
leading:
|
|
86
|
-
|
|
110
|
+
Icon(Icons.abc, color: context.theme.colorScheme.onSurface),
|
|
87
111
|
title: Text(context.l.engTitles),
|
|
88
|
-
onToggle: (value) => dispatch(
|
|
112
|
+
onToggle: (value) => store.dispatch(ToggleEngTitlesAction()),
|
|
89
113
|
),
|
|
90
114
|
],
|
|
91
115
|
),
|
|
92
116
|
SettingsSection(
|
|
117
|
+
title: Text(
|
|
118
|
+
context.l.aboutUsTitle,
|
|
93
|
-
|
|
119
|
+
style: context.theme.textTheme.headlineMedium,
|
|
120
|
+
),
|
|
94
|
-
margin: const EdgeInsetsDirectional.symmetric(
|
|
121
|
+
margin: const EdgeInsetsDirectional.symmetric(
|
|
122
|
+
horizontal: 20,
|
|
123
|
+
vertical: 20,
|
|
124
|
+
),
|
|
95
125
|
tiles: [
|
|
96
126
|
SettingsTile.navigation(
|
|
97
127
|
leading: const Icon(Icons.policy_outlined, color: Colors.brown),
|
|
@@ -99,12 +129,16 @@ class SettingsSheet extends StatelessWidget {
|
|
|
99
129
|
onPressed: showPrivacyPolicy,
|
|
100
130
|
),
|
|
101
131
|
SettingsTile.navigation(
|
|
132
|
+
leading: const Icon(
|
|
102
|
-
|
|
133
|
+
Icons.description_outlined,
|
|
134
|
+
color: Colors.blueGrey,
|
|
135
|
+
),
|
|
103
136
|
title: Text(context.l.termsAndConditionsTitle),
|
|
104
137
|
onPressed: showTermsAndConditions,
|
|
105
138
|
),
|
|
106
139
|
SettingsTile.navigation(
|
|
140
|
+
leading:
|
|
107
|
-
|
|
141
|
+
const Icon(Icons.share_outlined, color: Colors.blueAccent),
|
|
108
142
|
title: Text(context.l.shareAppTitle),
|
|
109
143
|
onPressed: shareAppLink,
|
|
110
144
|
),
|
|
@@ -114,7 +148,10 @@ class SettingsSheet extends StatelessWidget {
|
|
|
114
148
|
onPressed: rateApp,
|
|
115
149
|
),
|
|
116
150
|
SettingsTile.navigation(
|
|
151
|
+
leading: Icon(
|
|
152
|
+
Icons.info_outline,
|
|
117
|
-
|
|
153
|
+
color: context.theme.colorScheme.onSurface,
|
|
154
|
+
),
|
|
118
155
|
title: Text(context.l.aboutUsTitle),
|
|
119
156
|
onPressed: showAboutUs,
|
|
120
157
|
),
|
lib/store/actions.dart
CHANGED
|
@@ -1,60 +1,142 @@
|
|
|
1
|
+
import "package:async_redux/async_redux.dart";
|
|
1
2
|
import "package:only_bible_app/models.dart";
|
|
3
|
+
import "package:only_bible_app/store/app_state.dart";
|
|
2
4
|
|
|
3
|
-
sealed class AppAction {}
|
|
4
|
-
|
|
5
|
-
class
|
|
5
|
+
class FirstOpenDoneAction extends ReduxAction<AppState> {
|
|
6
|
+
@override
|
|
7
|
+
AppState reduce() => state.copy(firstOpen: false);
|
|
8
|
+
}
|
|
6
9
|
|
|
7
|
-
class
|
|
10
|
+
class UpdateBibleAction extends ReduxAction<AppState> {
|
|
8
11
|
final String name;
|
|
9
12
|
final String code;
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
UpdateBibleAction(this.name, this.code);
|
|
15
|
+
|
|
16
|
+
@override
|
|
17
|
+
AppState reduce() => state.copy(bibleName: name, languageCode: code);
|
|
12
18
|
}
|
|
13
19
|
|
|
14
|
-
class
|
|
20
|
+
class ToggleEngTitlesAction extends ReduxAction<AppState> {
|
|
21
|
+
@override
|
|
22
|
+
AppState reduce() => state.copy(engTitles: !state.engTitles);
|
|
23
|
+
}
|
|
15
24
|
|
|
16
|
-
class
|
|
25
|
+
class ToggleBoldFontAction extends ReduxAction<AppState> {
|
|
26
|
+
@override
|
|
27
|
+
AppState reduce() => state.copy(boldFont: !state.boldFont);
|
|
28
|
+
}
|
|
17
29
|
|
|
18
|
-
class
|
|
30
|
+
class ToggleDarkModeAction extends ReduxAction<AppState> {
|
|
31
|
+
@override
|
|
32
|
+
AppState reduce() => state.copy(darkMode: !state.darkMode);
|
|
33
|
+
}
|
|
19
34
|
|
|
20
|
-
class
|
|
35
|
+
class UpdateTextScaleAction extends ReduxAction<AppState> {
|
|
21
36
|
final double value;
|
|
22
37
|
|
|
23
|
-
|
|
38
|
+
UpdateTextScaleAction(this.value);
|
|
39
|
+
|
|
40
|
+
@override
|
|
41
|
+
AppState reduce() => state.copy(textScale: state.textScale + value);
|
|
24
42
|
}
|
|
25
43
|
|
|
26
|
-
class
|
|
44
|
+
class UpdateChapterAction extends ReduxAction<AppState> {
|
|
27
45
|
final int book;
|
|
28
46
|
final int chapter;
|
|
29
47
|
|
|
30
|
-
|
|
48
|
+
UpdateChapterAction(this.book, this.chapter);
|
|
49
|
+
|
|
50
|
+
@override
|
|
51
|
+
AppState reduce() => state.copy(savedBook: book, savedChapter: chapter);
|
|
31
52
|
}
|
|
32
53
|
|
|
33
|
-
class
|
|
54
|
+
class SetPlayingAction extends ReduxAction<AppState> {
|
|
34
55
|
final bool value;
|
|
35
56
|
|
|
36
|
-
|
|
57
|
+
SetPlayingAction(this.value);
|
|
58
|
+
|
|
59
|
+
@override
|
|
60
|
+
AppState reduce() => state.copy(isPlaying: value);
|
|
37
61
|
}
|
|
38
62
|
|
|
39
|
-
class
|
|
63
|
+
class SelectVerseAction extends ReduxAction<AppState> {
|
|
40
64
|
final Verse verse;
|
|
41
65
|
|
|
42
|
-
|
|
66
|
+
SelectVerseAction(this.verse);
|
|
67
|
+
|
|
68
|
+
@override
|
|
69
|
+
AppState reduce() {
|
|
70
|
+
final isSelected = state.selectedVerses.any(
|
|
71
|
+
(el) =>
|
|
72
|
+
el.book == verse.book &&
|
|
73
|
+
el.chapter == verse.chapter &&
|
|
74
|
+
el.index == verse.index,
|
|
75
|
+
);
|
|
76
|
+
if (isSelected) {
|
|
77
|
+
return state.copy(
|
|
78
|
+
selectedVerses: state.selectedVerses
|
|
79
|
+
.where((it) => it.index != verse.index)
|
|
80
|
+
.toList(),
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
return state.copy(
|
|
84
|
+
selectedVerses: [...state.selectedVerses, verse],
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
43
88
|
}
|
|
44
89
|
|
|
45
|
-
|
|
46
|
-
class
|
|
90
|
+
class ClearSelectedVersesAction extends ReduxAction<AppState> {
|
|
91
|
+
@override
|
|
47
|
-
|
|
92
|
+
AppState reduce() => state.copy(selectedVerses: []);
|
|
48
93
|
}
|
|
49
94
|
|
|
50
|
-
class
|
|
95
|
+
class SetActionsShownAction extends ReduxAction<AppState> {
|
|
51
96
|
final bool value;
|
|
52
97
|
|
|
53
|
-
|
|
98
|
+
SetActionsShownAction(this.value);
|
|
99
|
+
|
|
100
|
+
@override
|
|
101
|
+
AppState reduce() => state.copy(actionsShown: value);
|
|
54
102
|
}
|
|
55
103
|
|
|
56
|
-
class
|
|
104
|
+
class SetHighlightsShownAction extends ReduxAction<AppState> {
|
|
57
105
|
final bool value;
|
|
58
106
|
|
|
59
|
-
|
|
107
|
+
SetHighlightsShownAction(this.value);
|
|
108
|
+
|
|
109
|
+
@override
|
|
110
|
+
AppState reduce() => state.copy(highlightsShown: value);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class SetHighlightAction extends ReduxAction<AppState> {
|
|
114
|
+
final List<Verse> verses;
|
|
115
|
+
final int colorIndex;
|
|
116
|
+
|
|
117
|
+
SetHighlightAction(this.verses, this.colorIndex);
|
|
118
|
+
|
|
119
|
+
@override
|
|
120
|
+
AppState reduce() {
|
|
121
|
+
final updated = Map<String, int>.from(state.highlights);
|
|
122
|
+
for (final v in verses) {
|
|
123
|
+
updated["${v.book}:${v.chapter}:${v.index}"] = colorIndex;
|
|
124
|
+
}
|
|
125
|
+
return state.copy(highlights: updated);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class RemoveHighlightAction extends ReduxAction<AppState> {
|
|
130
|
+
final List<Verse> verses;
|
|
131
|
+
|
|
132
|
+
RemoveHighlightAction(this.verses);
|
|
133
|
+
|
|
134
|
+
@override
|
|
135
|
+
AppState reduce() {
|
|
136
|
+
final updated = Map<String, int>.from(state.highlights);
|
|
137
|
+
for (final v in verses) {
|
|
138
|
+
updated.remove("${v.book}:${v.chapter}:${v.index}");
|
|
139
|
+
}
|
|
140
|
+
return state.copy(highlights: updated);
|
|
141
|
+
}
|
|
60
142
|
}
|
lib/store/app_persistor.dart
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import "dart:convert";
|
|
2
|
+
import "dart:io";
|
|
3
|
+
|
|
4
|
+
import "package:path_provider/path_provider.dart";
|
|
5
|
+
import "package:async_redux/async_redux.dart";
|
|
6
|
+
import "package:only_bible_app/store/app_state.dart";
|
|
7
|
+
|
|
8
|
+
class AppPersistor extends Persistor<AppState> {
|
|
9
|
+
static const _fileName = "app_state.json";
|
|
10
|
+
|
|
11
|
+
Future<File> get _file async {
|
|
12
|
+
final dir = await getApplicationDocumentsDirectory();
|
|
13
|
+
return File("${dir.path}/$_fileName");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@override
|
|
17
|
+
Future<AppState?> readState() async {
|
|
18
|
+
try {
|
|
19
|
+
final file = await _file;
|
|
20
|
+
if (await file.exists()) {
|
|
21
|
+
final contents = await file.readAsString();
|
|
22
|
+
final json = jsonDecode(contents) as Map<String, dynamic>;
|
|
23
|
+
return AppState.fromJson(json);
|
|
24
|
+
}
|
|
25
|
+
} catch (_) {}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@override
|
|
30
|
+
Future<void> deleteState() async {
|
|
31
|
+
final file = await _file;
|
|
32
|
+
if (await file.exists()) {
|
|
33
|
+
await file.delete();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@override
|
|
38
|
+
Future<void> persistDifference({
|
|
39
|
+
required AppState? lastPersistedState,
|
|
40
|
+
required AppState newState,
|
|
41
|
+
}) async {
|
|
42
|
+
final file = await _file;
|
|
43
|
+
final contents = jsonEncode(newState.toJson());
|
|
44
|
+
await file.writeAsString(contents);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@override
|
|
48
|
+
Duration? get throttle => const Duration(seconds: 1);
|
|
49
|
+
}
|
lib/store/app_state.dart
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import "dart:developer";
|
|
2
|
+
import "package:flutter/material.dart";
|
|
3
|
+
import "package:just_audio/just_audio.dart";
|
|
4
|
+
import "package:only_bible_app/dialog.dart";
|
|
5
|
+
import "package:only_bible_app/models.dart";
|
|
6
|
+
import "package:only_bible_app/store/actions.dart";
|
|
7
|
+
import "package:only_bible_app/app.dart";
|
|
8
|
+
import "package:only_bible_app/store/buffer_audio_source.dart";
|
|
9
|
+
import "package:only_bible_app/theme.dart";
|
|
10
|
+
import "package:only_bible_app/utils.dart";
|
|
11
|
+
|
|
12
|
+
class AppState {
|
|
13
|
+
static final player = AudioPlayer();
|
|
14
|
+
static final noteTextController = TextEditingController();
|
|
15
|
+
|
|
16
|
+
final bool firstOpen;
|
|
17
|
+
final String languageCode;
|
|
18
|
+
final String bibleName;
|
|
19
|
+
final bool engTitles;
|
|
20
|
+
final bool boldFont;
|
|
21
|
+
final bool darkMode;
|
|
22
|
+
final double textScale;
|
|
23
|
+
final int savedBook;
|
|
24
|
+
final int savedChapter;
|
|
25
|
+
final bool isPlaying;
|
|
26
|
+
final List<Verse> selectedVerses;
|
|
27
|
+
final bool actionsShown;
|
|
28
|
+
final bool highlightsShown;
|
|
29
|
+
final Map<String, int> highlights;
|
|
30
|
+
|
|
31
|
+
const AppState({
|
|
32
|
+
this.firstOpen = true,
|
|
33
|
+
this.languageCode = "en",
|
|
34
|
+
this.bibleName = "English",
|
|
35
|
+
this.engTitles = false,
|
|
36
|
+
this.boldFont = false,
|
|
37
|
+
this.darkMode = false,
|
|
38
|
+
this.textScale = 0.0,
|
|
39
|
+
this.savedBook = 0,
|
|
40
|
+
this.savedChapter = 0,
|
|
41
|
+
this.isPlaying = false,
|
|
42
|
+
this.selectedVerses = const [],
|
|
43
|
+
this.actionsShown = false,
|
|
44
|
+
this.highlightsShown = false,
|
|
45
|
+
this.highlights = const {},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
AppState copy({
|
|
49
|
+
bool? firstOpen,
|
|
50
|
+
String? languageCode,
|
|
51
|
+
String? bibleName,
|
|
52
|
+
bool? engTitles,
|
|
53
|
+
bool? boldFont,
|
|
54
|
+
bool? darkMode,
|
|
55
|
+
double? textScale,
|
|
56
|
+
int? savedBook,
|
|
57
|
+
int? savedChapter,
|
|
58
|
+
bool? isPlaying,
|
|
59
|
+
List<Verse>? selectedVerses,
|
|
60
|
+
bool? actionsShown,
|
|
61
|
+
bool? highlightsShown,
|
|
62
|
+
Map<String, int>? highlights,
|
|
63
|
+
}) {
|
|
64
|
+
return AppState(
|
|
65
|
+
firstOpen: firstOpen ?? this.firstOpen,
|
|
66
|
+
languageCode: languageCode ?? this.languageCode,
|
|
67
|
+
bibleName: bibleName ?? this.bibleName,
|
|
68
|
+
engTitles: engTitles ?? this.engTitles,
|
|
69
|
+
boldFont: boldFont ?? this.boldFont,
|
|
70
|
+
darkMode: darkMode ?? this.darkMode,
|
|
71
|
+
textScale: textScale ?? this.textScale,
|
|
72
|
+
savedBook: savedBook ?? this.savedBook,
|
|
73
|
+
savedChapter: savedChapter ?? this.savedChapter,
|
|
74
|
+
isPlaying: isPlaying ?? this.isPlaying,
|
|
75
|
+
selectedVerses: selectedVerses ?? this.selectedVerses,
|
|
76
|
+
actionsShown: actionsShown ?? this.actionsShown,
|
|
77
|
+
highlightsShown: highlightsShown ?? this.highlightsShown,
|
|
78
|
+
highlights: highlights ?? this.highlights,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Map<String, dynamic> toJson() => {
|
|
83
|
+
"firstOpen": firstOpen,
|
|
84
|
+
"languageCode": languageCode,
|
|
85
|
+
"bibleName": bibleName,
|
|
86
|
+
"engTitles": engTitles,
|
|
87
|
+
"boldFont": boldFont,
|
|
88
|
+
"darkMode": darkMode,
|
|
89
|
+
"textScale": textScale,
|
|
90
|
+
"savedBook": savedBook,
|
|
91
|
+
"savedChapter": savedChapter,
|
|
92
|
+
"highlights": highlights,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
factory AppState.fromJson(Map<String, dynamic> json) => AppState(
|
|
96
|
+
firstOpen: json["firstOpen"] as bool? ?? true,
|
|
97
|
+
languageCode: json["languageCode"] as String? ?? "en",
|
|
98
|
+
bibleName: json["bibleName"] as String? ?? "English",
|
|
99
|
+
engTitles: json["engTitles"] as bool? ?? false,
|
|
100
|
+
boldFont: json["boldFont"] as bool? ?? false,
|
|
101
|
+
darkMode: json["darkMode"] as bool? ?? false,
|
|
102
|
+
textScale: (json["textScale"] as num?)?.toDouble() ?? 0.0,
|
|
103
|
+
savedBook: json["savedBook"] as int? ?? 0,
|
|
104
|
+
savedChapter: json["savedChapter"] as int? ?? 0,
|
|
105
|
+
highlights: (json["highlights"] as Map<String, dynamic>?)
|
|
106
|
+
?.map((k, v) => MapEntry(k, v as int)) ??
|
|
107
|
+
const {},
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
Color? getHighlight(Verse v) {
|
|
111
|
+
final key = "${v.book}:${v.chapter}:${v.index}";
|
|
112
|
+
final index = highlights[key];
|
|
113
|
+
if (index == null) return null;
|
|
114
|
+
return darkMode ? darkHighlights[index] : lightHighlights[index];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
bool isVerseSelected(Verse v) {
|
|
118
|
+
return selectedVerses.any(
|
|
119
|
+
(el) =>
|
|
120
|
+
el.book == v.book && el.chapter == v.chapter && el.index == v.index,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
TextStyle getHighlightStyle(BuildContext context, Verse v, bool heading) {
|
|
125
|
+
final isSelected = selectedVerses.any(
|
|
126
|
+
(el) =>
|
|
127
|
+
el.book == v.book && el.chapter == v.chapter && el.index == v.index,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
if (!heading && isSelected) {
|
|
131
|
+
return TextStyle(
|
|
132
|
+
backgroundColor: darkMode ? Colors.grey.shade800 : Colors.grey.shade200,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
final highlight = getHighlight(v);
|
|
136
|
+
if (darkMode) {
|
|
137
|
+
return TextStyle(
|
|
138
|
+
backgroundColor: highlight?.withValues(alpha: 0.7),
|
|
139
|
+
color: highlight != null
|
|
140
|
+
? Colors.white
|
|
141
|
+
: Theme.of(context).colorScheme.onSurface,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return TextStyle(
|
|
145
|
+
backgroundColor: highlight ?? Theme.of(context).colorScheme.surface,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
Future<void> onPlay(BuildContext context, Bible bible) async {
|
|
150
|
+
final versesToPlay = List<Verse>.from(store.state.selectedVerses);
|
|
151
|
+
if (store.state.isPlaying) {
|
|
152
|
+
await player.pause();
|
|
153
|
+
store.dispatch(SetPlayingAction(false));
|
|
154
|
+
} else {
|
|
155
|
+
store.dispatch(SetPlayingAction(true));
|
|
156
|
+
for (final v in versesToPlay) {
|
|
157
|
+
final pathname = "${bible.name}_${v.book}_${v.chapter}_${v.index}";
|
|
158
|
+
try {
|
|
159
|
+
final data =
|
|
160
|
+
await convertText(context.currentLang.audioVoice, v.text);
|
|
161
|
+
await player.setAudioSource(BufferAudioSource(data));
|
|
162
|
+
await player.play();
|
|
163
|
+
await player.stop();
|
|
164
|
+
} catch (err) {
|
|
165
|
+
log(
|
|
166
|
+
"Could not play audio",
|
|
167
|
+
name: "play",
|
|
168
|
+
error: (err.toString(), pathname),
|
|
169
|
+
);
|
|
170
|
+
recordError((err.toString(), pathname).toString(), null);
|
|
171
|
+
if (context.mounted) {
|
|
172
|
+
showError(context, context.l.audioError);
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
} finally {
|
|
176
|
+
await player.pause();
|
|
177
|
+
store.dispatch(SetPlayingAction(false));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
lib/store/buffer_audio_source.dart
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "package:flutter/services.dart";
|
|
2
|
+
import "package:just_audio/just_audio.dart";
|
|
3
|
+
|
|
4
|
+
class BufferAudioSource extends StreamAudioSource {
|
|
5
|
+
final Uint8List _buffer;
|
|
6
|
+
|
|
7
|
+
BufferAudioSource(this._buffer);
|
|
8
|
+
|
|
9
|
+
@override
|
|
10
|
+
Future<StreamAudioResponse> request([int? start, int? end]) {
|
|
11
|
+
start = start ?? 0;
|
|
12
|
+
end = end ?? _buffer.length;
|
|
13
|
+
|
|
14
|
+
return Future.value(
|
|
15
|
+
StreamAudioResponse(
|
|
16
|
+
sourceLength: _buffer.length,
|
|
17
|
+
contentLength: end - start,
|
|
18
|
+
offset: start,
|
|
19
|
+
contentType: "audio/mpeg",
|
|
20
|
+
stream:
|
|
21
|
+
Stream.value(List<int>.from(_buffer.skip(start).take(end - start))),
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
lib/store/state.dart
DELETED
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import "dart:developer";
|
|
2
|
-
import "package:atoms_state/atoms_state.dart";
|
|
3
|
-
import "package:flutter/material.dart";
|
|
4
|
-
import "package:flutter/services.dart";
|
|
5
|
-
import "package:get_storage/get_storage.dart";
|
|
6
|
-
import "package:just_audio/just_audio.dart";
|
|
7
|
-
import "package:only_bible_app/dialog.dart";
|
|
8
|
-
import "package:only_bible_app/models.dart";
|
|
9
|
-
import "package:only_bible_app/store/storage.dart";
|
|
10
|
-
import "package:only_bible_app/theme.dart";
|
|
11
|
-
import "package:only_bible_app/utils.dart";
|
|
12
|
-
import "package:only_bible_app/navigation.dart";
|
|
13
|
-
import "package:only_bible_app/store/actions.dart";
|
|
14
|
-
import "package:path_provider/path_provider.dart";
|
|
15
|
-
|
|
16
|
-
final box = GetStorage("only-bible-app-prefs");
|
|
17
|
-
final player = AudioPlayer();
|
|
18
|
-
final noteTextController = TextEditingController();
|
|
19
|
-
final storage = FileStorage(box: box);
|
|
20
|
-
|
|
21
|
-
Future<void> initState() async {
|
|
22
|
-
await box.initStorage;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
final bibleAtom = AsyncAtom(
|
|
26
|
-
callback: loadBible,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
final firstOpenAtom = Atom(
|
|
30
|
-
storage: storage,
|
|
31
|
-
key: "firstOpen",
|
|
32
|
-
initialState: true,
|
|
33
|
-
reducer: (state, action) {
|
|
34
|
-
if (action is FirstOpenDone) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
return state;
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
final languageCodeAtom = Atom(
|
|
42
|
-
storage: storage,
|
|
43
|
-
key: "languageCode",
|
|
44
|
-
initialState: "en",
|
|
45
|
-
reducer: (state, action) {
|
|
46
|
-
if (action is UpdateBible) {
|
|
47
|
-
return action.code;
|
|
48
|
-
}
|
|
49
|
-
return state;
|
|
50
|
-
},
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
final bibleNameAtom = Atom(
|
|
54
|
-
storage: storage,
|
|
55
|
-
key: "bibleName",
|
|
56
|
-
initialState: "English",
|
|
57
|
-
reducer: (state, action) {
|
|
58
|
-
if (action is UpdateBible) {
|
|
59
|
-
return action.name;
|
|
60
|
-
}
|
|
61
|
-
return state;
|
|
62
|
-
},
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
final engTitlesAtom = Atom(
|
|
66
|
-
storage: storage,
|
|
67
|
-
key: "engTitles",
|
|
68
|
-
initialState: false,
|
|
69
|
-
reducer: (state, action) {
|
|
70
|
-
if (action is ToggleEngTitles) {
|
|
71
|
-
return !state;
|
|
72
|
-
}
|
|
73
|
-
return state;
|
|
74
|
-
},
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
final boldFontAtom = Atom(
|
|
78
|
-
storage: storage,
|
|
79
|
-
key: "boldFont",
|
|
80
|
-
initialState: false,
|
|
81
|
-
reducer: (state, action) {
|
|
82
|
-
if (action is ToggleBoldFont) {
|
|
83
|
-
return !state;
|
|
84
|
-
}
|
|
85
|
-
return state;
|
|
86
|
-
},
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
final darkModeAtom = Atom(
|
|
90
|
-
storage: storage,
|
|
91
|
-
key: "darkMode",
|
|
92
|
-
initialState: false,
|
|
93
|
-
reducer: (state, action) {
|
|
94
|
-
if (action is ToggleDarkMode) {
|
|
95
|
-
updateStatusBar(!state);
|
|
96
|
-
return !state;
|
|
97
|
-
}
|
|
98
|
-
return state;
|
|
99
|
-
},
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
final textScaleAtom = Atom(
|
|
103
|
-
storage: storage,
|
|
104
|
-
key: "textScale",
|
|
105
|
-
initialState: 0.0,
|
|
106
|
-
reducer: (state, action) {
|
|
107
|
-
if (action is UpdateTextScale) {
|
|
108
|
-
return state += action.value;
|
|
109
|
-
}
|
|
110
|
-
return state;
|
|
111
|
-
},
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
final savedBookAtom = Atom(
|
|
115
|
-
storage: storage,
|
|
116
|
-
key: "savedBook",
|
|
117
|
-
initialState: 0,
|
|
118
|
-
reducer: (state, action) {
|
|
119
|
-
if (action is UpdateChapter) {
|
|
120
|
-
return action.book;
|
|
121
|
-
}
|
|
122
|
-
return state;
|
|
123
|
-
},
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
final savedChapterAtom = Atom(
|
|
127
|
-
storage: storage,
|
|
128
|
-
key: "savedChapter",
|
|
129
|
-
initialState: 0,
|
|
130
|
-
reducer: (state, action) {
|
|
131
|
-
if (action is UpdateChapter) {
|
|
132
|
-
return action.chapter;
|
|
133
|
-
}
|
|
134
|
-
return state;
|
|
135
|
-
},
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
final isPlaying = Atom(
|
|
139
|
-
key: "isPlaying",
|
|
140
|
-
initialState: false,
|
|
141
|
-
reducer: (state, action) {
|
|
142
|
-
if (action is SetPlaying) {
|
|
143
|
-
return action.value;
|
|
144
|
-
}
|
|
145
|
-
return state;
|
|
146
|
-
},
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
final selectedVersesAtom = Atom<List<Verse>, AppAction>(
|
|
150
|
-
key: "selectedVerses",
|
|
151
|
-
initialState: [],
|
|
152
|
-
reducer: (state, action) {
|
|
153
|
-
if (action is SelectVerse) {
|
|
154
|
-
if (isVerseSelected(action.verse)) {
|
|
155
|
-
return (state as List<Verse>)
|
|
156
|
-
.removeBy((it) => it.index == action.verse.index)
|
|
157
|
-
.toList();
|
|
158
|
-
} else {
|
|
159
|
-
return (state as List<Verse>).addBy(action.verse).toList();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (action is ClearSelectedVerses) {
|
|
163
|
-
return List<Verse>.from([]);
|
|
164
|
-
}
|
|
165
|
-
return state;
|
|
166
|
-
},
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
Color? getHighlight(Verse v) {
|
|
170
|
-
final key = "${v.book}:${v.chapter}:${v.index}:highlight";
|
|
171
|
-
if (box.hasData(key)) {
|
|
172
|
-
// box.remove(key);
|
|
173
|
-
// print(box.read(key));
|
|
174
|
-
final index = box.read<int>(key);
|
|
175
|
-
if (index == null) {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
return darkModeAtom.value ? darkHighlights[index] : lightHighlights[index];
|
|
179
|
-
}
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
void setHighlight(BuildContext context, int index) {
|
|
184
|
-
for (final v in selectedVersesAtom.value) {
|
|
185
|
-
box.write("${v.book}:${v.chapter}:${v.index}:highlight", index);
|
|
186
|
-
}
|
|
187
|
-
box.save();
|
|
188
|
-
hideActions(context);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
void removeHighlight(BuildContext context) {
|
|
192
|
-
for (final v in selectedVersesAtom.value) {
|
|
193
|
-
box.remove("${v.book}:${v.chapter}:${v.index}:highlight");
|
|
194
|
-
}
|
|
195
|
-
box.save();
|
|
196
|
-
hideActions(context);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
bool isVerseSelected(Verse v) {
|
|
200
|
-
return selectedVersesAtom.value.any((el) =>
|
|
201
|
-
el.book == v.book && el.chapter == v.chapter && el.index == v.index,);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
bool watchVerseSelected(BuildContext context, Verse v) {
|
|
205
|
-
return selectedVersesAtom.watch(context).any((el) =>
|
|
206
|
-
el.book == v.book && el.chapter == v.chapter && el.index == v.index,);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
void onVerseSelected(BuildContext context, Bible bible, Verse v) {
|
|
210
|
-
dispatch(SelectVerse(v));
|
|
211
|
-
if (selectedVersesAtom.value.isNotEmpty) {
|
|
212
|
-
showActions(context, bible);
|
|
213
|
-
}
|
|
214
|
-
if (selectedVersesAtom.value.isEmpty) {
|
|
215
|
-
hideActions(context);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
TextStyle getHighlightStyle(BuildContext context, Verse v, bool heading) {
|
|
220
|
-
if (!heading && watchVerseSelected(context, v)) {
|
|
221
|
-
return TextStyle(
|
|
222
|
-
backgroundColor:
|
|
223
|
-
darkModeAtom.value ? Colors.grey.shade800 : Colors.grey.shade200,
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
if (darkModeAtom.watch(context)) {
|
|
227
|
-
// return TextStyle(
|
|
228
|
-
// color: getHighlight(v) ?? context.theme.colorScheme.onBackground,
|
|
229
|
-
// );
|
|
230
|
-
return TextStyle(
|
|
231
|
-
backgroundColor: getHighlight(v)?.withOpacity(0.7),
|
|
232
|
-
color: getHighlight(v) != null
|
|
233
|
-
? Colors.white
|
|
234
|
-
: context.theme.colorScheme.onSurface,
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
return TextStyle(
|
|
238
|
-
backgroundColor: getHighlight(v) ?? context.theme.colorScheme.surface,
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
void clearEvents(BuildContext context) {
|
|
243
|
-
if (isPlaying.value) {
|
|
244
|
-
pause();
|
|
245
|
-
}
|
|
246
|
-
hideActions(context);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
Future<void> pause() async {
|
|
250
|
-
await player.pause();
|
|
251
|
-
dispatch(const SetPlaying(false));
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
class BufferAudioSource extends StreamAudioSource {
|
|
255
|
-
final Uint8List _buffer;
|
|
256
|
-
|
|
257
|
-
BufferAudioSource(this._buffer);
|
|
258
|
-
|
|
259
|
-
@override
|
|
260
|
-
Future<StreamAudioResponse> request([int? start, int? end]) {
|
|
261
|
-
start = start ?? 0;
|
|
262
|
-
end = end ?? _buffer.length;
|
|
263
|
-
|
|
264
|
-
return Future.value(
|
|
265
|
-
StreamAudioResponse(
|
|
266
|
-
sourceLength: _buffer.length,
|
|
267
|
-
contentLength: end - start,
|
|
268
|
-
offset: start,
|
|
269
|
-
contentType: "audio/mpeg",
|
|
270
|
-
stream:
|
|
271
|
-
Stream.value(List<int>.from(_buffer.skip(start).take(end - start))),
|
|
272
|
-
),
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
Future<void> onPlay(BuildContext context, Bible bible) async {
|
|
278
|
-
final versesToPlay = List<Verse>.from(selectedVersesAtom.value);
|
|
279
|
-
if (isPlaying.value) {
|
|
280
|
-
pause();
|
|
281
|
-
} else {
|
|
282
|
-
dispatch(const SetPlaying(true));
|
|
283
|
-
for (final v in versesToPlay) {
|
|
284
|
-
final directory = await getTemporaryDirectory();
|
|
285
|
-
final pathname = "${bible.name}_${v.book}_${v.chapter}_${v.index}";
|
|
286
|
-
try {
|
|
287
|
-
final data = await convertText(context.currentLang.audioVoice, v.text);
|
|
288
|
-
await player.setAudioSource(BufferAudioSource(data));
|
|
289
|
-
await player.play();
|
|
290
|
-
await player.stop();
|
|
291
|
-
} catch (err) {
|
|
292
|
-
log(
|
|
293
|
-
"Could not play audio",
|
|
294
|
-
name: "play",
|
|
295
|
-
error: (err.toString(), pathname),
|
|
296
|
-
);
|
|
297
|
-
recordError((err.toString(), pathname).toString(), null);
|
|
298
|
-
if (context.mounted) {
|
|
299
|
-
showError(context, context.l.audioError);
|
|
300
|
-
}
|
|
301
|
-
return;
|
|
302
|
-
} finally {
|
|
303
|
-
pause();
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
lib/store/storage.dart
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import "package:atoms_state/atoms_state.dart";
|
|
2
|
-
import "package:get_storage/get_storage.dart";
|
|
3
|
-
|
|
4
|
-
class FileStorage<T> implements Storage {
|
|
5
|
-
GetStorage box;
|
|
6
|
-
|
|
7
|
-
FileStorage({required this.box});
|
|
8
|
-
|
|
9
|
-
@override
|
|
10
|
-
T? get<T>(String key) {
|
|
11
|
-
return box.read(key);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
@override
|
|
15
|
-
Future<void> delete(String key) async {
|
|
16
|
-
await box.remove(key);
|
|
17
|
-
await box.save();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
@override
|
|
21
|
-
Future<void> set(String key, value) async {
|
|
22
|
-
await box.write(key, value);
|
|
23
|
-
await box.save();
|
|
24
|
-
}
|
|
25
|
-
}
|
lib/utils.dart
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import "dart:convert";
|
|
2
|
+
import "package:async_redux/async_redux.dart";
|
|
2
3
|
import "package:http/http.dart" as http;
|
|
3
4
|
import "package:flutter/services.dart";
|
|
4
5
|
import "package:only_bible_app/dialog.dart";
|
|
5
6
|
import "package:only_bible_app/env.dart";
|
|
6
7
|
import "package:only_bible_app/models.dart";
|
|
7
|
-
import "package:only_bible_app/store/
|
|
8
|
+
import "package:only_bible_app/store/app_state.dart";
|
|
9
|
+
|
|
8
10
|
import "package:package_info_plus/package_info_plus.dart";
|
|
9
11
|
import "package:url_launcher/url_launcher.dart";
|
|
10
12
|
import "package:flutter/foundation.dart"
|
|
@@ -41,13 +43,14 @@ extension AsyncSnapshotUtils<E> on AsyncSnapshot<E> {
|
|
|
41
43
|
extension AppContext on BuildContext {
|
|
42
44
|
ThemeData get theme => Theme.of(this);
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
AppState get state => StoreProvider.state<AppState>(this);
|
|
47
|
+
|
|
45
|
-
|
|
48
|
+
AppLocalizations get l => state.engTitles && state.languageCode != "en"
|
|
46
|
-
|
|
49
|
+
? lookupAppLocalizations(const Locale("en"))
|
|
47
|
-
|
|
50
|
+
: AppLocalizations.of(this)!;
|
|
48
51
|
|
|
49
52
|
AppLocalizations get currentLang => supportedLocalizations
|
|
50
|
-
.firstWhere((el) => el.languageCode ==
|
|
53
|
+
.firstWhere((el) => el.languageCode == state.languageCode);
|
|
51
54
|
|
|
52
55
|
double get actionsHeight {
|
|
53
56
|
if (isIOS()) {
|
lib/widgets/verses_view.dart
CHANGED
|
@@ -3,7 +3,9 @@ import "package:flutter/material.dart";
|
|
|
3
3
|
import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
|
|
4
4
|
import "package:only_bible_app/models.dart";
|
|
5
5
|
import "package:only_bible_app/navigation.dart";
|
|
6
|
+
import "package:only_bible_app/app.dart";
|
|
6
|
-
import "package:only_bible_app/store/
|
|
7
|
+
import "package:only_bible_app/store/actions.dart";
|
|
8
|
+
import "package:only_bible_app/utils.dart";
|
|
7
9
|
|
|
8
10
|
class VersesView extends StatelessWidget {
|
|
9
11
|
final Bible bible;
|
|
@@ -15,94 +17,109 @@ class VersesView extends StatelessWidget {
|
|
|
15
17
|
Widget build(BuildContext context) {
|
|
16
18
|
final textStyle = DefaultTextStyle.of(context).style;
|
|
17
19
|
return SwipeDetector(
|
|
18
|
-
|
|
20
|
+
onSwipeLeft: (offset) {
|
|
19
|
-
|
|
21
|
+
nextChapter(context, bible, chapter.book, chapter.index);
|
|
20
|
-
|
|
22
|
+
},
|
|
21
|
-
|
|
23
|
+
onSwipeRight: (offset) {
|
|
22
|
-
|
|
24
|
+
previousChapter(context, bible, chapter.book, chapter.index);
|
|
23
|
-
|
|
25
|
+
},
|
|
24
|
-
|
|
26
|
+
child: Column(
|
|
25
|
-
|
|
27
|
+
children: [
|
|
26
|
-
|
|
28
|
+
Expanded(
|
|
27
|
-
|
|
29
|
+
child: SingleChildScrollView(
|
|
28
|
-
|
|
30
|
+
physics: const BouncingScrollPhysics(),
|
|
29
|
-
|
|
31
|
+
child: Padding(
|
|
30
|
-
|
|
32
|
+
padding: const EdgeInsets.only(left: 20, right: 20),
|
|
31
|
-
|
|
33
|
+
child: Column(
|
|
32
|
-
|
|
34
|
+
mainAxisAlignment: MainAxisAlignment.start,
|
|
33
|
-
|
|
35
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
34
|
-
|
|
36
|
+
children: [
|
|
35
|
-
|
|
37
|
+
// const Padding(
|
|
36
|
-
|
|
38
|
+
// padding: EdgeInsets.only(top: 0),
|
|
37
|
-
|
|
39
|
+
// ),
|
|
38
|
-
|
|
40
|
+
Align(
|
|
39
|
-
|
|
41
|
+
alignment: Alignment.centerLeft,
|
|
40
|
-
|
|
42
|
+
child: Text.rich(
|
|
41
|
-
|
|
43
|
+
textScaler: TextScaler.linear(
|
|
42
|
-
|
|
44
|
+
1.1 + context.state.textScale / 2,
|
|
45
|
+
),
|
|
43
|
-
|
|
46
|
+
textAlign: TextAlign.left,
|
|
44
|
-
|
|
47
|
+
TextSpan(
|
|
45
|
-
|
|
48
|
+
style: context.state.boldFont
|
|
46
|
-
|
|
49
|
+
? textStyle.copyWith(
|
|
47
|
-
|
|
50
|
+
fontWeight: FontWeight.w500,
|
|
48
|
-
|
|
51
|
+
)
|
|
49
|
-
|
|
52
|
+
: textStyle,
|
|
50
|
-
|
|
53
|
+
children: chapter.verses
|
|
51
|
-
|
|
54
|
+
.map(
|
|
52
|
-
|
|
55
|
+
(v) => [
|
|
53
|
-
|
|
56
|
+
if (v.heading != "")
|
|
54
|
-
TextSpan(
|
|
55
|
-
text:
|
|
56
|
-
"${v.heading.replaceAll("<br>", "\n")}\n",
|
|
57
|
-
style:
|
|
58
|
-
getHighlightStyle(context, v, true)
|
|
59
|
-
.copyWith(
|
|
60
|
-
fontSize: 18,
|
|
61
|
-
fontWeight: FontWeight.w600,
|
|
62
|
-
height: 2,
|
|
63
|
-
),
|
|
64
|
-
),
|
|
65
|
-
WidgetSpan(
|
|
66
|
-
child: Transform.translate(
|
|
67
|
-
offset: const Offset(0, -2),
|
|
68
|
-
child: Text("${v.index + 1} ",
|
|
69
|
-
style: Theme.of(context)
|
|
70
|
-
.textTheme
|
|
71
|
-
.labelMedium,),
|
|
72
|
-
),
|
|
73
|
-
),
|
|
74
57
|
TextSpan(
|
|
75
|
-
text: "${v.text}\n",
|
|
76
|
-
|
|
58
|
+
text:
|
|
59
|
+
"${v.heading.replaceAll("<br>", "\n")}\n",
|
|
60
|
+
style: context.state
|
|
77
|
-
getHighlightStyle(context, v,
|
|
61
|
+
.getHighlightStyle(context, v, true)
|
|
78
|
-
|
|
62
|
+
.copyWith(
|
|
79
|
-
|
|
63
|
+
fontSize: 18,
|
|
80
|
-
|
|
64
|
+
fontWeight: FontWeight.w600,
|
|
65
|
+
height: 2,
|
|
81
|
-
|
|
66
|
+
),
|
|
82
67
|
),
|
|
83
|
-
|
|
68
|
+
WidgetSpan(
|
|
69
|
+
child: Transform.translate(
|
|
70
|
+
offset: const Offset(0, -2),
|
|
84
|
-
child:
|
|
71
|
+
child: Text(
|
|
85
|
-
|
|
72
|
+
"${v.index + 1} ",
|
|
86
|
-
|
|
73
|
+
style: Theme.of(context)
|
|
74
|
+
.textTheme
|
|
75
|
+
.labelMedium,
|
|
87
76
|
),
|
|
88
77
|
),
|
|
78
|
+
),
|
|
79
|
+
TextSpan(
|
|
80
|
+
text: "${v.text}\n",
|
|
81
|
+
style: context.state
|
|
82
|
+
.getHighlightStyle(context, v, false),
|
|
83
|
+
recognizer: TapGestureRecognizer()
|
|
84
|
+
..onTap = () {
|
|
85
|
+
store.dispatch(SelectVerseAction(v));
|
|
86
|
+
if (store
|
|
87
|
+
.state.selectedVerses.isNotEmpty) {
|
|
88
|
+
showActions(context, bible);
|
|
89
|
+
}
|
|
90
|
+
if (store
|
|
91
|
+
.state.selectedVerses.isEmpty) {
|
|
92
|
+
hideActions(context);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
const WidgetSpan(
|
|
97
|
+
child: Padding(
|
|
98
|
+
padding: EdgeInsets.only(
|
|
99
|
+
top: 16,
|
|
100
|
+
bottom: 16,
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
),
|
|
89
|
-
|
|
104
|
+
],
|
|
90
|
-
|
|
105
|
+
)
|
|
91
|
-
|
|
106
|
+
.expand((element) => element)
|
|
92
|
-
|
|
107
|
+
.toList(),
|
|
93
|
-
),
|
|
94
108
|
),
|
|
95
109
|
),
|
|
110
|
+
),
|
|
96
|
-
|
|
111
|
+
Padding(
|
|
97
|
-
|
|
112
|
+
padding: EdgeInsets.only(
|
|
98
|
-
|
|
113
|
+
bottom: context.state.actionsShown ? 120 : 0,
|
|
99
114
|
),
|
|
115
|
+
),
|
|
100
|
-
|
|
116
|
+
],
|
|
101
|
-
),
|
|
102
117
|
),
|
|
103
118
|
),
|
|
104
119
|
),
|
|
120
|
+
),
|
|
105
|
-
|
|
121
|
+
],
|
|
106
|
-
|
|
122
|
+
),
|
|
123
|
+
);
|
|
107
124
|
}
|
|
108
125
|
}
|
macos/Flutter/GeneratedPluginRegistrant.swift
CHANGED
|
@@ -7,6 +7,7 @@ import Foundation
|
|
|
7
7
|
|
|
8
8
|
import app_review
|
|
9
9
|
import audio_session
|
|
10
|
+
import connectivity_plus
|
|
10
11
|
import just_audio
|
|
11
12
|
import package_info_plus
|
|
12
13
|
import share_plus
|
|
@@ -17,6 +18,7 @@ import webview_flutter_wkwebview
|
|
|
17
18
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|
18
19
|
AppReviewPlugin.register(with: registry.registrar(forPlugin: "AppReviewPlugin"))
|
|
19
20
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
|
21
|
+
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
|
20
22
|
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
|
21
23
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
|
22
24
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
pubspec.lock
CHANGED
|
@@ -57,14 +57,22 @@ packages:
|
|
|
57
57
|
url: "https://pub.dev"
|
|
58
58
|
source: hosted
|
|
59
59
|
version: "2.13.0"
|
|
60
|
-
|
|
60
|
+
async_redux:
|
|
61
61
|
dependency: "direct main"
|
|
62
62
|
description:
|
|
63
|
-
name:
|
|
63
|
+
name: async_redux
|
|
64
|
-
sha256: "
|
|
64
|
+
sha256: "3f214bcc112a3033a137f47307de30d793a66ff4ee033c1fa6232bbd2e575df0"
|
|
65
65
|
url: "https://pub.dev"
|
|
66
66
|
source: hosted
|
|
67
|
+
version: "27.1.1"
|
|
68
|
+
async_redux_core:
|
|
69
|
+
dependency: transitive
|
|
70
|
+
description:
|
|
71
|
+
name: async_redux_core
|
|
72
|
+
sha256: "7c1075bc14304ae23352ccea5fe16515720a6d4d5a8f6d0e65ae63dc4b243c27"
|
|
73
|
+
url: "https://pub.dev"
|
|
74
|
+
source: hosted
|
|
67
|
-
version: "
|
|
75
|
+
version: "1.5.0"
|
|
68
76
|
audio_session:
|
|
69
77
|
dependency: transitive
|
|
70
78
|
description:
|
|
@@ -201,6 +209,22 @@ packages:
|
|
|
201
209
|
url: "https://pub.dev"
|
|
202
210
|
source: hosted
|
|
203
211
|
version: "3.0.0"
|
|
212
|
+
connectivity_plus:
|
|
213
|
+
dependency: transitive
|
|
214
|
+
description:
|
|
215
|
+
name: connectivity_plus
|
|
216
|
+
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
|
|
217
|
+
url: "https://pub.dev"
|
|
218
|
+
source: hosted
|
|
219
|
+
version: "7.0.0"
|
|
220
|
+
connectivity_plus_platform_interface:
|
|
221
|
+
dependency: transitive
|
|
222
|
+
description:
|
|
223
|
+
name: connectivity_plus_platform_interface
|
|
224
|
+
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
|
|
225
|
+
url: "https://pub.dev"
|
|
226
|
+
source: hosted
|
|
227
|
+
version: "2.0.1"
|
|
204
228
|
convert:
|
|
205
229
|
dependency: transitive
|
|
206
230
|
description:
|
|
@@ -265,6 +289,14 @@ packages:
|
|
|
265
289
|
url: "https://pub.dev"
|
|
266
290
|
source: hosted
|
|
267
291
|
version: "1.2.0"
|
|
292
|
+
dbus:
|
|
293
|
+
dependency: transitive
|
|
294
|
+
description:
|
|
295
|
+
name: dbus
|
|
296
|
+
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
|
|
297
|
+
url: "https://pub.dev"
|
|
298
|
+
source: hosted
|
|
299
|
+
version: "0.7.12"
|
|
268
300
|
equatable:
|
|
269
301
|
dependency: transitive
|
|
270
302
|
description:
|
|
@@ -281,6 +313,14 @@ packages:
|
|
|
281
313
|
url: "https://pub.dev"
|
|
282
314
|
source: hosted
|
|
283
315
|
version: "1.3.3"
|
|
316
|
+
fast_immutable_collections:
|
|
317
|
+
dependency: transitive
|
|
318
|
+
description:
|
|
319
|
+
name: fast_immutable_collections
|
|
320
|
+
sha256: "19f70498af299cbce5ff919dbbecd5abfd9d0c28139004f68d3810ce23dedfb3"
|
|
321
|
+
url: "https://pub.dev"
|
|
322
|
+
source: hosted
|
|
323
|
+
version: "11.1.0"
|
|
284
324
|
ffi:
|
|
285
325
|
dependency: transitive
|
|
286
326
|
description:
|
|
@@ -399,22 +439,6 @@ packages:
|
|
|
399
439
|
description: flutter
|
|
400
440
|
source: sdk
|
|
401
441
|
version: "0.0.0"
|
|
402
|
-
get:
|
|
403
|
-
dependency: transitive
|
|
404
|
-
description:
|
|
405
|
-
name: get
|
|
406
|
-
sha256: "5ed34a7925b85336e15d472cc4cfe7d9ebf4ab8e8b9f688585bf6b50f4c3d79a"
|
|
407
|
-
url: "https://pub.dev"
|
|
408
|
-
source: hosted
|
|
409
|
-
version: "4.7.3"
|
|
410
|
-
get_storage:
|
|
411
|
-
dependency: "direct main"
|
|
412
|
-
description:
|
|
413
|
-
name: get_storage
|
|
414
|
-
sha256: "39db1fffe779d0c22b3a744376e86febe4ade43bf65e06eab5af707dc84185a2"
|
|
415
|
-
url: "https://pub.dev"
|
|
416
|
-
source: hosted
|
|
417
|
-
version: "2.1.1"
|
|
418
442
|
glob:
|
|
419
443
|
dependency: transitive
|
|
420
444
|
description:
|
|
@@ -487,6 +511,14 @@ packages:
|
|
|
487
511
|
url: "https://pub.dev"
|
|
488
512
|
source: hosted
|
|
489
513
|
version: "4.1.2"
|
|
514
|
+
i18n_extension_core:
|
|
515
|
+
dependency: transitive
|
|
516
|
+
description:
|
|
517
|
+
name: i18n_extension_core
|
|
518
|
+
sha256: "5d17d355b95882e803f89c97cad610ab7967a798f8e519563d274f7888bc968e"
|
|
519
|
+
url: "https://pub.dev"
|
|
520
|
+
source: hosted
|
|
521
|
+
version: "5.0.2"
|
|
490
522
|
image:
|
|
491
523
|
dependency: transitive
|
|
492
524
|
description:
|
|
@@ -636,6 +668,14 @@ packages:
|
|
|
636
668
|
url: "https://pub.dev"
|
|
637
669
|
source: hosted
|
|
638
670
|
version: "0.17.6"
|
|
671
|
+
nm:
|
|
672
|
+
dependency: transitive
|
|
673
|
+
description:
|
|
674
|
+
name: nm
|
|
675
|
+
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
|
|
676
|
+
url: "https://pub.dev"
|
|
677
|
+
source: hosted
|
|
678
|
+
version: "0.5.0"
|
|
639
679
|
node_preamble:
|
|
640
680
|
dependency: transitive
|
|
641
681
|
description:
|
|
@@ -812,6 +852,14 @@ packages:
|
|
|
812
852
|
url: "https://pub.dev"
|
|
813
853
|
source: hosted
|
|
814
854
|
version: "0.28.0"
|
|
855
|
+
serverpod_serialization:
|
|
856
|
+
dependency: transitive
|
|
857
|
+
description:
|
|
858
|
+
name: serverpod_serialization
|
|
859
|
+
sha256: "6b087d9e6075dec0973c31220b0ba09f67d31d79cb691f689a704e4b7f8343a3"
|
|
860
|
+
url: "https://pub.dev"
|
|
861
|
+
source: hosted
|
|
862
|
+
version: "3.4.4"
|
|
815
863
|
settings_ui:
|
|
816
864
|
dependency: "direct main"
|
|
817
865
|
description:
|
|
@@ -953,6 +1001,14 @@ packages:
|
|
|
953
1001
|
url: "https://pub.dev"
|
|
954
1002
|
source: hosted
|
|
955
1003
|
version: "1.10.2"
|
|
1004
|
+
sprintf:
|
|
1005
|
+
dependency: transitive
|
|
1006
|
+
description:
|
|
1007
|
+
name: sprintf
|
|
1008
|
+
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
|
1009
|
+
url: "https://pub.dev"
|
|
1010
|
+
source: hosted
|
|
1011
|
+
version: "7.0.0"
|
|
956
1012
|
stack_trace:
|
|
957
1013
|
dependency: transitive
|
|
958
1014
|
description:
|
|
@@ -1169,6 +1225,14 @@ packages:
|
|
|
1169
1225
|
url: "https://pub.dev"
|
|
1170
1226
|
source: hosted
|
|
1171
1227
|
version: "1.2.1"
|
|
1228
|
+
weak_map:
|
|
1229
|
+
dependency: transitive
|
|
1230
|
+
description:
|
|
1231
|
+
name: weak_map
|
|
1232
|
+
sha256: "5f8e5d5ce57dc624db5fae814dd689ccae1f17f92b426e52f0a7cbe7f6f4ab97"
|
|
1233
|
+
url: "https://pub.dev"
|
|
1234
|
+
source: hosted
|
|
1235
|
+
version: "4.0.1"
|
|
1172
1236
|
web:
|
|
1173
1237
|
dependency: transitive
|
|
1174
1238
|
description:
|
pubspec.yaml
CHANGED
|
@@ -21,16 +21,15 @@ dependencies:
|
|
|
21
21
|
flutter_swipe_detector: ^2.0.0
|
|
22
22
|
cupertino_icons: ^1.0.8
|
|
23
23
|
settings_ui: ^2.0.2
|
|
24
|
-
get_storage: ^2.1.1
|
|
25
24
|
share_plus: ^12.0.1
|
|
26
25
|
url_launcher: ^6.3.2
|
|
27
26
|
package_info_plus: ^9.0.0
|
|
28
27
|
flutter_azure_tts: ^1.0.0
|
|
29
28
|
http: ^1.6.0
|
|
30
|
-
atoms_state: ^0.0.2
|
|
31
29
|
webview_flutter: ^4.13.1
|
|
32
30
|
app_review: ^3.0.0
|
|
33
31
|
go_router: ^17.1.0
|
|
32
|
+
async_redux: ^27.1.1
|
|
34
33
|
|
|
35
34
|
dev_dependencies:
|
|
36
35
|
flutter_test:
|
windows/flutter/generated_plugin_registrant.cc
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
#include "generated_plugin_registrant.h"
|
|
8
8
|
|
|
9
|
+
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
|
9
10
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
|
10
11
|
#include <url_launcher_windows/url_launcher_windows.h>
|
|
11
12
|
|
|
12
13
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|
14
|
+
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
|
15
|
+
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
|
13
16
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
|
14
17
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
|
15
18
|
UrlLauncherWindowsRegisterWithRegistrar(
|
windows/flutter/generated_plugins.cmake
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
|
|
5
5
|
list(APPEND FLUTTER_PLUGIN_LIST
|
|
6
|
+
connectivity_plus
|
|
6
7
|
share_plus
|
|
7
8
|
url_launcher_windows
|
|
8
9
|
)
|