~repos /only-bible-app

#kotlin#android#ios

git clone https://pyrossh.dev/repos/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.


71bf8a81 pyrossh

2 years ago
add bible selector
lib/app.dart CHANGED
@@ -20,7 +20,7 @@ class App extends StatelessWidget {
20
20
  darkTheme: darkTheme,
21
21
  routerConfig: GoRouter(
22
22
  debugLogDiagnostics: true,
23
- initialLocation: "/${selectedBible.value[bookIndex.value].name}/${chapterIndex.value}",
23
+ initialLocation: "/${selectedBible.value!.books[bookIndex.value].name}/${chapterIndex.value}",
24
24
  routes: [
25
25
  ShellRoute(
26
26
  builder: (context, state, child) {
lib/{models/book.dart → models.dart} RENAMED
@@ -1,3 +1,23 @@
1
+ class Bible {
2
+ final int id;
3
+ final String name;
4
+ final String fileName;
5
+ List<Book> books = [];
6
+
7
+ Bible({
8
+ required this.id,
9
+ required this.name,
10
+ required this.fileName,
11
+ });
12
+
13
+ Bible.withBooks({
14
+ required this.id,
15
+ required this.name,
16
+ required this.fileName,
17
+ required this.books,
18
+ });
19
+ }
20
+
1
21
  class Book {
2
22
  final int index;
3
23
  final String name;
@@ -43,7 +63,7 @@ class TimeRange {
43
63
  const TimeRange({required this.start, required this.end});
44
64
  }
45
65
 
46
- final bookNames = [
66
+ const bookNames = [
47
67
  "Genesis",
48
68
  "Exodus",
49
69
  "Leviticus",
@@ -111,3 +131,8 @@ final bookNames = [
111
131
  "Jude",
112
132
  "Revelation"
113
133
  ];
134
+
135
+ final bibles = [
136
+ Bible(id: 1, name: "KJV", fileName: "kj.csv.gz"),
137
+ Bible(id: 2, name: "Kannada", fileName: "kn.csv.gz"),
138
+ ];
lib/routes/home_screen.dart CHANGED
@@ -1,4 +1,5 @@
1
1
  import "package:flutter/material.dart";
2
+ import 'package:flutter_reactive_value/flutter_reactive_value.dart';
2
3
  import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
3
4
  import 'package:go_router/go_router.dart';
4
5
  import 'package:only_bible_app/widgets/header.dart';
@@ -73,7 +74,7 @@ class VerseList extends StatelessWidget {
73
74
 
74
75
  @override
75
76
  Widget build(BuildContext context) {
76
- final selectedBook = selectedBible.value[bookIndex.value];
77
+ final selectedBook = selectedBible.reactiveValue(context)!.books[bookIndex.value];
77
78
  final verses = selectedBook.chapters[chapterIndex.value].verses;
78
79
  return SelectionArea(
79
80
  child: ListView.builder(
lib/state.dart CHANGED
@@ -7,7 +7,7 @@ import 'package:flutter_reactive_value/flutter_reactive_value.dart';
7
7
  import 'package:go_router/go_router.dart';
8
8
  import 'package:just_audio/just_audio.dart';
9
9
  import 'package:only_bible_app/utils/dialog.dart';
10
- import 'package:only_bible_app/models/book.dart';
10
+ import 'package:only_bible_app/models.dart';
11
11
 
12
12
  final shellNavigatorKey = GlobalKey<NavigatorState>();
13
13
  final routeNavigatorKey = GlobalKey<NavigatorState>();
@@ -22,9 +22,9 @@ final fontBold = PersistentValueNotifier<bool>(
22
22
  initialValue: false,
23
23
  );
24
24
 
25
- final selectedBibleName = PersistentValueNotifier<String>(
25
+ final selectedBibleId = PersistentValueNotifier(
26
- sharedPreferencesKey: 'selectedBibleName',
26
+ sharedPreferencesKey: 'selectedBibleId',
27
- initialValue: "kn.csv.gz",
27
+ initialValue: 1,
28
28
  );
29
29
 
30
30
  final bookIndex = PersistentValueNotifier<int>(
@@ -38,7 +38,7 @@ final chapterIndex = PersistentValueNotifier<int>(
38
38
  );
39
39
 
40
40
  final slideTextDir = ValueNotifier<TextDirection?>(null);
41
- final selectedBible = ValueNotifier<List<Book>>([]);
41
+ final selectedBible = ValueNotifier<Bible?>(null);
42
42
  final selectedVerses = ValueNotifier([]);
43
43
  final isPlaying = ValueNotifier(false);
44
44
  final fontSizeDelta = ValueNotifier(0);
@@ -86,6 +86,13 @@ updateStatusBar() {
86
86
  }
87
87
  }
88
88
 
89
+ changeBible(BuildContext context, int i) {
90
+ selectedBibleId.value = i;
91
+ loadBible();
92
+ // This page is invoked with the other navigator so can't use context.pop()
93
+ Navigator.of(context).pop();
94
+ }
95
+
89
96
  navigateBookChapter(BuildContext context, int book, int chapter, bool noAnim) {
90
97
  if (isWide(context) || noAnim) {
91
98
  slideTextDir.value = null;
@@ -94,43 +101,49 @@ navigateBookChapter(BuildContext context, int book, int chapter, bool noAnim) {
94
101
  }
95
102
  bookIndex.value = book;
96
103
  chapterIndex.value = chapter;
97
- context.push("/${selectedBible.value[book].name}/$chapter");
104
+ context.push("/${selectedBible.value!.books[book].name}/$chapter");
98
105
  // Use this or use navigatorKey once header moves scaffold
99
106
  // if (!isWide(context)) {
100
- // context.pop();
107
+ // Navigator.of(context).pop();
101
108
  // }
102
109
  }
103
110
 
104
111
  onNext(BuildContext context) {
105
- final selectedBook = selectedBible.value[bookIndex.value];
112
+ final selectedBook = selectedBible.value!.books[bookIndex.value];
106
113
  final chapter = chapterIndex.value;
107
114
  if (selectedBook.chapters.length > chapter + 1) {
108
115
  navigateBookChapter(context, selectedBook.index, chapter + 1, false);
109
116
  } else {
110
- if (selectedBook.index + 1 < selectedBible.value.length) {
117
+ if (selectedBook.index + 1 < selectedBible.value!.books.length) {
111
- final nextBook = selectedBible.value[selectedBook.index + 1];
118
+ final nextBook = selectedBible.value!.books[selectedBook.index + 1];
112
119
  navigateBookChapter(context, nextBook.index, 0, false);
113
120
  }
114
121
  }
115
122
  }
116
123
 
117
124
  onPrevious(BuildContext context) {
118
- final selectedBook = selectedBible.value[bookIndex.value];
125
+ final selectedBook = selectedBible.value!.books[bookIndex.value];
119
126
  final chapter = chapterIndex.value;
120
127
  if (chapter - 1 >= 0) {
121
128
  navigateBookChapter(context, selectedBook.index, chapter - 1, false);
122
129
  } else {
123
130
  if (selectedBook.index - 1 >= 0) {
124
- final prevBook = selectedBible.value[selectedBook.index - 1];
131
+ final prevBook = selectedBible.value!.books[selectedBook.index - 1];
125
132
  navigateBookChapter(context, prevBook.index, prevBook.chapters.length - 1, false);
126
133
  }
127
134
  }
128
135
  }
129
136
 
130
137
  loadBible() async {
131
- // selectedBibleName.value
138
+ print(selectedBibleId.value);
139
+ final bible = bibles.firstWhere((it) => it.id == selectedBibleId.value);
132
- final value = await getBibleFromAsset("kn.csv.gz");
140
+ final books = await getBibleFromAsset(bible.fileName);
133
- selectedBible.value = value;
141
+ selectedBible.value = Bible.withBooks(
142
+ id: bible.id,
143
+ name: bible.name,
144
+ fileName: bible.fileName,
145
+ books: books,
146
+ );
134
147
  }
135
148
 
136
149
  getBibleFromAsset(String file) async {
@@ -177,7 +190,7 @@ List<Book> getBibleFromText(String text) {
177
190
  }
178
191
 
179
192
  onPlay(BuildContext context) async {
180
- final verses = selectedBible.value[bookIndex.value].chapters[chapterIndex.value].verses;
193
+ final verses = selectedBible.value!.books[bookIndex.value].chapters[chapterIndex.value].verses;
181
194
  final filteredVerses = verses.asMap().keys.where((it) => selectedVerses.value.contains(it)).map((it) => verses[it]);
182
195
  final player = AudioPlayer();
183
196
  player.setUrl(
lib/theme.dart CHANGED
@@ -54,6 +54,7 @@ final lightTheme = ThemeData(
54
54
  fontSize: 38,
55
55
  fontWeight: FontWeight.w700,
56
56
  color: Color(0xFFFFB341),
57
+ // letterSpacing: 2,
57
58
  shadows: [
58
59
  Shadow(color: Colors.black, offset: Offset(0, 1), blurRadius: 3.0),
59
60
  ],
lib/widgets/bible_selector.dart ADDED
@@ -0,0 +1,64 @@
1
+ import "package:flutter/material.dart";
2
+ import 'package:go_router/go_router.dart';
3
+ import 'package:only_bible_app/widgets/books_list.dart';
4
+ import 'package:only_bible_app/widgets/chapters_list.dart';
5
+ import 'package:only_bible_app/state.dart';
6
+ import 'package:only_bible_app/widgets/tile.dart';
7
+
8
+ import '../models.dart';
9
+
10
+ class BibleSelector extends StatelessWidget {
11
+ const BibleSelector({super.key});
12
+
13
+ @override
14
+ Widget build(BuildContext context) {
15
+ return Container(
16
+ margin: EdgeInsets.only(top: isWide(context) ? 5 : 0, left: 20),
17
+ child: ListView(
18
+ children: [
19
+ Column(
20
+ crossAxisAlignment: CrossAxisAlignment.start,
21
+ children: [
22
+ Container(
23
+ margin: const EdgeInsets.only(bottom: 20),
24
+ child: Row(
25
+ mainAxisAlignment: MainAxisAlignment.center,
26
+ children: [
27
+ Expanded(
28
+ child: Text("Bibles", style: Theme.of(context).textTheme.headlineMedium),
29
+ ),
30
+ Container(
31
+ margin: EdgeInsets.only(right: isWide(context) ? 30 : 10),
32
+ child: IconButton(
33
+ icon: const Icon(Icons.close, size: 28),
34
+ onPressed: () {
35
+ Navigator.of(context).pop();
36
+ },
37
+ ),
38
+ )
39
+ ],
40
+ ),
41
+ ),
42
+ Wrap(
43
+ children: List.of(
44
+ bibles.map((bible) {
45
+ return Container(
46
+ margin: const EdgeInsets.only(right: 16, bottom: 16),
47
+ child: TextButton(
48
+ style: TextButton.styleFrom(
49
+ padding: const EdgeInsets.all(20),
50
+ ),
51
+ child: Text(bible.name),
52
+ onPressed: () => changeBible(context, bible.id),
53
+ ),
54
+ );
55
+ }),
56
+ ),
57
+ ),
58
+ ],
59
+ ),
60
+ ],
61
+ ),
62
+ );
63
+ }
64
+ }
lib/widgets/book_selector.dart CHANGED
@@ -35,7 +35,7 @@ class BookSelectorState extends State<BookSelector> {
35
35
  @override
36
36
  Widget build(BuildContext context) {
37
37
  if (tab == 1) {
38
- final book = selectedBible.value[bookIndex];
38
+ final book = selectedBible.value!.books[bookIndex];
39
39
  return Container(
40
40
  margin: EdgeInsets.only(top: isWide(context) ? 5 : 0, left: 20),
41
41
  child: ChaptersList(
@@ -45,8 +45,8 @@ class BookSelectorState extends State<BookSelector> {
45
45
  ),
46
46
  );
47
47
  }
48
- final oldTestament = selectedBible.value.where((it) => it.isOldTestament()).toList();
48
+ final oldTestament = selectedBible.value!.books.where((it) => it.isOldTestament()).toList();
49
- final newTestament = selectedBible.value.where((it) => it.isNewTestament()).toList();
49
+ final newTestament = selectedBible.value!.books.where((it) => it.isNewTestament()).toList();
50
50
  return Container(
51
51
  margin: EdgeInsets.only(top: isWide(context) ? 5 : 0, left: 20),
52
52
  child: ListView(
lib/widgets/books_list.dart CHANGED
@@ -2,7 +2,7 @@ import "package:flutter/material.dart";
2
2
  import 'package:go_router/go_router.dart';
3
3
  import 'package:only_bible_app/state.dart';
4
4
  import 'package:only_bible_app/widgets/tile.dart';
5
- import 'package:only_bible_app/models/book.dart';
5
+ import 'package:only_bible_app/models.dart';
6
6
 
7
7
  class BooksList extends StatelessWidget {
8
8
  final String title;
lib/widgets/header.dart CHANGED
@@ -1,9 +1,11 @@
1
1
  import "package:flutter/material.dart";
2
2
  import 'package:flutter_reactive_value/flutter_reactive_value.dart';
3
+ import 'package:only_bible_app/widgets/book_selector.dart';
3
4
  import 'package:only_bible_app/widgets/play_button.dart';
4
5
  import 'package:only_bible_app/widgets/side_menu_page.dart';
5
6
  import 'package:only_bible_app/widgets/menu.dart';
6
7
  import 'package:only_bible_app/state.dart';
8
+ import 'package:only_bible_app/widgets/bible_selector.dart';
7
9
 
8
10
  class Header extends StatelessWidget {
9
11
  const Header({super.key});
@@ -12,7 +14,7 @@ class Header extends StatelessWidget {
12
14
  Widget build(BuildContext context) {
13
15
  final book = bookIndex.reactiveValue(context);
14
16
  final chapter = chapterIndex.reactiveValue(context);
15
- final selectedBook = selectedBible.value[book];
17
+ final selectedBook = selectedBible.value!.books[book];
16
18
  return Container(
17
19
  padding: EdgeInsets.only(
18
20
  left: 20,
@@ -41,12 +43,24 @@ class Header extends StatelessWidget {
41
43
  style: Theme.of(context).textTheme.headlineMedium,
42
44
  ),
43
45
  onPressed: () {
44
- Navigator.of(context).push(SideMenuPage());
46
+ Navigator.of(context).push(SideMenuPage(child: const BookSelector()));
45
47
  },
46
48
  ),
47
49
  Row(
48
50
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
49
51
  children: [
52
+ Container(
53
+ margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
54
+ child: TextButton(
55
+ style: TextButton.styleFrom(
56
+ padding: const EdgeInsets.all(20),
57
+ ),
58
+ child: Text(selectedBible.reactiveValue(context)!.name),
59
+ onPressed: () {
60
+ Navigator.of(context).push(SideMenuPage(child: const BibleSelector()));
61
+ },
62
+ ),
63
+ ),
50
64
  Container(
51
65
  margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
52
66
  child: const PlayButton(),
lib/widgets/side_menu_page.dart CHANGED
@@ -1,8 +1,11 @@
1
1
  import "package:flutter/material.dart";
2
- import 'package:only_bible_app/widgets/book_selector.dart';
3
2
  import 'package:only_bible_app/state.dart';
4
3
 
5
4
  class SideMenuPage extends ModalRoute<void> {
5
+ final Widget child;
6
+
7
+ SideMenuPage({required this.child});
8
+
6
9
  @override
7
10
  Duration get transitionDuration => const Duration(milliseconds: 300);
8
11
 
@@ -36,10 +39,10 @@ class SideMenuPage extends ModalRoute<void> {
36
39
  color: Theme.of(context).colorScheme.background,
37
40
  margin: EdgeInsets.only(left: 0, right: isWide(context) ? 650 : 0),
38
41
  child: isWide(context)
39
- ? const BookSelector()
42
+ ? child
40
43
  : SlideTransition(
41
44
  position: Tween(begin: const Offset(-1, 0), end: Offset.zero).animate(animation),
42
- child: const BookSelector(),
45
+ child: child,
43
46
  ),
44
47
  ),
45
48
  );
lib/widgets/sidebar.dart CHANGED
@@ -37,7 +37,7 @@ class Sidebar extends StatelessWidget {
37
37
  ),
38
38
  ),
39
39
  child: Padding(
40
- padding: const EdgeInsets.only(bottom: 50, right: 50),
40
+ padding: const EdgeInsets.only(bottom: 50, right: 50, top: 0),
41
41
  child: Column(
42
42
  mainAxisAlignment: MainAxisAlignment.center,
43
43
  crossAxisAlignment: CrossAxisAlignment.end,
@@ -68,7 +68,10 @@ class TrianglePainter extends CustomPainter {
68
68
 
69
69
  @override
70
70
  void paint(Canvas canvas, Size size) {
71
+ final paint = Paint()
72
+ ..color = color
73
+ ..style = PaintingStyle.fill;
71
- Path path = Path();
74
+ final path = Path();
72
75
  path.moveTo(0, 0);
73
76
  path.lineTo(15, 0);
74
77
  path.lineTo(15, 30);
@@ -77,12 +80,9 @@ class TrianglePainter extends CustomPainter {
77
80
  path.lineTo(-15, 0);
78
81
  path.lineTo(-15, 30);
79
82
  path.close();
83
+ // canvas.drawRect(const Offset(30, 100) & const Size(40, 300), paint);
84
+ // canvas.drawRect(const Offset(-20, 160) & const Size(140, 40), paint);
80
- canvas.drawPath(
85
+ canvas.drawPath(path, paint);
81
- path,
82
- Paint()
83
- ..color = color
84
- ..style = PaintingStyle.fill,
85
- );
86
86
 
87
87
  canvas.save();
88
88
  canvas.restore();
macos/Runner/Assets.xcassets/AppIcon.appiconset/logo.png ADDED
Binary file