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


65bf36fe pyrossh

2 years ago
improve app overall
README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # Only Bible App
2
2
 
3
- minSdkVersion 30
3
+ Fix dialog theme
4
- targetSdkVersion 34
5
4
 
6
5
  ## Setup
7
6
 
@@ -44,6 +43,5 @@ firebase deploy
44
43
  ## Web
45
44
  ```agsl
46
45
  https://only-bible-app.web.app
47
- https://only-bible-app.firebaseapp.app
48
46
  https://onlybible.app
49
47
  ```
android/app/build.gradle CHANGED
@@ -51,8 +51,8 @@ android {
51
51
  applicationId "sh.pyros.only_bible_app"
52
52
  // You can update the following values to match your application needs.
53
53
  // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
54
- minSdkVersion flutter.minSdkVersion
54
+ minSdkVersion 30
55
- targetSdkVersion flutter.targetSdkVersion
55
+ targetSdkVersion 34
56
56
  versionCode flutterVersionCode.toInteger()
57
57
  versionName flutterVersionName
58
58
  }
android/build.gradle CHANGED
@@ -8,7 +8,7 @@ buildscript {
8
8
  dependencies {
9
9
  classpath 'com.android.tools.build:gradle:7.3.0'
10
10
  // START: FlutterFire Configuration
11
- classpath 'com.google.gms:google-services:4.3.10'
11
+ classpath 'com.google.gms:google-services:4.3.14'
12
12
  // END: FlutterFire Configuration
13
13
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
14
  }
ios/Podfile.lock CHANGED
@@ -13,9 +13,6 @@ PODS:
13
13
  - FirebaseCoreInternal (10.13.0):
14
14
  - "GoogleUtilities/NSData+zlib (~> 7.8)"
15
15
  - Flutter (1.0.0)
16
- - flutter_charset_detector_ios (0.0.1):
17
- - Flutter
18
- - UniversalDetector2 (= 2.0.1)
19
16
  - flutter_native_splash (0.0.1):
20
17
  - Flutter
21
18
  - GoogleUtilities/Environment (7.11.5):
@@ -34,13 +31,11 @@ PODS:
34
31
  - shared_preferences_foundation (0.0.1):
35
32
  - Flutter
36
33
  - FlutterMacOS
37
- - UniversalDetector2 (2.0.1)
38
34
 
39
35
  DEPENDENCIES:
40
36
  - audio_session (from `.symlinks/plugins/audio_session/ios`)
41
37
  - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
42
38
  - Flutter (from `Flutter`)
43
- - flutter_charset_detector_ios (from `.symlinks/plugins/flutter_charset_detector_ios/ios`)
44
39
  - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
45
40
  - integration_test (from `.symlinks/plugins/integration_test/ios`)
46
41
  - just_audio (from `.symlinks/plugins/just_audio/ios`)
@@ -54,7 +49,6 @@ SPEC REPOS:
54
49
  - FirebaseCoreInternal
55
50
  - GoogleUtilities
56
51
  - PromisesObjC
57
- - UniversalDetector2
58
52
 
59
53
  EXTERNAL SOURCES:
60
54
  audio_session:
@@ -63,8 +57,6 @@ EXTERNAL SOURCES:
63
57
  :path: ".symlinks/plugins/firebase_core/ios"
64
58
  Flutter:
65
59
  :path: Flutter
66
- flutter_charset_detector_ios:
67
- :path: ".symlinks/plugins/flutter_charset_detector_ios/ios"
68
60
  flutter_native_splash:
69
61
  :path: ".symlinks/plugins/flutter_native_splash/ios"
70
62
  integration_test:
@@ -83,7 +75,6 @@ SPEC CHECKSUMS:
83
75
  FirebaseCore: f86a1394906b97ac445ae49c92552a9425831bed
84
76
  FirebaseCoreInternal: b342e37cd4f5b4454ec34308f073420e7920858e
85
77
  Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
86
- flutter_charset_detector_ios: 5157d0104855b9deb78e1395515a287197bc2d55
87
78
  flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
88
79
  GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084
89
80
  integration_test: 13825b8a9334a850581300559b8839134b124670
@@ -91,7 +82,6 @@ SPEC CHECKSUMS:
91
82
  path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
92
83
  PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4
93
84
  shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
94
- UniversalDetector2: 7c9ffd935cf050eeb19edf7e90f6febe3743a1af
95
85
 
96
86
  PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
97
87
 
lib/app.dart CHANGED
@@ -1,10 +1,8 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:flutter_gen/gen_l10n/app_localizations.dart";
3
3
  import "package:flutter_reactive_value/flutter_reactive_value.dart";
4
- import "package:go_router/go_router.dart";
5
- import "package:only_bible_app/routes/home_screen.dart";
4
+ import "package:only_bible_app/screens/chapter_view_screen.dart";
6
5
  import "package:only_bible_app/state.dart";
7
- import "package:only_bible_app/widgets/sidebar.dart";
8
6
  import "package:only_bible_app/theme.dart";
9
7
 
10
8
  class App extends StatelessWidget {
@@ -12,7 +10,7 @@ class App extends StatelessWidget {
12
10
 
13
11
  @override
14
12
  Widget build(BuildContext context) {
15
- return MaterialApp.router(
13
+ return MaterialApp(
16
14
  title: "Only Bible App",
17
15
  localizationsDelegates: AppLocalizations.localizationsDelegates,
18
16
  supportedLocales: AppLocalizations.supportedLocales,
@@ -20,44 +18,45 @@ class App extends StatelessWidget {
20
18
  themeMode: darkMode.reactiveValue(context) ? ThemeMode.dark : ThemeMode.light,
21
19
  theme: lightTheme,
22
20
  darkTheme: darkTheme,
21
+ home: ChapterViewScreen(book: bookIndex.value, chapter: chapterIndex.value),
23
- routerConfig: GoRouter(
22
+ // routerConfig: GoRouter(
24
- debugLogDiagnostics: true,
23
+ // debugLogDiagnostics: true,
25
- initialLocation: "/${selectedBible.value!.books[bookIndex.value].name}/${chapterIndex.value}",
24
+ // initialLocation: "/${selectedBible.value!.books[bookIndex.value].name}/${chapterIndex.value}",
26
- routes: [
25
+ // screens: [
27
- ShellRoute(
26
+ // ShellRoute(
28
- builder: (context, state, child) {
27
+ // builder: (context, state, child) {
29
- if (isWide(context)) {
28
+ // if (isWide(context)) {
29
+ // return Scaffold(
30
+ // backgroundColor: Theme.of(context).colorScheme.background,
31
+ // body: Row(
32
+ // children: [
33
+ // const Sidebar(),
34
+ // Flexible(
35
+ // child: child,
36
+ // ),
37
+ // ],
38
+ // ),
39
+ // );
40
+ // }
30
- return Scaffold(
41
+ // return Scaffold(
31
- backgroundColor: Theme.of(context).colorScheme.background,
42
+ // backgroundColor: Theme.of(context).colorScheme.background,
32
- body: Row(
33
- children: [
34
- const Sidebar(),
35
- Flexible(
36
- child: child,
37
- ),
38
- ],
39
- ),
40
- );
41
- }
42
- return Scaffold(
43
- backgroundColor: Theme.of(context).colorScheme.background,
44
- body: SafeArea(
43
+ // body: SafeArea(
45
- child: child,
44
+ // child: child,
46
- ),
45
+ // ),
47
- );
46
+ // );
48
- },
47
+ // },
49
- routes: [
48
+ // screens: [
50
- GoRouteData.$route(
49
+ // GoRouteData.$route(
51
- path: "/:book/:chapter",
50
+ // path: "/:book/:chapter",
52
- factory: (GoRouterState state) => HomeScreen(
51
+ // factory: (GoRouterState state) => HomeScreen(
53
- book: state.pathParameters["book"]!,
52
+ // book: state.pathParameters["book"]!,
54
- chapter: int.parse(state.pathParameters["chapter"]!),
53
+ // chapter: int.parse(state.pathParameters["chapter"]!),
54
+ // ),
55
+ // ),
56
+ // ],
57
+ // ),
58
+ // ],
55
- ),
59
+ // ),
56
- ),
57
- ],
58
- ),
59
- ],
60
- ),
61
60
  );
62
61
  }
63
62
  }
lib/models.dart CHANGED
@@ -148,3 +148,44 @@ final bibles = [
148
148
  Bible(id: 10, name: "Telugu"),
149
149
  Bible(id: 11, name: "Bengali"),
150
150
  ];
151
+
152
+ List<Book> getBibleFromText(String text) {
153
+ final List<Book> books = [];
154
+ final lines = text.split("\n");
155
+ for (var (index, line) in lines.indexed) {
156
+ // ignore last empty line
157
+ if (lines.length - 1 == index) {
158
+ continue;
159
+ }
160
+ var book = int.parse(line.substring(0, 2));
161
+ var chapter = int.parse(line.substring(3, 6));
162
+ // var verseNo = line.substring(7, 10);
163
+ var verseText = line.substring(11);
164
+ double start = 0;
165
+ double end = 0;
166
+ // if (item.length > 4) {
167
+ // start = double.parse(item[4]);
168
+ // end = double.parse(item[5]);
169
+ // }
170
+ if (books.length < book) {
171
+ books.add(
172
+ Book(
173
+ index: book - 1,
174
+ name: bookNames[book-1],
175
+ chapters: [],
176
+ ),
177
+ );
178
+ }
179
+ if (books[book - 1].chapters.length < chapter) {
180
+ // ignore: prefer_const_constructors
181
+ books[book - 1].chapters.add(Chapter(verses: []));
182
+ }
183
+ books[book - 1].chapters[chapter - 1].verses.add(
184
+ Verse(
185
+ text: verseText,
186
+ audioRange: TimeRange(start: start, end: end),
187
+ ),
188
+ );
189
+ }
190
+ return books;
191
+ }
lib/routes/home_screen.dart DELETED
@@ -1,98 +0,0 @@
1
- import "package:flutter/material.dart";
2
- import 'package:flutter_reactive_value/flutter_reactive_value.dart';
3
- import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
4
- import 'package:go_router/go_router.dart';
5
- import 'package:only_bible_app/widgets/header.dart';
6
- import 'package:only_bible_app/widgets/verse_view.dart';
7
- import 'package:only_bible_app/state.dart';
8
-
9
- class HomeScreen extends GoRouteData {
10
- final String book;
11
- final int chapter;
12
-
13
- HomeScreen({required this.book, required this.chapter}) {
14
- selectedVerses.value.clear();
15
- }
16
-
17
- @override
18
- Page buildPage(BuildContext context, GoRouterState state) {
19
- if (slideTextDir.value == null) {
20
- return NoTransitionPage(
21
- child: SwipeDetector(
22
- onSwipeLeft: (offset) {
23
- onNext(context);
24
- },
25
- onSwipeRight: (offset) {
26
- onPrevious(context);
27
- },
28
- child: const Column(
29
- children: [
30
- Header(),
31
- Flexible(
32
- child: VerseList(),
33
- ),
34
- ],
35
- ),
36
- ),
37
- );
38
- }
39
- return CustomTransitionPage(
40
- barrierDismissible: false,
41
- barrierColor: Theme.of(context).colorScheme.background,
42
- transitionDuration: const Duration(milliseconds: 360),
43
- transitionsBuilder: (context, animation, secondaryAnimation, child) {
44
- return SwipeDetector(
45
- onSwipeLeft: (offset) {
46
- onNext(context);
47
- },
48
- onSwipeRight: (offset) {
49
- onPrevious(context);
50
- },
51
- child: Column(
52
- children: [
53
- const Header(),
54
- Flexible(
55
- child: SlideTransition(
56
- textDirection: slideTextDir.value,
57
- position: Tween(begin: const Offset(1, 0), end: Offset.zero)
58
- .chain(CurveTween(curve: Curves.linear))
59
- .animate(animation),
60
- child: child,
61
- ),
62
- ),
63
- ],
64
- ),
65
- );
66
- },
67
- child: const VerseList(),
68
- );
69
- }
70
- }
71
-
72
- class VerseList extends StatelessWidget {
73
- const VerseList({super.key});
74
-
75
- @override
76
- Widget build(BuildContext context) {
77
- final selectedBook = selectedBible.reactiveValue(context)!.books[bookIndex.value];
78
- final verses = selectedBook.chapters[chapterIndex.value].verses;
79
- return SelectionArea(
80
- child: ListView.builder(
81
- shrinkWrap: false,
82
- padding: const EdgeInsets.only(
83
- left: 20,
84
- right: 20,
85
- bottom: 20,
86
- ),
87
- itemCount: verses.length,
88
- itemBuilder: (BuildContext context, int index) {
89
- final v = verses[index];
90
- return Container(
91
- margin: const EdgeInsets.symmetric(vertical: 6),
92
- child: VerseText(index: index, text: v.text),
93
- );
94
- },
95
- ),
96
- );
97
- }
98
- }
lib/screens/bible_select_screen.dart ADDED
@@ -0,0 +1,32 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/state.dart";
3
+ import "package:only_bible_app/models.dart";
4
+ import "package:only_bible_app/widgets/scaffold_menu.dart";
5
+ import "package:only_bible_app/widgets/sliver_heading.dart";
6
+ import "package:only_bible_app/widgets/sliver_tile_grid.dart";
7
+
8
+ class BibleSelectScreen extends StatelessWidget {
9
+ const BibleSelectScreen({super.key});
10
+
11
+ @override
12
+ Widget build(BuildContext context) {
13
+ return ScaffoldMenu(
14
+ child: CustomScrollView(
15
+ slivers: [
16
+ const SliverHeading(title: "Bibles", showClose: true),
17
+ SliverTileGrid(
18
+ listType: ListType.large,
19
+ children: List.of(
20
+ bibles.map((bible) {
21
+ return TextButton(
22
+ child: Text(bible.name),
23
+ onPressed: () => changeBible(context, bible.id),
24
+ );
25
+ }),
26
+ ),
27
+ ),
28
+ ],
29
+ ),
30
+ );
31
+ }
32
+ }
lib/screens/book_select_screen.dart ADDED
@@ -0,0 +1,55 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/state.dart";
3
+ import "package:only_bible_app/widgets/scaffold_menu.dart";
4
+ import "package:only_bible_app/screens/chapter_select_screen.dart";
5
+ import "package:only_bible_app/widgets/sliver_heading.dart";
6
+ import "package:only_bible_app/widgets/sliver_tile_grid.dart";
7
+
8
+ class BookSelectScreen extends StatelessWidget {
9
+ const BookSelectScreen({super.key});
10
+
11
+ onBookSelected(BuildContext context, int index) {
12
+ Navigator.of(context).pushReplacement(
13
+ PageRouteBuilder(
14
+ opaque: false,
15
+ transitionDuration: Duration.zero,
16
+ reverseTransitionDuration: Duration.zero,
17
+ pageBuilder: (context, _, __) => ChapterSelectScreen(
18
+ selectedBookIndex: index,
19
+ ),
20
+ ),
21
+ );
22
+ }
23
+
24
+ @override
25
+ Widget build(BuildContext context) {
26
+ return ScaffoldMenu(
27
+ child: CustomScrollView(
28
+ slivers: [
29
+ const SliverHeading(title: "Old Testament", showClose: true),
30
+ SliverTileGrid(
31
+ children: List.of(
32
+ selectedBible.value!.getOldBooks().map((book) {
33
+ return TextButton(
34
+ child: Text(book.shortName()),
35
+ onPressed: () => onBookSelected(context, book.index),
36
+ );
37
+ }),
38
+ ),
39
+ ),
40
+ const SliverHeading(title: "New Testament", top: 30, bottom: 20),
41
+ SliverTileGrid(
42
+ children: List.of(
43
+ selectedBible.value!.getNewBooks().map((book) {
44
+ return TextButton(
45
+ child: Text(book.shortName()),
46
+ onPressed: () => onBookSelected(context, book.index),
47
+ );
48
+ }),
49
+ ),
50
+ ),
51
+ ],
52
+ ),
53
+ );
54
+ }
55
+ }
lib/screens/chapter_select_screen.dart ADDED
@@ -0,0 +1,35 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/state.dart";
3
+ import "package:only_bible_app/widgets/scaffold_menu.dart";
4
+ import "package:only_bible_app/widgets/sliver_tile_grid.dart";
5
+ import "package:only_bible_app/widgets/sliver_heading.dart";
6
+
7
+ class ChapterSelectScreen extends StatelessWidget {
8
+ final int selectedBookIndex;
9
+
10
+ const ChapterSelectScreen({super.key, required this.selectedBookIndex});
11
+
12
+ onChapterSelected(BuildContext context, int index) {
13
+ navigateBookChapter(context, selectedBookIndex, index, true);
14
+ }
15
+
16
+ @override
17
+ Widget build(BuildContext context) {
18
+ final book = selectedBible.value!.books[selectedBookIndex];
19
+ return ScaffoldMenu(
20
+ child: CustomScrollView(
21
+ slivers: [
22
+ SliverHeading(title: book.name, showClose: true),
23
+ SliverTileGrid(
24
+ children: List.generate(book.chapters.length, (index) {
25
+ return TextButton(
26
+ child: Text("${index + 1}"),
27
+ onPressed: () => onChapterSelected(context, index),
28
+ );
29
+ }),
30
+ ),
31
+ ],
32
+ ),
33
+ );
34
+ }
35
+ }
lib/screens/chapter_view_screen.dart ADDED
@@ -0,0 +1,64 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:flutter_reactive_value/flutter_reactive_value.dart";
3
+ import "package:flutter_swipe_detector/flutter_swipe_detector.dart";
4
+ import "package:only_bible_app/widgets/header.dart";
5
+ import "package:only_bible_app/state.dart";
6
+ import "package:only_bible_app/widgets/play_button.dart";
7
+ import "package:only_bible_app/widgets/sidebar.dart";
8
+ import "package:only_bible_app/widgets/verse_list.dart";
9
+
10
+ class ChapterViewScreen extends StatelessWidget {
11
+ final int book;
12
+ final int chapter;
13
+
14
+ const ChapterViewScreen({super.key, required this.book, required this.chapter});
15
+
16
+ @override
17
+ Widget build(BuildContext context) {
18
+ final isDesktop = isWide(context);
19
+ final showPlay = selectedVerses.reactiveValue(context).isNotEmpty;
20
+ return Scaffold(
21
+ backgroundColor: Theme.of(context).colorScheme.background,
22
+ bottomSheet: !isDesktop && showPlay
23
+ ? BottomSheet(
24
+ enableDrag: false,
25
+ onClosing: () {},
26
+ builder: (BuildContext ctx) => Container(
27
+ padding: const EdgeInsets.only(bottom: 40),
28
+ child: const Row(
29
+ mainAxisAlignment: MainAxisAlignment.center,
30
+ children: [
31
+ PlayButton(),
32
+ ],
33
+ ),
34
+ ),
35
+ )
36
+ : null,
37
+ body: SafeArea(
38
+ child: SwipeDetector(
39
+ onSwipeLeft: (offset) {
40
+ onNext(context);
41
+ },
42
+ onSwipeRight: (offset) {
43
+ onPrevious(context);
44
+ },
45
+ child: Row(
46
+ children: [
47
+ if (isWide(context)) const Sidebar(),
48
+ const Flexible(
49
+ child: Column(
50
+ children: [
51
+ Header(),
52
+ Flexible(
53
+ child: VerseList(),
54
+ ),
55
+ ],
56
+ ),
57
+ ),
58
+ ],
59
+ ),
60
+ ),
61
+ ),
62
+ );
63
+ }
64
+ }
lib/state.dart CHANGED
@@ -1,40 +1,39 @@
1
- import 'dart:convert';
1
+ import "dart:convert";
2
- import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform;
2
+ import "package:flutter/foundation.dart" show defaultTargetPlatform, TargetPlatform;
3
- import 'package:flutter/services.dart';
3
+ import "package:flutter/services.dart";
4
- import 'package:flutter/material.dart';
4
+ import "package:flutter/material.dart";
5
- import 'package:flutter_persistent_value_notifier/flutter_persistent_value_notifier.dart';
5
+ import "package:flutter_persistent_value_notifier/flutter_persistent_value_notifier.dart";
6
- import 'package:flutter_reactive_value/flutter_reactive_value.dart';
6
+ import "package:flutter_reactive_value/flutter_reactive_value.dart";
7
- import 'package:go_router/go_router.dart';
8
- import 'package:just_audio/just_audio.dart';
7
+ import "package:just_audio/just_audio.dart";
9
- import 'package:only_bible_app/utils.dart';
8
+ import "package:only_bible_app/screens/chapter_view_screen.dart";
10
- import 'package:only_bible_app/utils/dialog.dart';
9
+ import "package:only_bible_app/utils/dialog.dart";
11
- import 'package:only_bible_app/models.dart';
10
+ import "package:only_bible_app/models.dart";
12
11
 
13
12
  final shellNavigatorKey = GlobalKey<NavigatorState>();
14
13
  final routeNavigatorKey = GlobalKey<NavigatorState>();
15
14
 
16
15
  final darkMode = PersistentValueNotifier<bool>(
17
- sharedPreferencesKey: 'darkMode',
16
+ sharedPreferencesKey: "darkMode",
18
17
  initialValue: false,
19
18
  );
20
19
 
21
20
  final fontBold = PersistentValueNotifier<bool>(
22
- sharedPreferencesKey: 'fontBold',
21
+ sharedPreferencesKey: "fontBold",
23
22
  initialValue: false,
24
23
  );
25
24
 
26
25
  final selectedBibleId = PersistentValueNotifier(
27
- sharedPreferencesKey: 'selectedBibleId',
26
+ sharedPreferencesKey: "selectedBibleId",
28
27
  initialValue: 1,
29
28
  );
30
29
 
31
30
  final bookIndex = PersistentValueNotifier<int>(
32
- sharedPreferencesKey: 'bookIndex',
31
+ sharedPreferencesKey: "bookIndex",
33
32
  initialValue: 0,
34
33
  );
35
34
 
36
35
  final chapterIndex = PersistentValueNotifier<int>(
37
- sharedPreferencesKey: 'chapterIndex',
36
+ sharedPreferencesKey: "chapterIndex",
38
37
  initialValue: 0,
39
38
  );
40
39
 
@@ -94,19 +93,44 @@ changeBible(BuildContext context, int i) {
94
93
  Navigator.of(context).pop();
95
94
  }
96
95
 
97
- navigateBookChapter(BuildContext context, int book, int chapter, bool noAnim) {
96
+ createSlideRoute({required BuildContext context, TextDirection? slideDir, required Widget page}) {
98
- if (isWide(context) || noAnim) {
97
+ if (isWide(context) || slideDir == null) {
98
+ return PageRouteBuilder(
99
- slideTextDir.value = null;
99
+ pageBuilder: (context, _, __) {
100
- } else {
100
+ return page;
101
+ },
101
- slideTextDir.value = bookIndex.value > book || chapterIndex.value > chapter ? TextDirection.rtl : TextDirection.ltr;
102
+ );
102
103
  }
104
+ return PageRouteBuilder(
105
+ pageBuilder: (context, animation, secondaryAnimation) => page,
106
+ transitionsBuilder: (context, animation, secondaryAnimation, child) {
107
+ const begin = Offset(1.0, 0.0);
108
+ const end = Offset.zero;
109
+ const curve = Curves.ease;
110
+ var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
111
+ return SlideTransition(
112
+ textDirection: slideDir,
113
+ position: animation.drive(tween),
114
+ child: child,
115
+ );
116
+ },
117
+ );
118
+ }
119
+
120
+ navigateBookChapter(BuildContext context, int book, int chapter, bool noAnim) {
121
+ final slideDir = bookIndex.value > book || chapterIndex.value > chapter ? TextDirection.rtl : TextDirection.ltr;
122
+ // TODO: add bible param here maybe
123
+ // route: /bible/book/chapter
103
124
  bookIndex.value = book;
104
125
  chapterIndex.value = chapter;
105
- context.push("/${selectedBible.value!.books[book].name}/$chapter");
106
- // Use this or use navigatorKey once header moves scaffold
107
- // if (!isWide(context)) {
126
+ selectedVerses.value.clear();
108
- // Navigator.of(context).pop();
127
+ Navigator.of(context).push(
128
+ createSlideRoute(
109
- // }
129
+ context: context,
130
+ slideDir: noAnim ? null : slideDir,
131
+ page: ChapterViewScreen(book: book, chapter: chapter),
132
+ ),
133
+ );
110
134
  }
111
135
 
112
136
  onNext(BuildContext context) {
@@ -161,9 +185,9 @@ onPlay(BuildContext context) async {
161
185
  isPlaying.value = true;
162
186
  for (final v in selectedVerses.value) {
163
187
  final bibleName = selectedBible.value!.name;
164
- final book = (bookIndex.value + 1).toString().padLeft(2, '0');
188
+ final book = (bookIndex.value + 1).toString().padLeft(2, "0");
165
- final chapter = (chapterIndex.value + 1).toString().padLeft(3, '0');
189
+ final chapter = (chapterIndex.value + 1).toString().padLeft(3, "0");
166
- final verse = (v + 1).toString().padLeft(3, '0');
190
+ final verse = (v + 1).toString().padLeft(3, "0");
167
191
  await player.setUrl(
168
192
  "http://localhost:3000/$bibleName/$book-$chapter-$verse.mp3",
169
193
  );
lib/theme.dart CHANGED
@@ -1,4 +1,4 @@
1
- import 'package:flutter/material.dart';
1
+ import "package:flutter/material.dart";
2
2
 
3
3
  final lightTheme = ThemeData(
4
4
  brightness: Brightness.light,
@@ -12,6 +12,31 @@ final lightTheme = ThemeData(
12
12
  hoverColor: const Color(0xAAF8D0DC),
13
13
  dividerColor: Colors.black,
14
14
  shadowColor: Colors.black,
15
+ bottomSheetTheme: const BottomSheetThemeData(
16
+ elevation: 10,
17
+ shadowColor: Colors.black,
18
+ backgroundColor: Colors.white,
19
+ surfaceTintColor: Colors.white,
20
+ shape: Border(
21
+ top: BorderSide(
22
+ width: 1.5,
23
+ color: Colors.black,
24
+ ),
25
+ ),
26
+ ),
27
+ dialogTheme: const DialogTheme(
28
+ elevation: 10,
29
+ // TODO: get this to inherit from top like darkTheme does
30
+ shadowColor: Colors.black,
31
+ backgroundColor: Colors.white,
32
+ surfaceTintColor: Colors.white,
33
+ shape: Border(
34
+ top: BorderSide(
35
+ width: 1.5,
36
+ color: Colors.black,
37
+ ),
38
+ ),
39
+ ),
15
40
  popupMenuTheme: const PopupMenuThemeData(
16
41
  enableFeedback: true,
17
42
  elevation: 4,
@@ -86,6 +111,24 @@ final darkTheme = ThemeData(
86
111
  hoverColor: const Color(0xAA5D4979),
87
112
  dividerColor: Colors.white,
88
113
  shadowColor: Colors.white,
114
+ bottomSheetTheme: const BottomSheetThemeData(
115
+ elevation: 1,
116
+ shape: Border(
117
+ top: BorderSide(
118
+ width: 1.5,
119
+ color: Color(0xAA5D4979),
120
+ ),
121
+ ),
122
+ ),
123
+ dialogTheme: const DialogTheme(
124
+ elevation: 1,
125
+ shape: Border(
126
+ top: BorderSide(
127
+ width: 1.5,
128
+ color: Color(0xAA5D4979),
129
+ ),
130
+ ),
131
+ ),
89
132
  popupMenuTheme: lightTheme.popupMenuTheme,
90
133
  colorScheme: const ColorScheme.dark(
91
134
  background: Color(0xFF1F1F22),
lib/utils.dart DELETED
@@ -1,42 +0,0 @@
1
- import 'package:only_bible_app/models.dart';
2
-
3
- List<Book> getBibleFromText(String text) {
4
- final List<Book> books = [];
5
- final lines = text.split("\n");
6
- for (var (index, line) in lines.indexed) {
7
- // ignore last empty line
8
- if (lines.length - 1 == index) {
9
- continue;
10
- }
11
- var book = int.parse(line.substring(0, 2));
12
- var chapter = int.parse(line.substring(3, 6));
13
- // var verseNo = line.substring(7, 10);
14
- var verseText = line.substring(11);
15
- double start = 0;
16
- double end = 0;
17
- // if (item.length > 4) {
18
- // start = double.parse(item[4]);
19
- // end = double.parse(item[5]);
20
- // }
21
- if (books.length < book) {
22
- books.add(
23
- Book(
24
- index: book - 1,
25
- name: bookNames[book-1],
26
- chapters: [],
27
- ),
28
- );
29
- }
30
- if (books[book - 1].chapters.length < chapter) {
31
- // ignore: prefer_const_constructors
32
- books[book - 1].chapters.add(Chapter(verses: []));
33
- }
34
- books[book - 1].chapters[chapter - 1].verses.add(
35
- Verse(
36
- text: verseText,
37
- audioRange: TimeRange(start: start, end: end),
38
- ),
39
- );
40
- }
41
- return books;
42
- }
lib/utils/dialog.dart CHANGED
@@ -41,8 +41,7 @@ showAlert(BuildContext context, String title, String message) {
41
41
  actions: [
42
42
  TextButton(
43
43
  onPressed: () {
44
- // Navigator.of(context).pop();
44
+ Navigator.of(context).pop();
45
- context.pop();
46
45
  },
47
46
  child: const Text("OK"),
48
47
  ),
lib/utils/side_menu_modal.dart DELETED
@@ -1,53 +0,0 @@
1
- import "package:flutter/material.dart";
2
- import "package:only_bible_app/state.dart";
3
-
4
- class SideMenuModal extends ModalRoute<void> {
5
- final Widget child;
6
-
7
- SideMenuModal({required this.child});
8
-
9
- @override
10
- Duration get transitionDuration => Duration.zero;
11
-
12
- @override
13
- Duration get reverseTransitionDuration => Duration.zero;
14
-
15
- @override
16
- bool get opaque => false;
17
-
18
- @override
19
- bool get barrierDismissible => true;
20
-
21
- @override
22
- Color get barrierColor => Colors.black.withOpacity(0.7);
23
-
24
- @override
25
- String? get barrierLabel => "Route";
26
-
27
- @override
28
- bool get maintainState => false;
29
-
30
- @override
31
- Widget buildPage(
32
- BuildContext context,
33
- Animation<double> animation,
34
- Animation<double> secondaryAnimation,
35
- ) {
36
- return Material(
37
- type: MaterialType.transparency,
38
- child: Container(
39
- color: Theme.of(context).colorScheme.background,
40
- margin: EdgeInsets.only(left: 0, right: isWide(context) ? 650 : 0),
41
- child: Container(
42
- margin: EdgeInsets.only(top: isWide(context) ? 5 : 0, left: 20, right: 20),
43
- child: isWide(context)
44
- ? child
45
- : SlideTransition(
46
- position: Tween(begin: const Offset(-1, 0), end: Offset.zero).animate(animation),
47
- child: child,
48
- ),
49
- ),
50
- ),
51
- );
52
- }
53
- }
lib/widgets/bible_selector.dart DELETED
@@ -1,57 +0,0 @@
1
- import "package:flutter/material.dart";
2
- import "package:only_bible_app/state.dart";
3
- import "package:only_bible_app/models.dart";
4
-
5
- class BibleSelector extends StatelessWidget {
6
- const BibleSelector({super.key});
7
-
8
- @override
9
- Widget build(BuildContext context) {
10
- return Column(
11
- crossAxisAlignment: CrossAxisAlignment.start,
12
- children: [
13
- Container(
14
- margin: const EdgeInsets.only(bottom: 10),
15
- child: Row(
16
- mainAxisAlignment: MainAxisAlignment.center,
17
- children: [
18
- Expanded(
19
- child: Text("Bibles", style: Theme.of(context).textTheme.headlineMedium),
20
- ),
21
- Container(
22
- margin: const EdgeInsets.only(right: 10),
23
- child: IconButton(
24
- icon: const Icon(Icons.close, size: 28),
25
- onPressed: () {
26
- Navigator.of(context).pop();
27
- },
28
- ),
29
- )
30
- ],
31
- ),
32
- ),
33
- Expanded(
34
- child: GridView.count(
35
- crossAxisCount: 2,
36
- padding: EdgeInsets.zero,
37
- shrinkWrap: true,
38
- crossAxisSpacing: 0,
39
- mainAxisSpacing: 0,
40
- childAspectRatio: 4,
41
- children: List.of(
42
- bibles.map((bible) {
43
- return Container(
44
- margin: const EdgeInsets.only(right: 16, bottom: 16),
45
- child: TextButton(
46
- child: Text(bible.name),
47
- onPressed: () => changeBible(context, bible.id),
48
- ),
49
- );
50
- }),
51
- ),
52
- ),
53
- ),
54
- ],
55
- );
56
- }
57
- }
lib/widgets/book_selector.dart DELETED
@@ -1,79 +0,0 @@
1
- import "package:flutter/material.dart";
2
- import "package:only_bible_app/state.dart";
3
- import "package:only_bible_app/widgets/chapter_selector.dart";
4
- import "package:only_bible_app/utils/side_menu_modal.dart";
5
-
6
- class BookSelector extends StatelessWidget {
7
- const BookSelector({super.key});
8
-
9
- onBookSelected(BuildContext context, int index) {
10
- Navigator.of(context).pushReplacement(
11
- SideMenuModal(
12
- child: ChapterSelector(
13
- selectedBookIndex: index,
14
- ),
15
- ),
16
- );
17
- }
18
-
19
- @override
20
- Widget build(BuildContext context) {
21
- return CustomScrollView(
22
- slivers: [
23
- SliverToBoxAdapter(
24
- child: Container(
25
- margin: const EdgeInsets.only(bottom: 10),
26
- child: Row(
27
- mainAxisAlignment: MainAxisAlignment.center,
28
- children: [
29
- Expanded(
30
- child: Text("Old Testament", style: Theme.of(context).textTheme.headlineMedium),
31
- ),
32
- IconButton(
33
- icon: const Icon(Icons.close, size: 28),
34
- onPressed: () {
35
- Navigator.of(context).pop();
36
- },
37
- ),
38
- ],
39
- ),
40
- ),
41
- ),
42
- SliverGrid.count(
43
- crossAxisCount: 6,
44
- crossAxisSpacing: 16,
45
- mainAxisSpacing: 16,
46
- childAspectRatio: 1.6,
47
- children: List.of(
48
- selectedBible.value!.getOldBooks().map((book) {
49
- return TextButton(
50
- child: Text(book.shortName()),
51
- onPressed: () => onBookSelected(context, book.index),
52
- );
53
- }),
54
- ),
55
- ),
56
- SliverToBoxAdapter(
57
- child: Container(
58
- margin: const EdgeInsets.only(top: 30, bottom: 20),
59
- child: Text("New Testament", style: Theme.of(context).textTheme.headlineMedium),
60
- ),
61
- ),
62
- SliverGrid.count(
63
- crossAxisCount: 6,
64
- crossAxisSpacing: 16,
65
- mainAxisSpacing: 16,
66
- childAspectRatio: 1.6,
67
- children: List.of(
68
- selectedBible.value!.getNewBooks().map((book) {
69
- return TextButton(
70
- child: Text(book.shortName()),
71
- onPressed: () => onBookSelected(context, book.index),
72
- );
73
- }),
74
- ),
75
- ),
76
- ],
77
- );
78
- }
79
- }
lib/widgets/chapter_selector.dart DELETED
@@ -1,52 +0,0 @@
1
- import "package:flutter/material.dart";
2
- import "package:only_bible_app/state.dart";
3
-
4
- class ChapterSelector extends StatelessWidget {
5
- final int selectedBookIndex;
6
-
7
- const ChapterSelector({super.key, required this.selectedBookIndex});
8
-
9
- onChapterSelected(BuildContext context, int index) {
10
- navigateBookChapter(context, selectedBookIndex, index, true);
11
- }
12
-
13
- @override
14
- Widget build(BuildContext context) {
15
- final book = selectedBible.value!.books[selectedBookIndex];
16
- return CustomScrollView(
17
- slivers: [
18
- SliverToBoxAdapter(
19
- child: Container(
20
- margin: const EdgeInsets.only(bottom: 10),
21
- child: Row(
22
- mainAxisAlignment: MainAxisAlignment.center,
23
- children: [
24
- Expanded(
25
- child: Text(book.name, style: Theme.of(context).textTheme.headlineMedium),
26
- ),
27
- IconButton(
28
- icon: const Icon(Icons.close, size: 28),
29
- onPressed: () {
30
- Navigator.of(context).pop();
31
- },
32
- ),
33
- ],
34
- ),
35
- ),
36
- ),
37
- SliverGrid.count(
38
- crossAxisCount: 6,
39
- crossAxisSpacing: 16,
40
- mainAxisSpacing: 16,
41
- childAspectRatio: 1.6,
42
- children: List.generate(book.chapters.length, (index) {
43
- return TextButton(
44
- child: Text("${index + 1}"),
45
- onPressed: () => onChapterSelected(context, index),
46
- );
47
- }),
48
- ),
49
- ],
50
- );
51
- }
52
- }
lib/widgets/header.dart CHANGED
@@ -1,11 +1,10 @@
1
1
  import "package:flutter/material.dart";
2
2
  import "package:flutter_reactive_value/flutter_reactive_value.dart";
3
+ import "package:only_bible_app/screens/bible_select_screen.dart";
3
- import "package:only_bible_app/widgets/book_selector.dart";
4
+ import "package:only_bible_app/screens/book_select_screen.dart";
4
5
  import "package:only_bible_app/widgets/play_button.dart";
5
- import "package:only_bible_app/utils/side_menu_modal.dart";
6
6
  import "package:only_bible_app/widgets/menu.dart";
7
7
  import "package:only_bible_app/state.dart";
8
- import "package:only_bible_app/widgets/bible_selector.dart";
9
8
 
10
9
  class Header extends StatelessWidget {
11
10
  const Header({super.key});
@@ -15,6 +14,7 @@ class Header extends StatelessWidget {
15
14
  final book = bookIndex.reactiveValue(context);
16
15
  final chapter = chapterIndex.reactiveValue(context);
17
16
  final selectedBook = selectedBible.value!.books[book];
17
+ final isDesktop = isWide(context);
18
18
  return Container(
19
19
  padding: EdgeInsets.only(
20
20
  left: 20,
@@ -43,28 +43,44 @@ class Header extends StatelessWidget {
43
43
  style: Theme.of(context).textTheme.headlineMedium,
44
44
  ),
45
45
  onPressed: () {
46
+ Navigator.of(context).push(
47
+ PageRouteBuilder(
48
+ opaque: false,
49
+ transitionDuration: Duration.zero,
50
+ reverseTransitionDuration: Duration.zero,
46
- Navigator.of(context).push(SideMenuModal(child: const BookSelector()));
51
+ pageBuilder: (context, _, __) => const BookSelectScreen(),
52
+ ),
53
+ );
47
54
  },
48
55
  ),
49
56
  Row(
50
57
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
51
58
  children: [
59
+ if (isDesktop)
52
- Container(
60
+ Container(
53
- margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
61
+ margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
54
- child: TextButton(
62
+ child: TextButton(
55
- style: TextButton.styleFrom(
63
+ style: TextButton.styleFrom(
56
- padding: const EdgeInsets.symmetric(horizontal: 20),
64
+ padding: const EdgeInsets.symmetric(horizontal: 20),
65
+ ),
66
+ child: Text(selectedBible.reactiveValue(context)!.name),
67
+ onPressed: () {
68
+ Navigator.of(context).push(
69
+ PageRouteBuilder(
70
+ opaque: false,
71
+ transitionDuration: Duration.zero,
72
+ reverseTransitionDuration: Duration.zero,
73
+ pageBuilder: (context, _, __) => const BibleSelectScreen(),
74
+ ),
75
+ );
76
+ },
57
77
  ),
58
- child: Text(selectedBible.reactiveValue(context)!.name),
59
- onPressed: () {
60
- Navigator.of(context).push(SideMenuModal(child: const BibleSelector()));
61
- },
62
78
  ),
79
+ if (isDesktop)
80
+ Container(
81
+ margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
82
+ child: const PlayButton(),
63
- ),
83
+ ),
64
- Container(
65
- margin: EdgeInsets.only(right: isWide(context) ? 10 : 8),
66
- child: const PlayButton(),
67
- ),
68
84
  const Menu(),
69
85
  ],
70
86
  ),
lib/widgets/play_button.dart CHANGED
@@ -7,11 +7,10 @@ class PlayButton extends StatelessWidget {
7
7
 
8
8
  @override
9
9
  Widget build(BuildContext context) {
10
+ final icon = isPlaying.reactiveValue(context) ? Icons.pause_circle_filled : Icons.play_circle_fill;
10
- final icon = isPlaying.reactiveValue(context)
11
+ final size = isWide(context) ? 28.0 : 42.0;
11
- ? Icons.pause_circle_filled
12
- : Icons.play_circle_fill;
13
12
  return IconButton(
14
- icon: Icon(icon, size: 28),
13
+ icon: Icon(icon, size: size),
15
14
  onPressed: () {
16
15
  onPlay(context);
17
16
  },
lib/widgets/scaffold_menu.dart ADDED
@@ -0,0 +1,26 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/state.dart";
3
+
4
+ class ScaffoldMenu extends StatelessWidget {
5
+ final Widget child;
6
+
7
+ const ScaffoldMenu({super.key, required this.child});
8
+
9
+ @override
10
+ Widget build(BuildContext context) {
11
+ return Scaffold(
12
+ backgroundColor: Colors.transparent,
13
+ body: SafeArea(
14
+ child: Container(
15
+ color: Colors.black.withOpacity(0.7),
16
+ margin: EdgeInsets.only(left: isWide(context) ? 250 : 0),
17
+ child: Container(
18
+ color: Theme.of(context).colorScheme.background,
19
+ margin: EdgeInsets.only(right: isWide(context) ? 650 : 0),
20
+ child: child,
21
+ ),
22
+ ),
23
+ ),
24
+ );
25
+ }
26
+ }
lib/widgets/sliver_heading.dart ADDED
@@ -0,0 +1,40 @@
1
+ import "package:flutter/material.dart";
2
+
3
+ class SliverHeading extends StatelessWidget {
4
+ final String title;
5
+ final bool showClose;
6
+ final double top;
7
+ final double bottom;
8
+
9
+ const SliverHeading({
10
+ super.key,
11
+ required this.title,
12
+ this.showClose = false,
13
+ this.top = 0,
14
+ this.bottom = 10,
15
+ });
16
+
17
+ @override
18
+ Widget build(BuildContext context) {
19
+ return SliverToBoxAdapter(
20
+ child: Container(
21
+ margin: EdgeInsets.only(top: top, bottom: bottom, left: 20, right: 10),
22
+ child: Row(
23
+ mainAxisAlignment: MainAxisAlignment.center,
24
+ children: [
25
+ Expanded(
26
+ child: Text(title, style: Theme.of(context).textTheme.headlineMedium),
27
+ ),
28
+ if (showClose)
29
+ IconButton(
30
+ icon: const Icon(Icons.close, size: 28),
31
+ onPressed: () {
32
+ Navigator.of(context).pop();
33
+ },
34
+ )
35
+ ],
36
+ ),
37
+ ),
38
+ );
39
+ }
40
+ }
lib/widgets/sliver_tile_grid.dart ADDED
@@ -0,0 +1,33 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:only_bible_app/state.dart";
3
+
4
+ enum ListType { small, large }
5
+
6
+ class SliverTileGrid extends StatelessWidget {
7
+ final ListType listType;
8
+ final List<Widget> children;
9
+
10
+ const SliverTileGrid({super.key, this.listType = ListType.small, required this.children});
11
+
12
+ @override
13
+ Widget build(BuildContext context) {
14
+ return SliverPadding(
15
+ padding: const EdgeInsets.symmetric(horizontal: 20),
16
+ sliver: SliverGrid.count(
17
+ crossAxisCount: listType == ListType.large
18
+ ? 2
19
+ : isWide(context)
20
+ ? 6
21
+ : 5,
22
+ crossAxisSpacing: 16,
23
+ mainAxisSpacing: 16,
24
+ childAspectRatio: listType == ListType.large
25
+ ? 4
26
+ : isWide(context)
27
+ ? 1.6
28
+ : 1.5,
29
+ children: children,
30
+ ),
31
+ );
32
+ }
33
+ }
lib/widgets/verse_list.dart ADDED
@@ -0,0 +1,32 @@
1
+ import "package:flutter/material.dart";
2
+ import "package:flutter_reactive_value/flutter_reactive_value.dart";
3
+ import "package:only_bible_app/widgets/verse_view.dart";
4
+ import "package:only_bible_app/state.dart";
5
+
6
+ class VerseList extends StatelessWidget {
7
+ const VerseList({super.key});
8
+
9
+ @override
10
+ Widget build(BuildContext context) {
11
+ final selectedBook = selectedBible.reactiveValue(context)!.books[bookIndex.value];
12
+ final verses = selectedBook.chapters[chapterIndex.value].verses;
13
+ return SelectionArea(
14
+ child: ListView.builder(
15
+ shrinkWrap: false,
16
+ padding: const EdgeInsets.only(
17
+ left: 20,
18
+ right: 20,
19
+ bottom: 20,
20
+ ),
21
+ itemCount: verses.length,
22
+ itemBuilder: (BuildContext context, int index) {
23
+ final v = verses[index];
24
+ return Container(
25
+ margin: const EdgeInsets.symmetric(vertical: 6),
26
+ child: VerseText(index: index, text: v.text),
27
+ );
28
+ },
29
+ ),
30
+ );
31
+ }
32
+ }
scripts/generate_audio.dart CHANGED
@@ -4,8 +4,8 @@ import 'package:flutter_azure_tts/flutter_azure_tts.dart';
4
4
  Future<void> convertText(Voice v, String fileName, String text) async {
5
5
  final ttsResponse = await AzureTts.getTts(TtsParams(
6
6
  voice: v,
7
- audioFormat: AudioOutputFormat.audio48khz96kBitrateMonoMp3,
7
+ audioFormat: AudioOutputFormat.audio24khz48kBitrateMonoMp3,
8
- rate: 0.80,
8
+ rate: 0.90,
9
9
  text: text,
10
10
  ));
11
11
  await File(fileName).writeAsBytes(ttsResponse.audio.buffer.asUint8List(), flush: true);
@@ -18,14 +18,14 @@ void main() async {
18
18
  name: "",
19
19
  displayName: "",
20
20
  localName: "",
21
- shortName: "kn-IN-GaganNeural",
21
+ shortName: "ne-NP-SagarNeural",
22
22
  gender: "Male",
23
- locale: "kn-IN",
23
+ locale: "ne-NP",
24
24
  sampleRateHertz: AudioOutputFormat.audio24khz48kBitrateMonoMp3,
25
25
  voiceType: "",
26
26
  status: "",
27
27
  );
28
- final bibleTxt = await File("./assets/bibles/Kannada.txt").readAsString();
28
+ final bibleTxt = await File("./assets/bibles/Nepali.txt").readAsString();
29
29
  final lines = bibleTxt.split("\n");
30
30
  for (final line in lines) {
31
31
  if (line.isEmpty) {
@@ -35,13 +35,16 @@ void main() async {
35
35
  final chapter = line.substring(3, 6);
36
36
  final verseNo = line.substring(7, 10);
37
37
  final verseText = line.substring(11);
38
- print("${book}-${chapter}-${verseNo}.mp3");
38
+ print("$book-$chapter-$verseNo.mp3");
39
- final outputFilename = "./scripts/audio/${book}-${chapter}-${verseNo}.mp3";
39
+ final outputFilename = "./scripts/audio/Nepali/$book-$chapter-$verseNo.mp3";
40
40
  final outFile = File(outputFilename);
41
41
  if (outFile.existsSync() && outFile.lengthSync() > 100) {
42
42
  continue;
43
43
  }
44
44
  await convertText(voice, outputFilename, verseText);
45
+ if (chapter == "002") {
46
+ break;
47
+ }
45
48
  }
46
49
  print("finished");
47
50
  }