~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.


1d1e1408 pyrossh

2 years ago
improve providers
lib/app.dart CHANGED
@@ -7,11 +7,10 @@ import "package:only_bible_app/utils.dart";
7
7
  import "package:only_bible_app/widgets/scaffold_markdown.dart";
8
8
 
9
9
  class App extends StatelessWidget {
10
- final bool firstOpen;
11
10
  final int initialBook;
12
11
  final int initialChapter;
13
12
 
14
- const App({super.key, required this.firstOpen, required this.initialBook, required this.initialChapter});
13
+ const App({super.key, required this.initialBook, required this.initialChapter});
15
14
 
16
15
  @override
17
16
  Widget build(BuildContext context) {
@@ -31,7 +30,9 @@ class App extends StatelessWidget {
31
30
  "/privacy-policy": (context) => const ScaffoldMarkdown(title: "Privacy Policy", file: "privacy-policy.md"),
32
31
  "/about-us": (context) => const ScaffoldMarkdown(title: "About Us", file: "about-us.md"),
33
32
  },
33
+ home: context.app.firstOpen
34
+ ? const BibleSelectScreen()
34
- home: firstOpen ? const BibleSelectScreen() : ChapterViewScreen(book: initialBook, chapter: initialChapter),
35
+ : ChapterViewScreen(bookIndex: initialBook, chapterIndex: initialChapter),
35
36
  );
36
37
  }
37
38
  }
lib/main.dart CHANGED
@@ -25,11 +25,12 @@ void main() async {
25
25
  FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
26
26
  usePathUrlStrategy();
27
27
  final model = AppProvider();
28
+ await model.loadData();
28
- final (firstOpen, book, chapter) = await model.loadData();
29
+ final (book, chapter) = model.loadBookChapter();
29
30
  runApp(
30
31
  ChangeNotifierProvider.value(
31
32
  value: model,
32
- child: App(firstOpen: firstOpen, initialBook: book, initialChapter: chapter),
33
+ child: App(initialBook: book, initialChapter: chapter),
33
34
  ),
34
35
  );
35
36
  FlutterNativeSplash.remove();
lib/models.dart CHANGED
@@ -54,9 +54,10 @@ class Book {
54
54
  }
55
55
 
56
56
  class Chapter {
57
+ final int index;
57
58
  final List<Verse> verses;
58
59
 
59
- const Chapter({required this.verses});
60
+ const Chapter({required this.index, required this.verses});
60
61
  }
61
62
 
62
63
  class Verse {
@@ -90,7 +91,7 @@ List<Book> getBibleFromText(String text) {
90
91
  }
91
92
  if (books[book - 1].chapters.length < chapter) {
92
93
  // ignore: prefer_const_constructors
93
- books[book - 1].chapters.add(Chapter(verses: []));
94
+ books[book - 1].chapters.add(Chapter(index: chapter - 1, verses: []));
94
95
  }
95
96
  books[book - 1].chapters[chapter - 1].verses.add(
96
97
  Verse(
lib/providers/app_provider.dart CHANGED
@@ -35,7 +35,6 @@ class AppProvider extends ChangeNotifier {
35
35
  late PackageInfo packageInfo;
36
36
  late Bible bible;
37
37
  late Locale locale;
38
- bool firstOpen = true;
39
38
  bool engTitles = false;
40
39
  bool darkMode = false;
41
40
  bool fontBold = false;
@@ -49,6 +48,13 @@ class AppProvider extends ChangeNotifier {
49
48
  List<HistoryFrame> history = [];
50
49
  final box = GetStorage("only-bible-app-backup");
51
50
 
51
+ get firstOpen => box.read("firstOpen") ?? true;
52
+
53
+ set firstOpen(v) {
54
+ box.write("firstOpen", v);
55
+ box.save();
56
+ }
57
+
52
58
  static AppProvider of(BuildContext context) {
53
59
  return Provider.of(context, listen: true);
54
60
  }
@@ -59,7 +65,6 @@ class AppProvider extends ChangeNotifier {
59
65
 
60
66
  save() async {
61
67
  final prefs = await SharedPreferences.getInstance();
62
- await prefs.setBool("firstOpen", firstOpen);
63
68
  await prefs.setString("bibleName", bible.name);
64
69
  await prefs.setBool("engTitles", engTitles);
65
70
  await prefs.setBool("darkMode", darkMode);
@@ -68,21 +73,17 @@ class AppProvider extends ChangeNotifier {
68
73
  await prefs.setString("languageCode", locale.languageCode);
69
74
  }
70
75
 
71
- Future<(bool, int, int)> loadData() async {
76
+ loadData() async {
72
77
  packageInfo = await PackageInfo.fromPlatform();
73
78
  final prefs = await SharedPreferences.getInstance();
74
79
  engTitles = prefs.getBool("engTitles") ?? false;
75
- firstOpen = prefs.getBool("firstOpen") ?? true;
76
80
  darkMode = prefs.getBool("darkMode") ?? false;
77
81
  fontBold = prefs.getBool("fontBold") ?? false;
78
82
  textScaleFactor = prefs.getDouble("textScaleFactor") ?? 1;
79
83
  locale = Locale(prefs.getString("languageCode") ?? "en");
80
84
  bible = await loadBible(prefs.getString("bibleName") ?? "English");
81
85
  // await Future.delayed(Duration(seconds: 3));
82
- final book = prefs.getInt("book") ?? 0;
83
- final chapter = prefs.getInt("chapter") ?? 0;
84
86
  updateStatusBar();
85
- return (firstOpen, book, chapter);
86
87
  }
87
88
 
88
89
  Future<Bible> loadBible(String name) async {
@@ -146,29 +147,69 @@ class AppProvider extends ChangeNotifier {
146
147
  hideActions(context);
147
148
  }
148
149
 
150
+ onNext(BuildContext context, int book, int chapter) {
151
+ final selectedBook = bible.books[book];
152
+ if (selectedBook.chapters.length > chapter + 1) {
153
+ pushBookChapter(context, selectedBook.index, chapter + 1, TextDirection.ltr);
154
+ } else {
155
+ if (selectedBook.index + 1 < bible.books.length) {
156
+ final nextBook = bible.books[selectedBook.index + 1];
157
+ pushBookChapter(context, nextBook.index, 0, TextDirection.ltr);
158
+ }
159
+ }
160
+ }
161
+
162
+ onPrevious(BuildContext context, int book, int chapter) {
163
+ final selectedBook = bible.books[book];
164
+ if (chapter - 1 >= 0) {
165
+ // if (Navigator.of(context).canPop()) {
166
+ // Navigator.of(context).pop();
167
+ // } else {
168
+ pushBookChapter(context, selectedBook.index, chapter - 1, TextDirection.rtl);
169
+ // }
170
+ } else {
171
+ if (selectedBook.index - 1 >= 0) {
172
+ final prevBook = bible.books[selectedBook.index - 1];
173
+ pushBookChapter(context, prevBook.index, prevBook.chapters.length - 1, TextDirection.rtl);
174
+ }
175
+ }
176
+ }
177
+
178
+ (int, int) loadBookChapter() {
179
+ return (box.read("book") ?? 0, box.read("chapter") ?? 0);
180
+ }
181
+
182
+ saveBookChapter(int book, int chapter) {
183
+ box.write("book", book);
184
+ box.write("chapter", chapter);
185
+ box.save();
186
+ }
187
+
149
188
  pushBookChapter(BuildContext context, int book, int chapter, TextDirection? dir) {
189
+ saveBookChapter(book, chapter);
150
190
  clearEvents(context);
151
191
  Navigator.of(context).push(
152
192
  createSlideRoute(
153
193
  context: context,
154
194
  slideDir: dir,
155
- page: ChapterViewScreen(book: book, chapter: chapter),
195
+ page: ChapterViewScreen(bookIndex: book, chapterIndex: chapter),
156
196
  ),
157
197
  );
158
198
  }
159
199
 
160
200
  replaceBookChapter(BuildContext context, int book, int chapter) {
201
+ saveBookChapter(book, chapter);
161
202
  clearEvents(context);
162
203
  Navigator.of(context).pushReplacement(
163
204
  createNoTransitionPageRoute(
164
- ChapterViewScreen(book: book, chapter: chapter),
205
+ ChapterViewScreen(bookIndex: book, chapterIndex: chapter),
165
206
  ),
166
207
  );
167
208
  }
168
209
 
169
210
  updateFirstOpen() {
170
- firstOpen = false;
211
+ box.write("firstOpen", true);
171
- save();
212
+ box.save();
172
213
  }
173
214
 
174
215
  toggleDarkMode() {
@@ -450,7 +491,7 @@ class AppProvider extends ChangeNotifier {
450
491
  }
451
492
 
452
493
  void shareVerses(BuildContext context) {
453
- final name = bible.books[selectedVerses.first.book].name;
494
+ final name = bible.books[selectedVerses.first.book].name(context);
454
495
  final chapter = selectedVerses.first.chapter + 1;
455
496
  final title = "$name $chapter: ${selectedVerses.map((e) => e.index + 1).join(", ")}";
456
497
  final text = selectedVerses.map((e) => e.text).join("\n");
lib/providers/chapter_provider.dart DELETED
@@ -1,73 +0,0 @@
1
- import "package:flutter/material.dart";
2
- import "package:only_bible_app/models.dart";
3
- import "package:provider/provider.dart";
4
- import "package:shared_preferences/shared_preferences.dart";
5
- import "package:only_bible_app/utils.dart";
6
- import "package:only_bible_app/providers/app_provider.dart";
7
-
8
- class ChapterProvider extends ChangeNotifier {
9
- final int book;
10
- final int chapter;
11
-
12
- static ChapterProvider of(BuildContext context) {
13
- return Provider.of(context, listen: true);
14
- }
15
-
16
- static ChapterProvider ofEvent(BuildContext context) {
17
- return Provider.of(context, listen: false);
18
- }
19
-
20
- static Book selectedBook(BuildContext context) {
21
- final model = of(context);
22
- return AppProvider.of(context).bible.books[model.book];
23
- }
24
-
25
- static Chapter selectedChapter(BuildContext context) {
26
- final model = of(context);
27
- return AppProvider.of(context).bible.books[model.book].chapters[model.chapter];
28
- }
29
-
30
- ChapterProvider({required this.book, required this.chapter}) {
31
- save(book, chapter);
32
- }
33
-
34
- save(int book, int chapter) async {
35
- final prefs = await SharedPreferences.getInstance();
36
- prefs.setInt("book", book);
37
- prefs.setInt("chapter", chapter);
38
- }
39
-
40
- navigateBookChapter(BuildContext context, int book, int chapter, TextDirection? dir) {
41
- context.appEvent.pushBookChapter(context, book, chapter, dir);
42
- }
43
-
44
- onNext(BuildContext context, int book, int chapter) {
45
- final selectedBible = AppProvider.ofEvent(context).bible;
46
- final selectedBook = selectedBible.books[book];
47
- if (selectedBook.chapters.length > chapter + 1) {
48
- navigateBookChapter(context, selectedBook.index, chapter + 1, TextDirection.ltr);
49
- } else {
50
- if (selectedBook.index + 1 < selectedBible.books.length) {
51
- final nextBook = selectedBible.books[selectedBook.index + 1];
52
- navigateBookChapter(context, nextBook.index, 0, TextDirection.ltr);
53
- }
54
- }
55
- }
56
-
57
- onPrevious(BuildContext context, int book, int chapter) {
58
- final selectedBible = AppProvider.ofEvent(context).bible;
59
- final selectedBook = selectedBible.books[book];
60
- if (chapter - 1 >= 0) {
61
- // if (Navigator.of(context).canPop()) {
62
- // Navigator.of(context).pop();
63
- // } else {
64
- navigateBookChapter(context, selectedBook.index, chapter - 1, TextDirection.rtl);
65
- // }
66
- } else {
67
- if (selectedBook.index - 1 >= 0) {
68
- final prevBook = selectedBible.books[selectedBook.index - 1];
69
- navigateBookChapter(context, prevBook.index, prevBook.chapters.length - 1, TextDirection.rtl);
70
- }
71
- }
72
- }
73
- }
lib/screens/bible_select_screen.dart CHANGED
@@ -30,7 +30,7 @@ class BibleSelectScreen extends StatelessWidget {
30
30
  onPressed: () {
31
31
  AppProvider.ofEvent(context).updateCurrentBible(context, Locale(l.localeName), l.languageTitle);
32
32
  if (context.appEvent.firstOpen) {
33
- context.appEvent.updateFirstOpen();
33
+ context.appEvent.firstOpen = false;
34
34
  context.appEvent.pushBookChapter(context, 0, 0, null);
35
35
  } else {
36
36
  Navigator.of(context).pop();
lib/screens/chapter_view_screen.dart CHANGED
@@ -1,16 +1,14 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/providers/chapter_provider.dart";
3
2
  import "package:only_bible_app/utils.dart";
4
3
  import "package:only_bible_app/widgets/chapter_app_bar.dart";
5
4
  import "package:only_bible_app/widgets/sidebar.dart";
6
5
  import "package:only_bible_app/widgets/verses_view.dart";
7
- import "package:provider/provider.dart";
8
6
 
9
7
  class ChapterViewScreen extends StatelessWidget {
10
- final int book;
8
+ final int bookIndex;
11
- final int chapter;
9
+ final int chapterIndex;
12
10
 
13
- const ChapterViewScreen({super.key, required this.book, required this.chapter});
11
+ const ChapterViewScreen({super.key, required this.bookIndex, required this.chapterIndex});
14
12
 
15
13
  @override
16
14
  Widget build(BuildContext context) {
@@ -31,37 +29,33 @@ class ChapterViewScreen extends StatelessWidget {
31
29
  // );
32
30
  // },
33
31
  // ),
32
+ final book = context.app.bible.books[bookIndex];
33
+ final chapter = book.chapters[chapterIndex];
34
- return ChangeNotifierProvider(
34
+ return Scaffold(
35
- create: (_) => ChapterProvider(
35
+ appBar: context.isWide ? null : ChapterAppBar(book: book, chapter: chapter),
36
+ backgroundColor: Theme.of(context).colorScheme.background,
36
- book: book,
37
+ body: SafeArea(
38
+ child: context.isWide
39
+ ? Row(
40
+ children: [
37
- chapter: chapter,
41
+ const Sidebar(),
42
+ Flexible(
43
+ child: Column(
44
+ children: [
45
+ ChapterAppBar(book: book, chapter: chapter),
46
+ const Padding(
47
+ padding: EdgeInsets.only(bottom: 10),
48
+ child: Divider(height: 5, indent: 20, endIndent: 20, thickness: 1.5),
38
- ),
49
+ ),
39
- child: Scaffold(
40
- appBar: context.isWide ? null : const ChapterAppBar(),
41
- backgroundColor: Theme.of(context).colorScheme.background,
42
- body: SafeArea(
43
- child: context.isWide
44
- ? const Row(
45
- children: [
46
- Sidebar(),
47
- Flexible(
50
+ Flexible(
48
- child: Column(
49
- children: [
50
- ChapterAppBar(),
51
- Padding(
52
- padding: EdgeInsets.only(bottom: 10),
53
- child: Divider(height: 5, indent: 20, endIndent: 20, thickness: 1.5),
51
+ child: VersesView(book: book, chapter: chapter),
54
- ),
52
+ ),
55
- Flexible(
56
- child: VersesView(),
57
- ),
58
- ],
53
+ ],
59
- ),
60
54
  ),
55
+ ),
61
- ],
56
+ ],
62
- )
57
+ )
63
- : const VersesView(),
58
+ : VersesView(book: book, chapter: chapter),
64
- ),
65
59
  ),
66
60
  );
67
61
  }
lib/utils.dart CHANGED
@@ -1,7 +1,6 @@
1
1
  import "dart:convert";
2
2
  import "package:only_bible_app/dialog.dart";
3
3
  import "package:only_bible_app/providers/app_provider.dart";
4
- import "package:only_bible_app/providers/chapter_provider.dart";
5
4
  import "package:url_launcher/url_launcher.dart";
6
5
  import "package:flutter/foundation.dart" show defaultTargetPlatform, TargetPlatform;
7
6
  import "package:flutter/material.dart";
@@ -29,10 +28,6 @@ extension AppContext on BuildContext {
29
28
 
30
29
  AppProvider get appEvent => Provider.of(this, listen: false);
31
30
 
32
- ChapterProvider get chapter => Provider.of(this, listen: true);
33
-
34
- ChapterProvider get chapterEvent => Provider.of(this, listen: false);
35
-
36
31
  double get actionsHeight {
37
32
  if (isIOS()) {
38
33
  return 80.0;
lib/widgets/chapter_app_bar.dart CHANGED
@@ -1,22 +1,19 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:only_bible_app/providers/app_provider.dart";
2
+ import "package:only_bible_app/models.dart";
3
- import "package:only_bible_app/providers/chapter_provider.dart";
4
3
  import "package:only_bible_app/utils.dart";
5
4
 
6
5
  class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
6
+ final Book book;
7
- const ChapterAppBar({super.key});
7
+ final Chapter chapter;
8
+
9
+ const ChapterAppBar({super.key, required this.book, required this.chapter});
8
10
 
9
11
  @override
10
12
  Size get preferredSize => const Size.fromHeight(40);
11
13
 
12
- // TODO: add next/prev buttons for desktop mode
13
-
14
14
  @override
15
15
  Widget build(BuildContext context) {
16
- final app = AppProvider.of(context);
17
- final model = ChapterProvider.of(context);
18
- final selectedBook = app.bible.books[model.book];
19
- final bookName = selectedBook.name(context);
16
+ final bookName = book.name(context);
20
17
  final isDesktop = context.isWide;
21
18
  return SafeArea(
22
19
  child: Container(
@@ -27,11 +24,11 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
27
24
  children: [
28
25
  InkWell(
29
26
  enableFeedback: true,
30
- onTap: () => app.changeBook(context),
27
+ onTap: () => context.appEvent.changeBook(context),
31
28
  child: Row(
32
29
  children: [
33
30
  Text(
34
- "$bookName ${model.chapter + 1}",
31
+ "$bookName ${chapter.index + 1}",
35
32
  style: Theme.of(context).textTheme.headlineMedium,
36
33
  ),
37
34
  Icon(
@@ -48,7 +45,6 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
48
45
  children: [
49
46
  if (isDesktop)
50
47
  TextButton.icon(
51
- onPressed: () => model.onPrevious(context, model.book, model.chapter),
52
48
  style: TextButton.styleFrom(
53
49
  elevation: 0,
54
50
  padding: const EdgeInsets.symmetric(horizontal: 10),
@@ -58,11 +54,11 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
58
54
  ),
59
55
  icon: const Icon(Icons.chevron_left),
60
56
  label: const Text("Prev"),
57
+ onPressed: () => context.appEvent.onPrevious(context, book.index, chapter.index),
61
58
  ),
62
59
  if (isDesktop) const SizedBox(width: 10),
63
60
  if (isDesktop)
64
61
  TextButton.icon(
65
- onPressed: () => model.onNext(context, model.book, model.chapter),
66
62
  style: TextButton.styleFrom(
67
63
  elevation: 0,
68
64
  padding: const EdgeInsets.symmetric(horizontal: 10),
@@ -72,11 +68,11 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
72
68
  ),
73
69
  icon: const Icon(Icons.chevron_right),
74
70
  label: const Text("Next"),
71
+ onPressed: () => context.appEvent.onNext(context, book.index, chapter.index),
75
72
  ),
76
73
  if (isDesktop) const SizedBox(width: 20),
77
74
  if (isDesktop)
78
75
  TextButton.icon(
79
- onPressed: () => app.changeBibleFromHeader(context),
80
76
  style: TextButton.styleFrom(
81
77
  padding: const EdgeInsets.symmetric(horizontal: 10),
82
78
  shadowColor: Theme.of(context).shadowColor,
@@ -90,14 +86,15 @@ class ChapterAppBar extends StatelessWidget implements PreferredSizeWidget {
90
86
  ),
91
87
  ),
92
88
  icon: const Icon(Icons.book_outlined),
93
- label: Text(app.bible.name),
89
+ label: Text(context.app.bible.name),
90
+ onPressed: () => context.appEvent.changeBibleFromHeader(context),
94
91
  ),
95
92
  Padding(
96
93
  padding: const EdgeInsets.only(left: 10),
97
94
  child: IconButton(
98
95
  padding: EdgeInsets.zero,
99
- onPressed: () => AppProvider.ofEvent(context).showSettings(context),
100
96
  icon: Icon(Icons.more_vert, size: isDesktop ? 28 : 24),
97
+ onPressed: () => context.appEvent.showSettings(context),
101
98
  ),
102
99
  ),
103
100
  ],
lib/widgets/verses_view.dart CHANGED
@@ -1,25 +1,25 @@
1
1
  import "package:flutter/gestures.dart";
2
2
  import "package:flutter/material.dart";
3
3
  import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
4
+ import "package:only_bible_app/models.dart";
4
5
  import "package:only_bible_app/providers/app_provider.dart";
5
- import "package:only_bible_app/providers/chapter_provider.dart";
6
6
  import "package:only_bible_app/utils.dart";
7
7
 
8
8
  class VersesView extends StatelessWidget {
9
+ final Book book;
10
+ final Chapter chapter;
9
- const VersesView({super.key});
11
+ const VersesView({super.key, required this.book, required this.chapter});
10
12
 
11
13
  @override
12
14
  Widget build(BuildContext context) {
13
15
  final app = AppProvider.of(context);
14
- final model = ChapterProvider.of(context);
15
- final chapter = ChapterProvider.selectedChapter(context);
16
16
  final textStyle = DefaultTextStyle.of(context).style;
17
17
  return SwipeDetector(
18
18
  onSwipeLeft: (offset) {
19
- model.onNext(context, model.book, model.chapter);
19
+ context.appEvent.onNext(context, book.index, chapter.index);
20
20
  },
21
21
  onSwipeRight: (offset) {
22
- model.onPrevious(context, model.book, model.chapter);
22
+ context.appEvent.onPrevious(context, book.index, chapter.index);
23
23
  },
24
24
  child: Padding(
25
25
  padding: const EdgeInsets.only(left: 20, right: 20),