~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.
d4129a44
—
pyrossh 1 month ago
add go_router back again
- assets/bibles/en_kjv.bin +0 -0
- lib/app.dart +53 -9
- lib/home.dart +4 -3
- lib/main.dart +23 -20
- lib/store/actions_navigation.dart +27 -7
- lib/store/app_navigator.dart +21 -0
- lib/widgets/chapter_select_sheet.dart +2 -1
- lib/widgets/home_app_bar.dart +3 -2
- lib/widgets/verses_view.dart +6 -3
- pubspec.lock +8 -0
- pubspec.yaml +1 -0
assets/bibles/en_kjv.bin
CHANGED
|
Binary file
|
lib/app.dart
CHANGED
|
@@ -1,27 +1,71 @@
|
|
|
1
1
|
import "package:async_redux/async_redux.dart" show Store, StoreProvider;
|
|
2
2
|
import "package:flutter/material.dart";
|
|
3
|
+
import "package:go_router/go_router.dart";
|
|
3
4
|
import "package:only_bible_app/home.dart";
|
|
5
|
+
import "package:only_bible_app/store/app_navigator.dart";
|
|
4
6
|
import "package:only_bible_app/store/app_state.dart";
|
|
5
7
|
import "package:only_bible_app/theme.dart";
|
|
6
8
|
import "package:only_bible_app/utils.dart";
|
|
7
9
|
|
|
8
10
|
class App extends StatelessWidget {
|
|
11
|
+
final GlobalKey<NavigatorState> globalNavigatorKey;
|
|
9
12
|
final Store<AppState> store;
|
|
13
|
+
late final GoRouter _router;
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
App({super.key, required this.globalNavigatorKey, required this.store}) {
|
|
16
|
+
final s = store.state;
|
|
17
|
+
_router = GoRouter(
|
|
18
|
+
navigatorKey: globalNavigatorKey,
|
|
19
|
+
initialLocation: "/chapter/${s.savedBook}/${s.savedChapter}",
|
|
20
|
+
routes: [
|
|
21
|
+
GoRoute(
|
|
22
|
+
path: "/chapter/:bookIndex/:chapterIndex",
|
|
23
|
+
pageBuilder: (context, state) {
|
|
24
|
+
final bookIndex = int.parse(state.pathParameters["bookIndex"]!);
|
|
25
|
+
final chapterIndex = int.parse(state.pathParameters["chapterIndex"]!);
|
|
26
|
+
final slideDir = state.extra as TextDirection?;
|
|
27
|
+
if (slideDir != null) {
|
|
28
|
+
return CustomTransitionPage(
|
|
29
|
+
key: state.pageKey,
|
|
30
|
+
child: Home(bookIndex: bookIndex, chapterIndex: chapterIndex),
|
|
31
|
+
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
32
|
+
const begin = Offset(1.0, 0.0);
|
|
33
|
+
const end = Offset.zero;
|
|
34
|
+
const curve = Curves.ease;
|
|
35
|
+
final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
|
|
36
|
+
return SlideTransition(
|
|
37
|
+
textDirection: slideDir,
|
|
38
|
+
position: animation.drive(tween),
|
|
39
|
+
child: child,
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return NoTransitionPage(
|
|
45
|
+
key: state.pageKey,
|
|
46
|
+
child: Home(bookIndex: bookIndex, chapterIndex: chapterIndex),
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
),
|
|
50
|
+
],
|
|
51
|
+
);
|
|
52
|
+
}
|
|
12
53
|
|
|
13
54
|
@override
|
|
14
55
|
Widget build(BuildContext context) {
|
|
15
56
|
return StoreProvider<AppState>(
|
|
16
57
|
store: store,
|
|
58
|
+
child: AppRouterScope(
|
|
59
|
+
router: _router,
|
|
17
|
-
|
|
60
|
+
child: Builder(
|
|
18
|
-
|
|
61
|
+
builder: (context) => MaterialApp.router(
|
|
62
|
+
routerConfig: _router,
|
|
19
|
-
|
|
63
|
+
title: "Bible",
|
|
20
|
-
|
|
64
|
+
debugShowCheckedModeBanner: false,
|
|
21
|
-
|
|
65
|
+
themeMode: context.select((s) => s.darkMode) ? ThemeMode.dark : ThemeMode.light,
|
|
22
|
-
|
|
66
|
+
theme: lightTheme,
|
|
23
|
-
|
|
67
|
+
darkTheme: darkTheme,
|
|
24
|
-
|
|
68
|
+
),
|
|
25
69
|
),
|
|
26
70
|
),
|
|
27
71
|
);
|
lib/home.dart
CHANGED
|
@@ -4,13 +4,14 @@ import "package:only_bible_app/widgets/home_app_bar.dart";
|
|
|
4
4
|
import "package:only_bible_app/widgets/verses_view.dart";
|
|
5
5
|
|
|
6
6
|
class Home extends StatelessWidget {
|
|
7
|
+
final int bookIndex;
|
|
7
|
-
|
|
8
|
+
final int chapterIndex;
|
|
9
|
+
|
|
10
|
+
const Home({super.key, required this.bookIndex, required this.chapterIndex});
|
|
8
11
|
|
|
9
12
|
@override
|
|
10
13
|
Widget build(BuildContext context) {
|
|
11
14
|
final bible = context.select((s) => s.bible);
|
|
12
|
-
final bookIndex = context.select((s) => s.savedBook);
|
|
13
|
-
final chapterIndex = context.select((s) => s.savedChapter);
|
|
14
15
|
final book = bible.books![bookIndex];
|
|
15
16
|
final chapter = book.chapters![chapterIndex];
|
|
16
17
|
return Scaffold(
|
lib/main.dart
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
|
+
import "package:flutter/scheduler.dart";
|
|
2
3
|
import "package:flutter/services.dart";
|
|
3
4
|
import "package:flutter/foundation.dart";
|
|
4
5
|
import "package:flutter_azure_tts/flutter_azure_tts.dart";
|
|
@@ -6,11 +7,14 @@ import "package:flutter_web_plugins/url_strategy.dart";
|
|
|
6
7
|
import "package:flutter_native_splash/flutter_native_splash.dart";
|
|
7
8
|
import "package:async_redux/async_redux.dart";
|
|
8
9
|
import "package:only_bible_app/app.dart";
|
|
10
|
+
import "package:only_bible_app/dialog.dart";
|
|
9
11
|
|
|
10
12
|
import "package:only_bible_app/store/app_persistor.dart";
|
|
11
13
|
import "package:only_bible_app/store/app_state.dart";
|
|
12
14
|
import "package:only_bible_app/utils.dart";
|
|
13
15
|
|
|
16
|
+
final navigatorKey = GlobalKey<NavigatorState>();
|
|
17
|
+
|
|
14
18
|
void updateStatusBar(bool v) {
|
|
15
19
|
if (v) {
|
|
16
20
|
SystemChrome.setSystemUIOverlayStyle(
|
|
@@ -37,25 +41,24 @@ void main() async {
|
|
|
37
41
|
FlutterNativeSplash.preserve(
|
|
38
42
|
widgetsBinding: WidgetsFlutterBinding.ensureInitialized(),
|
|
39
43
|
);
|
|
40
|
-
|
|
44
|
+
FlutterError.onError = (errorDetails) {
|
|
45
|
+
FlutterError.presentError(errorDetails);
|
|
41
|
-
|
|
46
|
+
SchedulerBinding.instance.addPostFrameCallback((d) {
|
|
42
|
-
|
|
47
|
+
showReportError(
|
|
43
|
-
|
|
48
|
+
navigatorKey.currentState!.context,
|
|
44
|
-
|
|
49
|
+
errorDetails.exception.toString(),
|
|
45
|
-
|
|
50
|
+
errorDetails.stack,
|
|
46
|
-
|
|
51
|
+
);
|
|
47
|
-
|
|
52
|
+
});
|
|
48
|
-
|
|
53
|
+
};
|
|
49
|
-
|
|
54
|
+
PlatformDispatcher.instance.onError = (error, stack) {
|
|
50
|
-
// Future.delayed(const Duration(seconds: 1), () {
|
|
51
|
-
|
|
55
|
+
showReportError(
|
|
52
|
-
|
|
56
|
+
navigatorKey.currentState!.context,
|
|
53
|
-
|
|
57
|
+
error.toString(),
|
|
54
|
-
|
|
58
|
+
stack,
|
|
55
|
-
// );
|
|
56
|
-
|
|
59
|
+
);
|
|
57
|
-
|
|
60
|
+
return true;
|
|
58
|
-
|
|
61
|
+
};
|
|
59
62
|
usePathUrlStrategy();
|
|
60
63
|
FlutterAzureTts.init(
|
|
61
64
|
subscriptionKey: "a9d2d78796924a2a9df2b6d5c1c4a576",
|
|
@@ -72,6 +75,6 @@ void main() async {
|
|
|
72
75
|
persistor: persistor,
|
|
73
76
|
);
|
|
74
77
|
updateStatusBar(store.state.darkMode);
|
|
75
|
-
runApp(App(store: store));
|
|
78
|
+
runApp(App(globalNavigatorKey: navigatorKey, store: store));
|
|
76
79
|
FlutterNativeSplash.remove();
|
|
77
80
|
}
|
lib/store/actions_navigation.dart
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "package:async_redux/async_redux.dart";
|
|
2
2
|
import "package:flutter/material.dart";
|
|
3
|
+
import "package:go_router/go_router.dart";
|
|
3
4
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
4
5
|
import "package:only_bible_app/store/actions_state.dart" show audioPlayer;
|
|
5
6
|
import "package:only_bible_app/store/app_state.dart";
|
|
@@ -31,9 +32,10 @@ class ShowSettingsAction extends ReduxAction<AppState> {
|
|
|
31
32
|
|
|
32
33
|
class ShowBookSelectAction extends ReduxAction<AppState> {
|
|
33
34
|
final BuildContext buildContext;
|
|
35
|
+
final GoRouter router;
|
|
34
36
|
final Bible bible;
|
|
35
37
|
|
|
36
|
-
ShowBookSelectAction(this.buildContext, this.bible);
|
|
38
|
+
ShowBookSelectAction(this.buildContext, this.router, this.bible);
|
|
37
39
|
|
|
38
40
|
@override
|
|
39
41
|
AppState? reduce() {
|
|
@@ -51,9 +53,9 @@ class ShowBookSelectAction extends ReduxAction<AppState> {
|
|
|
51
53
|
final book = bible.books![index];
|
|
52
54
|
Navigator.of(sheetContext).pop();
|
|
53
55
|
if (book.chapters!.length == 1) {
|
|
54
|
-
dispatch(GoToChapterAction(index, 0));
|
|
56
|
+
dispatch(GoToChapterAction(router, index, 0));
|
|
55
57
|
} else {
|
|
56
|
-
dispatch(ShowChapterSelectAction(buildContext, bible, book));
|
|
58
|
+
dispatch(ShowChapterSelectAction(buildContext, router, bible, book));
|
|
57
59
|
}
|
|
58
60
|
},
|
|
59
61
|
);
|
|
@@ -65,10 +67,11 @@ class ShowBookSelectAction extends ReduxAction<AppState> {
|
|
|
65
67
|
|
|
66
68
|
class ShowChapterSelectAction extends ReduxAction<AppState> {
|
|
67
69
|
final BuildContext buildContext;
|
|
70
|
+
final GoRouter router;
|
|
68
71
|
final Bible bible;
|
|
69
72
|
final Book book;
|
|
70
73
|
|
|
71
|
-
ShowChapterSelectAction(this.buildContext, this.bible, this.book);
|
|
74
|
+
ShowChapterSelectAction(this.buildContext, this.router, this.bible, this.book);
|
|
72
75
|
|
|
73
76
|
@override
|
|
74
77
|
AppState? reduce() {
|
|
@@ -91,14 +94,16 @@ class ShowChapterSelectAction extends ReduxAction<AppState> {
|
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
class GoToChapterAction extends ReduxAction<AppState> {
|
|
97
|
+
final GoRouter router;
|
|
94
98
|
final int book;
|
|
95
99
|
final int chapter;
|
|
96
100
|
|
|
97
|
-
GoToChapterAction(this.book, this.chapter);
|
|
101
|
+
GoToChapterAction(this.router, this.book, this.chapter);
|
|
98
102
|
|
|
99
103
|
@override
|
|
100
104
|
AppState reduce() {
|
|
101
105
|
audioPlayer.pause();
|
|
106
|
+
router.push("/chapter/$book/$chapter");
|
|
102
107
|
return state.copy(
|
|
103
108
|
savedBook: book,
|
|
104
109
|
savedChapter: chapter,
|
|
@@ -108,11 +113,12 @@ class GoToChapterAction extends ReduxAction<AppState> {
|
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
class NextChapterAction extends ReduxAction<AppState> {
|
|
116
|
+
final GoRouter router;
|
|
111
117
|
final Bible bible;
|
|
112
118
|
final int book;
|
|
113
119
|
final int chapter;
|
|
114
120
|
|
|
115
|
-
NextChapterAction(this.bible, this.book, this.chapter);
|
|
121
|
+
NextChapterAction(this.router, this.bible, this.book, this.chapter);
|
|
116
122
|
|
|
117
123
|
@override
|
|
118
124
|
AppState? reduce() {
|
|
@@ -131,6 +137,10 @@ class NextChapterAction extends ReduxAction<AppState> {
|
|
|
131
137
|
|
|
132
138
|
if (newBook == null) return null;
|
|
133
139
|
audioPlayer.pause();
|
|
140
|
+
router.pushReplacement(
|
|
141
|
+
"/chapter/$newBook/$newChapter",
|
|
142
|
+
extra: TextDirection.ltr,
|
|
143
|
+
);
|
|
134
144
|
return state.copy(
|
|
135
145
|
savedBook: newBook,
|
|
136
146
|
savedChapter: newChapter,
|
|
@@ -140,11 +150,12 @@ class NextChapterAction extends ReduxAction<AppState> {
|
|
|
140
150
|
}
|
|
141
151
|
|
|
142
152
|
class PreviousChapterAction extends ReduxAction<AppState> {
|
|
153
|
+
final GoRouter router;
|
|
143
154
|
final Bible bible;
|
|
144
155
|
final int book;
|
|
145
156
|
final int chapter;
|
|
146
157
|
|
|
147
|
-
PreviousChapterAction(this.bible, this.book, this.chapter);
|
|
158
|
+
PreviousChapterAction(this.router, this.bible, this.book, this.chapter);
|
|
148
159
|
|
|
149
160
|
@override
|
|
150
161
|
AppState? reduce() {
|
|
@@ -161,8 +172,17 @@ class PreviousChapterAction extends ReduxAction<AppState> {
|
|
|
161
172
|
newChapter = prevBook.chapters!.length - 1;
|
|
162
173
|
}
|
|
163
174
|
|
|
175
|
+
if (router.canPop()) {
|
|
176
|
+
router.pop();
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
164
180
|
if (newBook == null) return null;
|
|
165
181
|
audioPlayer.pause();
|
|
182
|
+
router.pushReplacement(
|
|
183
|
+
"/chapter/$newBook/$newChapter",
|
|
184
|
+
extra: TextDirection.rtl,
|
|
185
|
+
);
|
|
166
186
|
return state.copy(
|
|
167
187
|
savedBook: newBook,
|
|
168
188
|
savedChapter: newChapter,
|
lib/store/app_navigator.dart
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "package:flutter/material.dart";
|
|
2
|
+
import "package:go_router/go_router.dart";
|
|
3
|
+
|
|
4
|
+
class AppRouterScope extends InheritedWidget {
|
|
5
|
+
final GoRouter router;
|
|
6
|
+
|
|
7
|
+
const AppRouterScope({
|
|
8
|
+
super.key,
|
|
9
|
+
required this.router,
|
|
10
|
+
required super.child,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
static AppRouterScope of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<AppRouterScope>()!;
|
|
14
|
+
|
|
15
|
+
@override
|
|
16
|
+
bool updateShouldNotify(AppRouterScope old) => false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
extension AppRouterContext on BuildContext {
|
|
20
|
+
GoRouter get router => AppRouterScope.of(this).router;
|
|
21
|
+
}
|
lib/widgets/chapter_select_sheet.dart
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "package:flutter/material.dart";
|
|
2
2
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
3
3
|
import "package:only_bible_app/store/actions_navigation.dart";
|
|
4
|
+
import "package:only_bible_app/store/app_navigator.dart";
|
|
4
5
|
import "package:only_bible_app/utils.dart";
|
|
5
6
|
import "package:only_bible_app/widgets/book_tile.dart";
|
|
6
7
|
|
|
@@ -46,7 +47,7 @@ class ChapterSelectSheet extends StatelessWidget {
|
|
|
46
47
|
onTap: () {
|
|
47
48
|
Navigator.of(sheetContext).pop();
|
|
48
49
|
parentContext.dispatch(
|
|
49
|
-
GoToChapterAction(book.index, index),
|
|
50
|
+
GoToChapterAction(parentContext.router, book.index, index),
|
|
50
51
|
);
|
|
51
52
|
},
|
|
52
53
|
);
|
lib/widgets/home_app_bar.dart
CHANGED
|
@@ -2,6 +2,7 @@ import "package:flutter/material.dart";
|
|
|
2
2
|
import "package:only_bible_app/dialog.dart";
|
|
3
3
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
4
4
|
import "package:only_bible_app/store/actions_navigation.dart";
|
|
5
|
+
import "package:only_bible_app/store/app_navigator.dart";
|
|
5
6
|
import "package:only_bible_app/utils.dart";
|
|
6
7
|
|
|
7
8
|
class HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|
@@ -31,7 +32,7 @@ class HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|
|
31
32
|
InkWell(
|
|
32
33
|
key: const Key("bookTitle"),
|
|
33
34
|
enableFeedback: true,
|
|
34
|
-
onTap: () => context.dispatch(ShowBookSelectAction(context, bible)),
|
|
35
|
+
onTap: () => context.dispatch(ShowBookSelectAction(context, context.router, bible)),
|
|
35
36
|
child: Text(
|
|
36
37
|
book.name!,
|
|
37
38
|
style: Theme.of(context).textTheme.headlineMedium,
|
|
@@ -40,7 +41,7 @@ class HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|
|
40
41
|
InkWell(
|
|
41
42
|
key: const Key("chapterTitle"),
|
|
42
43
|
enableFeedback: true,
|
|
43
|
-
onTap: () => context.dispatch(ShowChapterSelectAction(context, bible, book)),
|
|
44
|
+
onTap: () => context.dispatch(ShowChapterSelectAction(context, context.router, bible, book)),
|
|
44
45
|
child: Padding(
|
|
45
46
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
|
46
47
|
child: Text(
|
lib/widgets/verses_view.dart
CHANGED
|
@@ -3,6 +3,7 @@ import "package:flutter/material.dart";
|
|
|
3
3
|
import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
|
|
4
4
|
import "package:only_bible_app/gen/bible.gen.dart";
|
|
5
5
|
import "package:only_bible_app/store/actions_navigation.dart";
|
|
6
|
+
import "package:only_bible_app/store/app_navigator.dart";
|
|
6
7
|
import "package:only_bible_app/widgets/menu_overlay.dart";
|
|
7
8
|
import "package:only_bible_app/store/actions_state.dart";
|
|
8
9
|
import "package:only_bible_app/utils.dart";
|
|
@@ -26,8 +27,10 @@ class VersesView extends StatelessWidget {
|
|
|
26
27
|
return Stack(
|
|
27
28
|
children: [
|
|
28
29
|
SwipeDetector(
|
|
30
|
+
onSwipeLeft: (offset) =>
|
|
29
|
-
|
|
31
|
+
context.dispatch(NextChapterAction(context.router, bible, chapter.book, chapter.index)),
|
|
32
|
+
onSwipeRight: (offset) =>
|
|
30
|
-
|
|
33
|
+
context.dispatch(PreviousChapterAction(context.router, bible, chapter.book, chapter.index)),
|
|
31
34
|
child: SingleChildScrollView(
|
|
32
35
|
key: ValueKey("${chapter.book}-${chapter.index}"),
|
|
33
36
|
physics: const BouncingScrollPhysics(),
|
|
@@ -102,7 +105,7 @@ class VersesView extends StatelessWidget {
|
|
|
102
105
|
decoration: TextDecoration.underline,
|
|
103
106
|
),
|
|
104
107
|
recognizer: TapGestureRecognizer()
|
|
105
|
-
..onTap = () => context.dispatch(GoToChapterAction(bookIndex, chapterIndex)),
|
|
108
|
+
..onTap = () => context.dispatch(GoToChapterAction(context.router, bookIndex, chapterIndex)),
|
|
106
109
|
),
|
|
107
110
|
);
|
|
108
111
|
lastEnd = match.end;
|
pubspec.lock
CHANGED
|
@@ -434,6 +434,14 @@ packages:
|
|
|
434
434
|
url: "https://pub.dev"
|
|
435
435
|
source: hosted
|
|
436
436
|
version: "2.1.3"
|
|
437
|
+
go_router:
|
|
438
|
+
dependency: "direct main"
|
|
439
|
+
description:
|
|
440
|
+
name: go_router
|
|
441
|
+
sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896"
|
|
442
|
+
url: "https://pub.dev"
|
|
443
|
+
source: hosted
|
|
444
|
+
version: "17.1.0"
|
|
437
445
|
golden_screenshot:
|
|
438
446
|
dependency: "direct dev"
|
|
439
447
|
description:
|
pubspec.yaml
CHANGED
|
@@ -24,6 +24,7 @@ dependencies:
|
|
|
24
24
|
http: ^1.6.0
|
|
25
25
|
async_redux: ^27.1.1
|
|
26
26
|
flat_buffers: ^25.9.23
|
|
27
|
+
go_router: ^17.1.0
|
|
27
28
|
|
|
28
29
|
dev_dependencies:
|
|
29
30
|
flutter_test:
|