~repos /only-bible-app

#kotlin#android#ios

git clone https://pyrossh.dev/repos/only-bible-app.git

The only bible app you will ever need. No ads. No in-app purchases. No distractions.


cf00613a pyrossh

2 years ago
fix navigation
lib/app.dart CHANGED
@@ -11,14 +11,23 @@ class App extends StatelessWidget {
11
11
  @override
12
12
  Widget build(BuildContext context) {
13
13
  return MaterialApp(
14
- title: "Only Bible App",
14
+ title: "Only Bible App",
15
- localizationsDelegates: AppLocalizations.localizationsDelegates,
15
+ localizationsDelegates: AppLocalizations.localizationsDelegates,
16
- supportedLocales: AppLocalizations.supportedLocales,
16
+ supportedLocales: AppLocalizations.supportedLocales,
17
- debugShowCheckedModeBanner: false,
17
+ debugShowCheckedModeBanner: false,
18
- themeMode: darkMode.reactiveValue(context) ? ThemeMode.dark : ThemeMode.light,
18
+ themeMode: darkMode.reactiveValue(context) ? ThemeMode.dark : ThemeMode.light,
19
- theme: lightTheme,
19
+ theme: lightTheme,
20
- darkTheme: darkTheme,
20
+ darkTheme: darkTheme,
21
+ home: FutureBuilder(
22
+ future: loadPrefs(),
23
+ builder: (context, snapshot) {
24
+ if (snapshot.connectionState == ConnectionState.done) {
21
- home: ChapterViewScreen(book: bookIndex.value, chapter: chapterIndex.value),
25
+ return ChapterViewScreen(book: snapshot.data!.$1, chapter: snapshot.data!.$2);
26
+ }
27
+ return const Center(
28
+ child: CircularProgressIndicator(),
22
- );
29
+ );
30
+ },
31
+ ));
23
32
  }
24
33
  }
lib/main.dart CHANGED
@@ -30,6 +30,7 @@ void main() async {
30
30
  // };
31
31
  FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
32
32
  await initPersistentValueNotifier();
33
+ await loadPrefs();
33
34
  await loadBible();
34
35
  await updateStatusBar();
35
36
  runApp(const App());
lib/models.dart CHANGED
@@ -188,4 +188,4 @@ List<Book> getBibleFromText(String text) {
188
188
  );
189
189
  }
190
190
  return books;
191
- }
191
+ }
lib/screens/chapter_select_screen.dart CHANGED
@@ -3,6 +3,7 @@ import "package:only_bible_app/state.dart";
3
3
  import "package:only_bible_app/widgets/scaffold_menu.dart";
4
4
  import "package:only_bible_app/widgets/sliver_tile_grid.dart";
5
5
  import "package:only_bible_app/widgets/sliver_heading.dart";
6
+ import "package:only_bible_app/screens/chapter_view_screen.dart";
6
7
 
7
8
  class ChapterSelectScreen extends StatelessWidget {
8
9
  final int selectedBookIndex;
@@ -10,7 +11,11 @@ class ChapterSelectScreen extends StatelessWidget {
10
11
  const ChapterSelectScreen({super.key, required this.selectedBookIndex});
11
12
 
12
13
  onChapterSelected(BuildContext context, int index) {
14
+ Navigator.of(context).pushReplacement(
15
+ createNoTransitionPageRoute(
13
- navigateBookChapter(context, selectedBookIndex, index, true);
16
+ ChapterViewScreen(book: selectedBookIndex, chapter: index),
17
+ ),
18
+ );
14
19
  }
15
20
 
16
21
  @override
lib/screens/chapter_view_screen.dart CHANGED
@@ -1,11 +1,12 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:flutter_reactive_value/flutter_reactive_value.dart";
3
3
  import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
4
+ import "package:only_bible_app/widgets/actions_bar.dart";
4
5
  import "package:only_bible_app/widgets/header.dart";
5
6
  import "package:only_bible_app/state.dart";
6
- import "package:only_bible_app/widgets/play_button.dart";
7
7
  import "package:only_bible_app/widgets/sidebar.dart";
8
8
  import "package:only_bible_app/widgets/verse_list.dart";
9
+ import "package:provider/provider.dart";
9
10
 
10
11
  class ChapterViewScreen extends StatelessWidget {
11
12
  final int book;
@@ -15,48 +16,42 @@ class ChapterViewScreen extends StatelessWidget {
15
16
 
16
17
  @override
17
18
  Widget build(BuildContext context) {
19
+ return ChangeNotifierProvider(
18
- final isDesktop = isWide(context);
20
+ create: (context) => ChapterViewModel(
21
+ book: book,
22
+ chapter: chapter,
19
- final showPlay = selectedVerses.reactiveValue(context).isNotEmpty;
23
+ selectedVerses: [],
24
+ ),
20
- return Scaffold(
25
+ child: Scaffold(
21
- backgroundColor: Theme.of(context).colorScheme.background,
26
+ backgroundColor: Theme.of(context).colorScheme.background,
22
- bottomSheet: !isDesktop && showPlay
27
+ bottomSheet: const ActionsBar(),
23
- ? BottomSheet(
28
+ body: SafeArea(
29
+ child: SwipeDetector(
24
- enableDrag: false,
30
+ onSwipeLeft: (offset) {
31
+ onNext(context, book, chapter);
32
+ },
25
- onClosing: () {},
33
+ onSwipeRight: (offset) {
26
- builder: (BuildContext ctx) => Container(
34
+ onPrevious(context, book, chapter);
27
- // TODO: check if this is needed
28
- // padding: const EdgeInsets.only(bottom: 0),
35
+ },
29
- child: const Row(
36
+ child: Row(
30
- mainAxisAlignment: MainAxisAlignment.center,
31
- children: [
37
+ children: [
38
+ if (isWide(context)) const Sidebar(),
39
+ const Flexible(
40
+ child: Column(
41
+ children: [
32
- PlayButton(),
42
+ Header(),
43
+ Flexible(
44
+ child: VerseList(),
45
+ ),
46
+ // TODO: add padding only if bottom sheet is shown
47
+ // Padding(
48
+ // padding: EdgeInsets.only(bottom: 40),
49
+ // )
33
- ],
50
+ ],
34
- ),
51
+ ),
35
- ),
36
- )
37
- : null,
38
- body: SafeArea(
39
- child: SwipeDetector(
40
- onSwipeLeft: (offset) {
41
- onNext(context);
42
- },
43
- onSwipeRight: (offset) {
44
- onPrevious(context);
45
- },
46
- child: Row(
47
- children: [
48
- if (isWide(context)) const Sidebar(),
49
- const Flexible(
50
- child: Column(
51
- children: [
52
- Header(),
53
- Flexible(
54
- child: VerseList(),
55
- ),
56
- ],
57
52
  ),
53
+ ],
58
- ),
54
+ ),
59
- ],
60
55
  ),
61
56
  ),
62
57
  ),
lib/state.dart CHANGED
@@ -3,14 +3,91 @@ import "package:flutter/foundation.dart" show defaultTargetPlatform, TargetPlatf
3
3
  import "package:flutter/services.dart";
4
4
  import "package:flutter/material.dart";
5
5
  import "package:flutter_persistent_value_notifier/flutter_persistent_value_notifier.dart";
6
- import "package:flutter_reactive_value/flutter_reactive_value.dart";
7
6
  import "package:just_audio/just_audio.dart";
8
7
  import "package:only_bible_app/screens/chapter_view_screen.dart";
9
8
  import "package:only_bible_app/utils/dialog.dart";
10
9
  import "package:only_bible_app/models.dart";
10
+ import "package:provider/provider.dart";
11
+ import "package:shared_preferences/shared_preferences.dart";
11
12
 
13
+ class ChapterViewModel extends ChangeNotifier {
14
+ final int book;
15
+ final int chapter;
16
+ final List<int> selectedVerses;
17
+ final player = AudioPlayer();
18
+
19
+ static ChapterViewModel of(BuildContext context) {
20
+ return Provider.of<ChapterViewModel>(context, listen: true);
21
+ }
22
+
23
+ static ChapterViewModel ofEvent(BuildContext context) {
24
+ return Provider.of<ChapterViewModel>(context, listen: false);
25
+ }
26
+
27
+ ChapterViewModel({required this.book, required this.chapter, required this.selectedVerses}) {
28
+ savePrefs(book, chapter);
29
+ }
30
+
31
+ bool hasSelectedVerses() {
32
+ return selectedVerses.isNotEmpty;
33
+ }
34
+
35
+ bool isVerseSelected(int i) {
36
+ return selectedVerses.contains(i);
37
+ }
38
+
39
+ void onVerseSelected(int i) {
40
+ if (selectedVerses.contains(i)) {
41
+ selectedVerses.remove(i);
42
+ } else {
43
+ selectedVerses.add(i);
44
+ }
45
+ notifyListeners();
46
+ }
47
+
48
+ onPlay(BuildContext context) async {
12
- final shellNavigatorKey = GlobalKey<NavigatorState>();
49
+ final model = ChapterViewModel.ofEvent(context);
50
+ if (isPlaying.value) {
51
+ await player.pause();
52
+ isPlaying.value = false;
53
+ } else {
54
+ try {
55
+ isPlaying.value = true;
56
+ for (final v in selectedVerses) {
57
+ final bibleName = selectedBible.value!.name;
58
+ final book = (model.book + 1).toString().padLeft(2, "0");
59
+ final chapter = (model.chapter + 1).toString().padLeft(3, "0");
60
+ final verse = (v + 1).toString().padLeft(3, "0");
61
+ await player.setUrl(
62
+ "http://localhost:3000/$bibleName/$book-$chapter-$verse.mp3",
63
+ );
64
+ await player.play();
65
+ await player.stop();
66
+ }
67
+ } catch (err) {
68
+ // TODO: log this error
69
+ print(err.toString());
70
+ showError(context, "Could not play audio");
71
+ } finally {
72
+ await player.pause();
73
+ isPlaying.value = false;
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ Future<(int, int)> loadPrefs() async {
13
- final routeNavigatorKey = GlobalKey<NavigatorState>();
80
+ final prefs = await SharedPreferences.getInstance();
81
+ // await Future.delayed(Duration(seconds: 3));
82
+ return (prefs.getInt("book") ?? 0, prefs.getInt("chapter") ?? 0);
83
+ }
84
+
85
+ savePrefs(int book, int chapter) async {
86
+ final prefs = await SharedPreferences.getInstance();
87
+ // prefs.setInt("bibleId", selectedBibleId.value);
88
+ prefs.setInt("book", book);
89
+ prefs.setInt("chapter", chapter);
90
+ }
14
91
 
15
92
  final darkMode = PersistentValueNotifier<bool>(
16
93
  sharedPreferencesKey: "darkMode",
@@ -27,19 +104,7 @@ final selectedBibleId = PersistentValueNotifier(
27
104
  initialValue: 1,
28
105
  );
29
106
 
30
- final bookIndex = PersistentValueNotifier<int>(
31
- sharedPreferencesKey: "bookIndex",
32
- initialValue: 0,
33
- );
34
-
35
- final chapterIndex = PersistentValueNotifier<int>(
36
- sharedPreferencesKey: "chapterIndex",
37
- initialValue: 0,
38
- );
39
-
40
- final slideTextDir = ValueNotifier<TextDirection?>(null);
41
107
  final selectedBible = ValueNotifier<Bible?>(null);
42
- final selectedVerses = ValueNotifier([]);
43
108
  final isPlaying = ValueNotifier(false);
44
109
  final fontSizeDelta = ValueNotifier(0);
45
110
 
@@ -88,11 +153,19 @@ updateStatusBar() {
88
153
 
89
154
  changeBible(BuildContext context, int i) {
90
155
  selectedBibleId.value = i;
156
+ // TODO: maybe use a future as the bible needs to load
91
157
  loadBible();
92
- // This page is invoked with the other navigator so can't use context.pop()
93
158
  Navigator.of(context).pop();
94
159
  }
95
160
 
161
+ createNoTransitionPageRoute(Widget page) {
162
+ return PageRouteBuilder(
163
+ pageBuilder: (context, _, __) {
164
+ return page;
165
+ },
166
+ );
167
+ }
168
+
96
169
  createSlideRoute({required BuildContext context, TextDirection? slideDir, required Widget page}) {
97
170
  if (isWide(context) || slideDir == null) {
98
171
  return PageRouteBuilder(
@@ -117,53 +190,38 @@ createSlideRoute({required BuildContext context, TextDirection? slideDir, requir
117
190
  );
118
191
  }
119
192
 
120
- navigateBookChapter(BuildContext context, int book, int chapter, bool noAnim) {
193
+ navigateBookChapter(BuildContext context, int book, int chapter, TextDirection? dir) {
121
- print("${bookIndex.value} ${book} ${chapterIndex.value} ${chapter}");
122
- var slideDir = TextDirection.ltr;
123
- if (book > bookIndex.value) {
124
- slideDir = TextDirection.ltr;
125
- } else if (bookIndex.value > book) {
126
- slideDir = TextDirection.rtl;
127
- } else if (chapterIndex.value > chapter) {
128
- slideDir = TextDirection.rtl;
129
- }
130
- // final slideDir = book > bookIndex.value || ? TextDirection.rtl : TextDirection.ltr;
131
194
  // TODO: add bible param here maybe
132
195
  // route: /bible/book/chapter
133
- bookIndex.value = book;
134
- chapterIndex.value = chapter;
135
- selectedVerses.value.clear();
136
196
  Navigator.of(context).push(
137
197
  createSlideRoute(
138
198
  context: context,
139
- slideDir: noAnim ? null : slideDir,
199
+ slideDir: dir,
140
200
  page: ChapterViewScreen(book: book, chapter: chapter),
141
201
  ),
142
202
  );
143
203
  }
144
204
 
145
- onNext(BuildContext context) {
205
+ onNext(BuildContext context, int book, int chapter) {
146
- final selectedBook = selectedBible.value!.books[bookIndex.value];
206
+ final selectedBook = selectedBible.value!.books[book];
147
- final chapter = chapterIndex.value;
148
207
  if (selectedBook.chapters.length > chapter + 1) {
149
- navigateBookChapter(context, selectedBook.index, chapter + 1, false);
208
+ navigateBookChapter(context, selectedBook.index, chapter + 1, TextDirection.ltr);
150
209
  } else {
151
210
  if (selectedBook.index + 1 < selectedBible.value!.books.length) {
152
211
  final nextBook = selectedBible.value!.books[selectedBook.index + 1];
153
- navigateBookChapter(context, nextBook.index, 0, false);
212
+ navigateBookChapter(context, nextBook.index, 0, TextDirection.ltr);
154
213
  }
155
214
  }
156
215
  }
157
216
 
158
- onPrevious(BuildContext context) {
217
+ onPrevious(BuildContext context, int book, int chapter) {
159
- final selectedBook = selectedBible.value!.books[bookIndex.value];
218
+ final selectedBook = selectedBible.value!.books[book];
160
- final chapter = chapterIndex.value;
161
219
  if (chapter - 1 >= 0) {
162
- navigateBookChapter(context, selectedBook.index, chapter - 1, false);
220
+ navigateBookChapter(context, selectedBook.index, chapter - 1, TextDirection.rtl);
163
221
  } else {
164
222
  if (selectedBook.index - 1 >= 0) {
165
223
  final prevBook = selectedBible.value!.books[selectedBook.index - 1];
166
- navigateBookChapter(context, prevBook.index, prevBook.chapters.length - 1, false);
224
+ navigateBookChapter(context, prevBook.index, prevBook.chapters.length - 1, TextDirection.rtl);
167
225
  }
168
226
  }
169
227
  }
@@ -182,46 +240,3 @@ getBibleFromAsset(String file) async {
182
240
  final bytes = await rootBundle.load("assets/bibles/$file.txt");
183
241
  return getBibleFromText(utf8.decode(bytes.buffer.asUint8List(), allowMalformed: false));
184
242
  }
185
-
186
- final player = AudioPlayer();
187
-
188
- onPlay(BuildContext context) async {
189
- if (isPlaying.value) {
190
- await player.pause();
191
- isPlaying.value = false;
192
- } else {
193
- try {
194
- isPlaying.value = true;
195
- for (final v in selectedVerses.value) {
196
- final bibleName = selectedBible.value!.name;
197
- final book = (bookIndex.value + 1).toString().padLeft(2, "0");
198
- final chapter = (chapterIndex.value + 1).toString().padLeft(3, "0");
199
- final verse = (v + 1).toString().padLeft(3, "0");
200
- await player.setUrl(
201
- "http://localhost:3000/$bibleName/$book-$chapter-$verse.mp3",
202
- );
203
- await player.play();
204
- await player.stop();
205
- }
206
- } catch (err) {
207
- // TODO: log this error
208
- print(err.toString());
209
- showError(context, "Could not play audio");
210
- } finally {
211
- await player.pause();
212
- isPlaying.value = false;
213
- }
214
- }
215
- }
216
-
217
- isVerseSelected(BuildContext context, int i) {
218
- return selectedVerses.reactiveValue(context).contains(i);
219
- }
220
-
221
- onVerseSelected(int i) {
222
- if (selectedVerses.value.contains(i)) {
223
- selectedVerses.value = [...selectedVerses.value.where((it) => it != i)];
224
- } else {
225
- selectedVerses.value = [...selectedVerses.value, i];
226
- }
227
- }
lib/widgets/header.dart CHANGED
@@ -11,86 +11,87 @@ class Header extends StatelessWidget {
11
11
 
12
12
  @override
13
13
  Widget build(BuildContext context) {
14
- final book = bookIndex.reactiveValue(context);
14
+ final book = ChapterViewModel.of(context).book;
15
- final chapter = chapterIndex.reactiveValue(context);
15
+ final chapter = ChapterViewModel.of(context).chapter;
16
16
  final selectedBook = selectedBible.value!.books[book];
17
17
  final isDesktop = isWide(context);
18
18
  return Container(
19
- padding: EdgeInsets.only(
19
+ padding: EdgeInsets.only(
20
- left: 20,
20
+ left: 20,
21
- right: 20,
21
+ right: 20,
22
- top: isWide(context) ? 10 : 0,
22
+ top: isWide(context) ? 10 : 0,
23
- bottom: 0,
23
+ bottom: 0,
24
- ),
24
+ ),
25
- child: Column(
25
+ child: Column(
26
- children: [
26
+ children: [
27
- Row(
27
+ Row(
28
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
28
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
29
- crossAxisAlignment: CrossAxisAlignment.center,
29
+ crossAxisAlignment: CrossAxisAlignment.center,
30
- children: [
30
+ children: [
31
- TextButton.icon(
31
+ TextButton.icon(
32
- style: TextButton.styleFrom(
32
+ style: TextButton.styleFrom(
33
- padding: EdgeInsets.zero,
33
+ padding: EdgeInsets.zero,
34
- backgroundColor: Theme.of(context).colorScheme.background,
34
+ backgroundColor: Theme.of(context).colorScheme.background,
35
- elevation: 0,
35
+ elevation: 0,
36
- ),
36
+ ),
37
- label: Icon(
37
+ label: Icon(
38
- Icons.expand_more,
38
+ Icons.expand_more,
39
- size: 28,
39
+ size: 28,
40
- color: Theme.of(context).textTheme.headlineMedium!.color,
40
+ color: Theme.of(context).textTheme.headlineMedium!.color,
41
- ),
41
+ ),
42
- icon: Text(
42
+ icon: Text(
43
- "${selectedBook.name} ${chapter + 1}",
43
+ "${selectedBook.name} ${chapter + 1}",
44
- style: Theme.of(context).textTheme.headlineMedium,
44
+ style: Theme.of(context).textTheme.headlineMedium,
45
- ),
46
- onPressed: () {
47
- // TODO: move this to state
48
- Navigator.of(context).push(
49
- PageRouteBuilder(
50
- opaque: false,
51
- transitionDuration: Duration.zero,
52
- reverseTransitionDuration: Duration.zero,
53
- pageBuilder: (context, _, __) => const BookSelectScreen(),
54
- ),
55
- );
56
- },
57
45
  ),
46
+ onPressed: () {
47
+ // TODO: move this to state
48
+ Navigator.of(context).push(
49
+ PageRouteBuilder(
50
+ opaque: false,
51
+ transitionDuration: Duration.zero,
52
+ reverseTransitionDuration: Duration.zero,
53
+ pageBuilder: (context, _, __) => const BookSelectScreen(),
54
+ ),
55
+ );
56
+ },
57
+ ),
58
- // TODO: show this in more menu in mobile
58
+ // TODO: show this in more menu in mobile
59
- Row(
59
+ Row(
60
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
60
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
61
- children: [
61
+ children: [
62
- if (isDesktop)
62
+ if (isDesktop)
63
- Container(
63
+ Container(
64
- margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
64
+ margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
65
- child: TextButton(
65
+ child: TextButton(
66
- style: TextButton.styleFrom(
66
+ style: TextButton.styleFrom(
67
- padding: const EdgeInsets.symmetric(horizontal: 20),
67
+ padding: const EdgeInsets.symmetric(horizontal: 20),
68
- ),
69
- child: Text(selectedBible.reactiveValue(context)!.name),
70
- onPressed: () {
71
- Navigator.of(context).push(
72
- PageRouteBuilder(
73
- opaque: false,
74
- transitionDuration: Duration.zero,
75
- reverseTransitionDuration: Duration.zero,
76
- pageBuilder: (context, _, __) => const BibleSelectScreen(),
77
- ),
78
- );
79
- },
80
68
  ),
69
+ child: Text(selectedBible.reactiveValue(context)!.name),
70
+ onPressed: () {
71
+ Navigator.of(context).push(
72
+ PageRouteBuilder(
73
+ opaque: false,
74
+ transitionDuration: Duration.zero,
75
+ reverseTransitionDuration: Duration.zero,
76
+ pageBuilder: (context, _, __) => const BibleSelectScreen(),
77
+ ),
78
+ );
79
+ },
81
80
  ),
81
+ ),
82
- if (isDesktop)
82
+ if (isDesktop)
83
- Container(
83
+ Container(
84
- margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
84
+ margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
85
- child: const PlayButton(),
85
+ child: const PlayButton(),
86
- ),
86
+ ),
87
- const Menu(),
87
+ const Menu(),
88
- ],
88
+ ],
89
- ),
89
+ ),
90
- ],
90
+ ],
91
- ),
91
+ ),
92
- const Divider(height: 0, endIndent: 5),
92
+ Divider(height: isDesktop ? 10 : 0, endIndent: 5, thickness: 1.5),
93
- ],
93
+ ],
94
+ ),
94
- ));
95
+ );
95
96
  }
96
97
  }
lib/widgets/play_button.dart CHANGED
@@ -1,6 +1,6 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:flutter_reactive_value/flutter_reactive_value.dart";
3
- import 'package:only_bible_app/state.dart';
3
+ import "package:only_bible_app/state.dart";
4
4
 
5
5
  class PlayButton extends StatelessWidget {
6
6
  const PlayButton({super.key});
@@ -9,10 +9,11 @@ class PlayButton extends StatelessWidget {
9
9
  Widget build(BuildContext context) {
10
10
  final icon = isPlaying.reactiveValue(context) ? Icons.pause_circle_filled : Icons.play_circle_fill;
11
11
  final size = isWide(context) ? 28.0 : 42.0;
12
+
12
13
  return IconButton(
13
14
  icon: Icon(icon, size: size),
14
15
  onPressed: () {
15
- onPlay(context);
16
+ ChapterViewModel.ofEvent(context).onPlay(context);
16
17
  },
17
18
  );
18
19
  }
lib/widgets/sliver_tile_grid.dart CHANGED
@@ -11,19 +11,21 @@ class SliverTileGrid extends StatelessWidget {
11
11
 
12
12
  @override
13
13
  Widget build(BuildContext context) {
14
+ final isDesktop = isWide(context);
15
+ final spacing = isDesktop ? 16.0 : 12.0;
14
16
  return SliverPadding(
15
17
  padding: const EdgeInsets.symmetric(horizontal: 20),
16
18
  sliver: SliverGrid.count(
17
19
  crossAxisCount: listType == ListType.large
18
20
  ? 2
19
- : isWide(context)
21
+ : isDesktop
20
22
  ? 6
21
23
  : 5,
22
- crossAxisSpacing: 16,
24
+ crossAxisSpacing: spacing,
23
- mainAxisSpacing: 16,
25
+ mainAxisSpacing: spacing,
24
26
  childAspectRatio: listType == ListType.large
25
27
  ? 4
26
- : isWide(context)
28
+ : isDesktop
27
29
  ? 1.6
28
30
  : 1.5,
29
31
  children: children,
lib/widgets/verse_list.dart CHANGED
@@ -8,8 +8,10 @@ class VerseList extends StatelessWidget {
8
8
 
9
9
  @override
10
10
  Widget build(BuildContext context) {
11
+ final book = ChapterViewModel.of(context).book;
12
+ final chapter = ChapterViewModel.of(context).chapter;
11
- final selectedBook = selectedBible.reactiveValue(context)!.books[bookIndex.value];
13
+ final selectedBook = selectedBible.reactiveValue(context)!.books[book];
12
- final verses = selectedBook.chapters[chapterIndex.value].verses;
14
+ final verses = selectedBook.chapters[chapter].verses;
13
15
  return SelectionArea(
14
16
  child: ListView.builder(
15
17
  shrinkWrap: false,
lib/widgets/verse_view.dart CHANGED
@@ -10,14 +10,14 @@ class VerseText extends StatelessWidget {
10
10
 
11
11
  @override
12
12
  Widget build(BuildContext context) {
13
- final selected = isVerseSelected(context, index);
13
+ final selected = ChapterViewModel.of(context).isVerseSelected(index);
14
14
  final delta = fontSizeDelta.reactiveValue(context);
15
15
  final bodySize = Theme.of(context).textTheme.bodyMedium!.fontSize! + delta;
16
16
  final weight =
17
17
  fontBold.reactiveValue(context) ? FontWeight.w600 : Theme.of(context).textTheme.bodyMedium!.fontWeight;
18
18
 
19
19
  onTap() {
20
- onVerseSelected(index);
20
+ ChapterViewModel.ofEvent(context).onVerseSelected(index);
21
21
  }
22
22
 
23
23
  return GestureDetector(
pubspec.lock CHANGED
@@ -532,6 +532,14 @@ packages:
532
532
  url: "https://pub.dev"
533
533
  source: hosted
534
534
  version: "1.0.4"
535
+ nested:
536
+ dependency: transitive
537
+ description:
538
+ name: nested
539
+ sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
540
+ url: "https://pub.dev"
541
+ source: hosted
542
+ version: "1.0.0"
535
543
  package_config:
536
544
  dependency: transitive
537
545
  description:
@@ -644,6 +652,14 @@ packages:
644
652
  url: "https://pub.dev"
645
653
  source: hosted
646
654
  version: "4.2.4"
655
+ provider:
656
+ dependency: "direct main"
657
+ description:
658
+ name: provider
659
+ sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f
660
+ url: "https://pub.dev"
661
+ source: hosted
662
+ version: "6.0.5"
647
663
  pub_semver:
648
664
  dependency: transitive
649
665
  description:
pubspec.yaml CHANGED
@@ -21,6 +21,7 @@ dependencies:
21
21
  flutter_swipe_detector: ^2.0.0
22
22
  cupertino_icons: ^1.0.5
23
23
  firebase_core: ^2.15.0
24
+ provider: ^6.0.5
24
25
  # firebase_crashlytics: ^3.3.4
25
26
  # firebase_performance: ^0.9.2+4
26
27