~repos /only-bible-app

#kotlin#android#ios

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.


lib/app.dart CHANGED
@@ -15,14 +15,19 @@ import "package:only_bible_app/utils.dart";
15
15
  class App extends StatelessWidget {
16
16
  final GlobalKey<NavigatorState> globalNavigatorKey;
17
17
  final Store<AppState> store;
18
+ final AppNavigator navigator;
18
19
  late final GoRouter _router;
19
20
 
20
- App({super.key, required this.globalNavigatorKey, required this.store}) {
21
+ App({super.key, required this.globalNavigatorKey, required this.store, required this.navigator}) {
21
22
  final s = store.state;
22
23
  _router = GoRouter(
23
24
  navigatorKey: globalNavigatorKey,
24
25
  initialLocation:
25
26
  s.firstOpen ? "/bible" : "/chapter/${Uri.encodeComponent(s.bibleName)}/${s.savedBook}/${s.savedChapter}",
27
+ redirect: (context, state) {
28
+ navigator.hideActions();
29
+ return null;
30
+ },
26
31
  routes: [
27
32
  GoRoute(
28
33
  path: "/bible",
@@ -102,23 +107,21 @@ class App extends StatelessWidget {
102
107
 
103
108
  @override
104
109
  Widget build(BuildContext context) {
105
- return StoreProvider<AppState>(
110
+ return AppNavigatorProvider(
106
111
  store: store,
107
- child: AppNavigatorProvider(
108
- navigator: AppNavigator(),
112
+ navigator: navigator,
109
- child: StoreConnector<AppState, _AppVm>(
113
+ child: StoreConnector<AppState, _AppVm>(
110
- vm: () => _AppVmFactory(),
114
+ vm: () => _AppVmFactory(),
111
- builder: (context, vm) => MaterialApp.router(
115
+ builder: (context, vm) => MaterialApp.router(
112
- routerConfig: _router,
116
+ routerConfig: _router,
113
- onGenerateTitle: (context) => context.l.title,
117
+ onGenerateTitle: (context) => context.l.title,
114
- localizationsDelegates: AppLocalizations.localizationsDelegates,
118
+ localizationsDelegates: AppLocalizations.localizationsDelegates,
115
- supportedLocales: AppLocalizations.supportedLocales,
119
+ supportedLocales: AppLocalizations.supportedLocales,
116
- debugShowCheckedModeBanner: false,
120
+ debugShowCheckedModeBanner: false,
117
- themeMode: vm.darkMode ? ThemeMode.dark : ThemeMode.light,
121
+ themeMode: vm.darkMode ? ThemeMode.dark : ThemeMode.light,
118
- theme: lightTheme,
122
+ theme: lightTheme,
119
- darkTheme: darkTheme,
123
+ darkTheme: darkTheme,
120
- locale: Locale(vm.languageCode),
124
+ locale: Locale(vm.languageCode),
121
- ),
122
125
  ),
123
126
  ),
124
127
  );
lib/main.dart CHANGED
@@ -74,6 +74,7 @@ void main() async {
74
74
  );
75
75
  updateStatusBar(store.state.darkMode);
76
76
  await store.dispatchAndWait(LoadBibleAction());
77
+ final navigator = AppNavigator(store);
77
- runApp(App(globalNavigatorKey: globalNavigatorKey, store: store));
78
+ runApp(App(globalNavigatorKey: globalNavigatorKey, store: store, navigator: navigator));
78
79
  FlutterNativeSplash.remove();
79
80
  }
lib/sheets/actions_sheet.dart CHANGED
@@ -23,7 +23,7 @@ class ActionsSheet extends StatelessWidget {
23
23
  index,
24
24
  ),
25
25
  );
26
- context.nav.hideActions(context);
26
+ context.nav.hideActions();
27
27
  }
28
28
 
29
29
  return Material(
@@ -44,7 +44,7 @@ class ActionsSheet extends StatelessWidget {
44
44
  List<Verse>.from(context.state.selectedVerses),
45
45
  ),
46
46
  );
47
- context.nav.hideActions(context);
47
+ context.nav.hideActions();
48
48
  },
49
49
  icon: Icon(Icons.cancel_outlined, size: 28, color: iconColor),
50
50
  ),
lib/store/app_navigator.dart CHANGED
@@ -1,3 +1,4 @@
1
+ import "package:async_redux/async_redux.dart" show Store, StoreProvider;
1
2
  import "package:flutter/material.dart";
2
3
  import "package:app_review/app_review.dart";
3
4
  import "package:go_router/go_router.dart";
@@ -9,21 +10,44 @@ import "package:only_bible_app/store/app_state.dart";
9
10
  import "package:only_bible_app/utils.dart";
10
11
  import "package:share_plus/share_plus.dart";
11
12
 
12
- class AppNavigatorProvider extends InheritedWidget {
13
+ class AppNavigatorProvider extends StatelessWidget {
14
+ final Store<AppState> store;
13
15
  final AppNavigator navigator;
16
+ final Widget child;
14
17
 
15
18
  const AppNavigatorProvider({
16
19
  super.key,
20
+ required this.store,
17
21
  required this.navigator,
18
- required super.child,
22
+ required this.child,
19
23
  });
20
24
 
21
25
  static AppNavigator of(BuildContext context) {
22
- return context.dependOnInheritedWidgetOfExactType<AppNavigatorProvider>()!.navigator;
26
+ return context.dependOnInheritedWidgetOfExactType<_InheritedAppNavigator>()!.navigator;
23
27
  }
24
28
 
25
29
  @override
30
+ Widget build(BuildContext context) {
31
+ return StoreProvider<AppState>(
32
+ store: store,
33
+ child: _InheritedAppNavigator(
34
+ navigator: navigator,
35
+ child: child,
36
+ ),
37
+ );
38
+ }
39
+ }
40
+
41
+ class _InheritedAppNavigator extends InheritedWidget {
42
+ final AppNavigator navigator;
43
+
44
+ const _InheritedAppNavigator({
45
+ required this.navigator,
46
+ required super.child,
47
+ });
48
+
49
+ @override
26
- bool updateShouldNotify(AppNavigatorProvider oldWidget) => false;
50
+ bool updateShouldNotify(_InheritedAppNavigator oldWidget) => false;
27
51
  }
28
52
 
29
53
  extension AppNavigatorContext on BuildContext {
@@ -31,8 +55,11 @@ extension AppNavigatorContext on BuildContext {
31
55
  }
32
56
 
33
57
  class AppNavigator {
58
+ final Store<AppState> store;
34
59
  OverlayEntry? _actionsOverlay;
35
60
 
61
+ AppNavigator(this.store);
62
+
36
63
  String _chapterPath(String bibleName, int book, int chapter) {
37
64
  return "/chapter/${Uri.encodeComponent(bibleName)}/$book/$chapter";
38
65
  }
@@ -44,12 +71,12 @@ class AppNavigator {
44
71
  int chapter,
45
72
  TextDirection? dir,
46
73
  ) {
47
- context.dispatch(UpdateChapterAction(book, chapter));
74
+ store.dispatch(UpdateChapterAction(book, chapter));
48
- if (context.state.isPlaying) {
75
+ if (store.state.isPlaying) {
49
76
  AppState.player.pause();
50
- context.dispatch(SetPlayingAction(false));
77
+ store.dispatch(SetPlayingAction(false));
51
78
  }
52
- hideActions(context);
79
+ hideActions();
53
80
  context.push(_chapterPath(bibleName, book, chapter), extra: dir);
54
81
  }
55
82
 
@@ -59,12 +86,12 @@ class AppNavigator {
59
86
  int book,
60
87
  int chapter,
61
88
  ) {
62
- context.dispatch(UpdateChapterAction(book, chapter));
89
+ store.dispatch(UpdateChapterAction(book, chapter));
63
- if (context.state.isPlaying) {
90
+ if (store.state.isPlaying) {
64
91
  AppState.player.pause();
65
- context.dispatch(SetPlayingAction(false));
92
+ store.dispatch(SetPlayingAction(false));
66
93
  }
67
- hideActions(context);
94
+ hideActions();
68
95
  context.go(_chapterPath(bibleName, book, chapter));
69
96
  }
70
97
 
@@ -151,9 +178,9 @@ class AppNavigator {
151
178
  int book,
152
179
  int chapter,
153
180
  ) async {
154
- hideActions(context);
181
+ hideActions();
155
- await context.dispatchAndWait(UpdateBibleAction(name, code));
182
+ await store.dispatchAndWait(UpdateBibleAction(name, code));
156
- await context.dispatchAndWait(LoadBibleAction());
183
+ await store.dispatchAndWait(LoadBibleAction());
157
184
  context.go(_chapterPath(name, book, chapter));
158
185
  }
159
186
 
@@ -190,7 +217,7 @@ class AppNavigator {
190
217
  text: "$title\n$text",
191
218
  ),
192
219
  );
193
- hideActions(context);
220
+ hideActions();
194
221
  }
195
222
 
196
223
  void showSettings(BuildContext context, Bible bible) {
@@ -223,9 +250,11 @@ class AppNavigator {
223
250
  overlay.insert(_actionsOverlay!);
224
251
  }
225
252
 
226
- void hideActions(BuildContext context) {
253
+ void hideActions() {
254
+ if (_actionsOverlay != null) {
227
- _actionsOverlay?.remove();
255
+ _actionsOverlay?.remove();
228
- _actionsOverlay = null;
256
+ _actionsOverlay = null;
229
- context.dispatch(ClearSelectedVersesAction());
257
+ store.dispatch(ClearSelectedVersesAction());
258
+ }
230
259
  }
231
260
  }
lib/theme.dart CHANGED
@@ -128,11 +128,17 @@ final lightTheme = ThemeData(
128
128
  style: TextButton.styleFrom(
129
129
  enableFeedback: true,
130
130
  padding: EdgeInsets.zero,
131
- shape: const RoundedRectangleBorder(),
131
+ shape: RoundedRectangleBorder(
132
+ borderRadius: BorderRadius.circular(4),
133
+ side: BorderSide(
134
+ color: Colors.black,
135
+ width: 1,
136
+ ),
137
+ ),
132
- elevation: 0.5,
138
+ elevation: 2,
133
139
  shadowColor: Colors.black,
134
- backgroundColor: const Color(0xFFEAE9E9),
135
- foregroundColor: lightColorScheme.primary,
140
+ backgroundColor: lightColorScheme.surface,
141
+ foregroundColor: Colors.black,
136
142
  textStyle: const TextStyle(
137
143
  fontSize: 18,
138
144
  fontWeight: FontWeight.w500,
@@ -235,17 +241,30 @@ final darkTheme = lightTheme.copyWith(
235
241
  ),
236
242
  ),
237
243
  textButtonTheme: TextButtonThemeData(
238
- style: ButtonStyle(
244
+ style: TextButton.styleFrom(
239
245
  enableFeedback: lightTheme.textButtonTheme.style!.enableFeedback,
240
- padding: lightTheme.textButtonTheme.style!.padding,
241
- shape: lightTheme.textButtonTheme.style!.shape,
242
- textStyle: lightTheme.textButtonTheme.style!.textStyle,
243
- elevation: WidgetStateProperty.all(1),
246
+ padding: EdgeInsets.all(0),
247
+ shape: RoundedRectangleBorder(
248
+ borderRadius: BorderRadius.circular(4),
249
+ side: BorderSide(
250
+ color: darkColorScheme.onSurface,
251
+ width: 1,
252
+ ),
253
+ ),
254
+ textStyle: const TextStyle(
255
+ fontSize: 18,
256
+ fontWeight: FontWeight.w500,
257
+ letterSpacing: 0,
258
+ ),
259
+ elevation: 0,
244
- shadowColor: WidgetStateProperty.all(Colors.white),
260
+ shadowColor: Colors.white,
245
- backgroundColor: WidgetStateProperty.all(darkColorScheme.tertiary),
261
+ backgroundColor: darkColorScheme.surface,
246
- foregroundColor: WidgetStateProperty.all(darkColorScheme.primary),
262
+ foregroundColor: darkColorScheme.primary,
247
263
  ),
248
264
  ),
265
+ // shadowColor: Colors.black,
266
+ // backgroundColor: lightColorScheme.surface,
267
+ // foregroundColor: Colors.black,
249
268
  textTheme: TextTheme(
250
269
  bodyMedium: lightTheme.textTheme.bodyMedium!.copyWith(
251
270
  color: darkColorScheme.onSurface,
lib/widgets/chapter_app_bar.dart CHANGED
@@ -30,14 +30,25 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
30
30
  children: [
31
31
  InkWell(
32
32
  enableFeedback: true,
33
- onTap: () => context.nav.changeChapter(context, bible, book, book.index),
34
- onLongPress: () => context.nav.changeBook(context, bible),
33
+ onTap: () => context.nav.changeBook(context, bible),
35
34
  child: Text(
36
- "$bookName ${chapter.index + 1}",
35
+ bookName,
37
36
  style: Theme.of(context).textTheme.headlineMedium,
38
37
  key: const Key("bookTitle"),
39
38
  ),
40
39
  ),
40
+ InkWell(
41
+ enableFeedback: true,
42
+ onTap: () => context.nav.changeChapter(context, bible, book, book.index),
43
+ child: Padding(
44
+ padding: const EdgeInsets.only(left: 16),
45
+ child: Text(
46
+ "${chapter.index + 1}",
47
+ style: Theme.of(context).textTheme.headlineMedium,
48
+ key: const Key("chapterTitle"),
49
+ ),
50
+ ),
51
+ ),
41
52
  Expanded(
42
53
  child: Row(
43
54
  mainAxisAlignment: MainAxisAlignment.end,
@@ -46,7 +57,7 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
46
57
  padding: const EdgeInsets.only(left: 10),
47
58
  child: IconButton(
48
59
  padding: EdgeInsets.zero,
49
- icon: const Icon(Icons.more_vert, size: 24),
60
+ icon: const Icon(Icons.more_vert, size: 28),
50
61
  onPressed: () => context.nav.showSettings(context, bible),
51
62
  ),
52
63
  ),
lib/widgets/sliver_heading.dart CHANGED
@@ -27,7 +27,14 @@ class SliverHeading extends StatelessWidget {
27
27
  Expanded(
28
28
  child: Text(
29
29
  title,
30
- style: Theme.of(context).textTheme.headlineMedium,
30
+ style: Theme.of(context).textTheme.headlineMedium?.copyWith(
31
+ shadows: [
32
+ Shadow(
33
+ color: Theme.of(context).shadowColor.withOpacity(0.3),
34
+ offset: const Offset(1, 1),
35
+ ),
36
+ ],
37
+ ),
31
38
  ),
32
39
  ),
33
40
  if (showClose)
lib/widgets/verses_view.dart CHANGED
@@ -16,7 +16,7 @@ class VersesView extends StatelessWidget {
16
16
  if (context.state.selectedVerses.isNotEmpty) {
17
17
  context.nav.showActions(context, bible);
18
18
  } else {
19
- context.nav.hideActions(context);
19
+ context.nav.hideActions();
20
20
  }
21
21
  }
22
22