bible-tracker/lib/screens/book_screen.dart
2026-04-20 14:39:49 -04:00

139 lines
4.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../data/bible.dart';
import '../models/tracker.dart';
import '../services/progress.dart';
import '../services/tracker_service.dart';
import '../widgets/progress_bar.dart';
import '../widgets/theme.dart';
class BookScreen extends ConsumerWidget {
const BookScreen({
super.key,
required this.trackerId,
required this.bookIndex,
});
final String trackerId;
final int bookIndex;
@override
Widget build(BuildContext context, WidgetRef ref) {
final Tracker? tracker = ref.watch(trackerByIdProvider(trackerId));
if (tracker == null) {
return Scaffold(
appBar: AppBar(),
body: const Center(child: Text('Tracker not found')),
);
}
final Book book = Bible.books[bookIndex];
final Color accent = Color(tracker.colorValue);
final TrackerProgress progress = TrackerProgress(tracker);
final int chaptersRead = progress.chaptersReadIn(book);
final bool allRead = chaptersRead == book.chapterCount;
return Scaffold(
appBar: AppBar(
title: Text(book.name),
actions: <Widget>[
TextButton(
onPressed: () => ref
.read(trackerServiceProvider.notifier)
.setBookRead(trackerId, bookIndex, read: !allRead),
child: Text(allRead ? 'Clear book' : 'Mark all'),
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(AppTokens.spaceL),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'$chaptersRead of ${book.chapterCount} chapters',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: AppTokens.spaceM),
ProgressBar(
value: progress.fractionOf(book),
color: accent,
height: 8,
),
],
),
),
const Divider(),
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(AppTokens.spaceL),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
mainAxisSpacing: AppTokens.spaceM,
crossAxisSpacing: AppTokens.spaceM,
childAspectRatio: 1,
),
itemCount: book.chapterCount,
itemBuilder: (BuildContext context, int index) {
final int chapter = index + 1;
final bool read = tracker.readChapters.contains(
Bible.chapterKey(bookIndex, chapter),
);
return _ChapterCell(
chapter: chapter,
read: read,
accent: accent,
onTap: () => ref
.read(trackerServiceProvider.notifier)
.toggleChapter(trackerId, bookIndex, chapter),
);
},
),
),
],
),
);
}
}
class _ChapterCell extends StatelessWidget {
const _ChapterCell({
required this.chapter,
required this.read,
required this.accent,
required this.onTap,
});
final int chapter;
final bool read;
final Color accent;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Material(
color: read ? accent : Theme.of(context).colorScheme.surfaceContainerHigh,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppTokens.radiusM),
),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(AppTokens.radiusM),
child: Center(
child: Text(
'$chapter',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: read
? Colors.white
: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
),
),
),
);
}
}