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


469b205a pyrossh

2 years ago
improve state
bible_app/lib/components/book_selector.dart CHANGED
@@ -1,35 +1,59 @@
1
1
  import "package:flutter/material.dart";
2
- import "package:flutter_reactive_value/flutter_reactive_value.dart";
3
- import '../models/book.dart';
2
+ import 'package:go_router/go_router.dart';
4
- import '../state.dart';
5
3
  import 'books_list.dart';
6
4
  import 'chapters_list.dart';
5
+ import '../state.dart';
7
6
 
8
- // TODO: use local state for tab instead of global
9
-
10
- class BookSelector extends StatelessWidget {
7
+ class BookSelector extends StatefulWidget {
11
8
  const BookSelector({super.key});
12
9
 
10
+ @override
11
+ State<StatefulWidget> createState() => BookSelectorState();
12
+ }
13
+
14
+ class BookSelectorState extends State<BookSelector> {
15
+ int tab = 0;
16
+ int bookIndex = 0;
17
+
18
+ @override
19
+ void initState() {
20
+ super.initState();
21
+ tab = 0;
22
+ }
23
+
24
+ onBookSelected(int index) {
25
+ setState(() {
26
+ bookIndex = index;
27
+ tab = 1;
28
+ });
29
+ }
30
+
31
+ onChapterSelected(int index) {
32
+ saveBookIndex(bookIndex, index);
33
+ context.push("/${selectedBible.value[bookIndex].name}/$index");
34
+ context.pop();
35
+ }
36
+
13
37
  @override
14
38
  Widget build(BuildContext context) {
15
- final tab = tabIndex.reactiveValue(context);
16
39
  if (tab == 1) {
40
+ final book = selectedBible.value[bookIndex];
17
41
  return Container(
18
42
  margin: const EdgeInsets.only(top: 15, left: 20),
19
- child: const ChaptersList(),
43
+ child: ChaptersList(title: book.name, length: book.chapters.length, onTap: onChapterSelected),
20
44
  );
21
45
  }
22
- final oldTestament = selectedBible.value.where((it) => it.isOldTestament()).map((it) => it.name).toList();
46
+ final oldTestament = selectedBible.value.where((it) => it.isOldTestament()).toList();
23
- final newTestament = selectedBible.value.where((it) => it.isNewTestament()).map((it) => it.name).toList();
47
+ final newTestament = selectedBible.value.where((it) => it.isNewTestament()).toList();
24
48
  return Container(
25
49
  margin: const EdgeInsets.only(top: 15, left: 20),
26
50
  child: ListView(
27
51
  children: [
28
- BooksList(title: "Old Testament", offset: 0, books: oldTestament),
52
+ BooksList(title: "Old Testament", books: oldTestament, onTap: onBookSelected),
29
53
  Container(
30
54
  margin: const EdgeInsets.only(bottom: 10),
31
55
  ),
32
- BooksList(title: "New Testament", offset: 39, books: newTestament),
56
+ BooksList(title: "New Testament", books: newTestament, onTap: onBookSelected),
33
57
  ],
34
58
  ),
35
59
  );
bible_app/lib/components/books_list.dart CHANGED
@@ -1,14 +1,13 @@
1
1
  import "package:flutter/material.dart";
2
- import "../utils/string.dart";
3
- import "../state.dart";
4
2
  import "tile.dart";
3
+ import "../models/book.dart";
5
4
 
6
5
  class BooksList extends StatelessWidget {
7
6
  final String title;
8
- final int offset;
9
- final List<String> books;
7
+ final List<Book> books;
8
+ final Function(int) onTap;
10
9
 
11
- const BooksList({super.key, required this.title, required this.offset, required this.books});
10
+ const BooksList({super.key, required this.title, required this.books, required this.onTap});
12
11
 
13
12
  @override
14
13
  Widget build(BuildContext context) {
@@ -17,24 +16,20 @@ class BooksList extends StatelessWidget {
17
16
  children: [
18
17
  Container(
19
18
  margin: const EdgeInsets.only(bottom: 20),
20
- child: Text(title, style: Theme
21
- .of(context)
19
+ child: Text(
22
- .textTheme
20
+ title,
23
- .headlineMedium,
21
+ style: Theme.of(context).textTheme.headlineMedium,
24
22
  ),
25
23
  ),
26
24
  Wrap(
27
- children: List.generate(books.length, (index) {
25
+ children: List.of(books.map((book) {
28
- final name = books[index].bookShortName();
26
+ final name = book.shortName();
29
27
  return InkWell(
30
28
  borderRadius: const BorderRadius.all(Radius.circular(30)),
31
- onTap: () {
32
- tabBookIndex.value = offset + index;
29
+ onTap: () => onTap(book.index),
33
- tabIndex.value = 1;
34
- },
35
30
  child: Tile(name: name),
36
31
  );
37
- }),
32
+ })),
38
33
  ),
39
34
  ],
40
35
  );
bible_app/lib/components/chapters_list.dart CHANGED
@@ -1,32 +1,26 @@
1
1
  import "package:flutter/material.dart";
2
- import 'package:flutter_reactive_value/flutter_reactive_value.dart';
3
- import 'package:go_router/go_router.dart';
4
- import '../state.dart';
5
2
  import 'tile.dart';
6
3
 
7
4
  class ChaptersList extends StatelessWidget {
5
+ final String title;
6
+ final int length;
8
- const ChaptersList({super.key});
7
+ final Function(int) onTap;
8
+
9
+ const ChaptersList({super.key, required this.title, required this.length, required this.onTap});
9
10
 
10
11
  @override
11
12
  Widget build(BuildContext context) {
12
- final bookIndex = tabBookIndex.reactiveValue(context);
13
- final book = selectedBible.value[bookIndex];
14
13
  return ListView(
15
14
  children: [
16
15
  Container(
17
16
  margin: const EdgeInsets.only(bottom: 20),
18
- child: Text(book.name, style: Theme.of(context).textTheme.headlineMedium),
17
+ child: Text(title, style: Theme.of(context).textTheme.headlineMedium),
19
18
  ),
20
19
  Wrap(
21
- children: List.generate(book.chapters.length, (index) {
20
+ children: List.generate(length, (index) {
22
21
  return InkWell(
23
22
  borderRadius: const BorderRadius.all(Radius.circular(30)),
24
- onTap: () {
25
- saveBookIndex(bookIndex, index);
23
+ onTap: () => onTap(index),
26
- context.push("/${book.name}/$index");
27
- context.pop();
28
- resetTab();
29
- },
30
24
  child: Tile(name: "${index + 1}"),
31
25
  );
32
26
  }),
bible_app/lib/components/header.dart CHANGED
@@ -2,6 +2,7 @@ import "dart:math";
2
2
  import "package:flutter/material.dart";
3
3
  import "./play_button.dart";
4
4
  import "./side_menu_page.dart";
5
+ import "./menu.dart";
5
6
  import '../models/book.dart';
6
7
  import "../state.dart";
7
8
 
@@ -45,6 +46,7 @@ class Header extends StatelessWidget {
45
46
  ),
46
47
  const Spacer(flex: 1),
47
48
  PlayButton(book: book, chapter: chapter, verses: verses),
49
+ Menu(),
48
50
  // const Spacer(flex: 1),
49
51
  ],
50
52
  ),
bible_app/lib/components/play_button.dart CHANGED
@@ -16,6 +16,7 @@ class PlayButton extends StatelessWidget {
16
16
  Widget build(BuildContext context) {
17
17
  final icon = isPlaying.reactiveValue(context) ? Icons.pause_circle_filled : Icons.play_circle_fill;
18
18
  return IconButton(
19
+ padding: EdgeInsets.symmetric(horizontal: 50),
19
20
  icon: Icon(icon, size: 36),
20
21
  onPressed: () async {
21
22
  final player = AudioPlayer();
bible_app/lib/components/side_menu_page.dart CHANGED
@@ -35,7 +35,7 @@ class SideMenuPage extends ModalRoute<void> {
35
35
  child: Container(
36
36
  color: Colors.white,
37
37
  margin: EdgeInsets.only(left: 0, right: isDesktop() ? 650 : 0),
38
- child: BookSelector(),
38
+ child: const BookSelector(),
39
39
  ),
40
40
  );
41
41
  }
bible_app/lib/main.dart CHANGED
@@ -12,11 +12,9 @@ void main() async {
12
12
  await initPersistentValueNotifier();
13
13
  await loadBible();
14
14
  runApp(MaterialApp.router(
15
- debugShowCheckedModeBanner: false,
16
15
  routerConfig: GoRouter(
17
- navigatorKey: GlobalKey<NavigatorState>(),
18
- initialLocation: "/${selectedBible.value[bookIndex.value].name}/${chapterIndex.value}",
19
16
  debugLogDiagnostics: true,
17
+ initialLocation: "/${selectedBible.value[bookIndex.value].name}/${chapterIndex.value}",
20
18
  routes: [
21
19
  Shell(
22
20
  routes: [
bible_app/lib/models/book.dart CHANGED
@@ -9,6 +9,13 @@ class Book {
9
9
  bool isOldTestament() => index < 39;
10
10
 
11
11
  bool isNewTestament() => index >= 39;
12
+
13
+ String shortName() {
14
+ if (name[0] == "1" || name[0] == "2" || name[0] == "3") {
15
+ return "${name[0]}${name[2].toUpperCase()}${name.substring(3, 4).toLowerCase()}";
16
+ }
17
+ return "${name[0].toUpperCase()}${name.substring(1, 3).toLowerCase()}";
18
+ }
12
19
  }
13
20
 
14
21
  class Chapter {
bible_app/lib/state.dart CHANGED
@@ -2,7 +2,6 @@ import 'dart:convert';
2
2
  import 'dart:io' show GZipCodec, Platform;
3
3
  import 'package:flutter/services.dart';
4
4
  import 'package:flutter/material.dart';
5
- import 'package:flutter/scheduler.dart';
6
5
  import 'package:flutter_persistent_value_notifier/flutter_persistent_value_notifier.dart';
7
6
  import 'package:flutter_reactive_value/flutter_reactive_value.dart';
8
7
  import 'models/book.dart';
@@ -95,20 +94,6 @@ onVerseSelected(int i) {
95
94
  }
96
95
  }
97
96
 
98
- final tabIndex = ValueNotifier(0);
99
- final tabBookIndex = ValueNotifier(0);
100
-
101
- onTabBookChange(int i) {
102
- tabBookIndex.value = i;
103
- tabIndex.value = 1;
104
- }
105
-
106
- resetTab() {
107
- SchedulerBinding.instance.addPostFrameCallback((_) {
108
- tabIndex.value = 0;
109
- });
110
- }
111
-
112
97
  bool isDesktop() {
113
98
  return Platform.isMacOS || Platform.isLinux || Platform.isWindows;
114
99
  }
bible_app/lib/utils/string.dart DELETED
@@ -1,9 +0,0 @@
1
- extension StringExtension on String {
2
- String bookShortName() {
3
- // final name = replaceAll(" ", "").substring(0, 3);
4
- if (this[0] == "1" || this[0] == "2" || this[0] == "3") {
5
- return "${this[0]}${this[2].toUpperCase()}${substring(3, 4).toLowerCase()}";
6
- }
7
- return "${this[0].toUpperCase()}${substring(1, 3).toLowerCase()}";
8
- }
9
- }