9.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is a Flutter mobile application for managing a personal book library. The app features book cataloging, categorization, reading status tracking, and cover scanning capabilities (via camera + Gemini AI).
Tech Stack:
- Flutter SDK ^3.10.8
- State Management: flutter_bloc ^9.1.0
- UI: Material 3 with Google Fonts (Inter)
- AI: google_generative_ai ^0.4.6 (for book cover analysis)
- Camera: camera ^0.11.1
Development Commands
Running the App
# Run on connected device/simulator
flutter run
# Run on specific device
flutter run -d <device-id>
# Run in release mode
flutter run --release
# List available devices
flutter devices
Building
# Build APK for Android
flutter build apk
# Build iOS
flutter build ios
# Build for web (not currently configured)
flutter build web
Code Quality
# Install/update dependencies
flutter pub get
# Check for outdated packages
flutter pub outdated
# Run static analysis
flutter analyze
# Format all Dart files
flutter format lib/
# Run tests
flutter test
Using Dart MCP Tools
When the Dart MCP server is available, prefer using these tools instead of bash commands:
mcp__dart__analyze_filesinstead offlutter analyzemcp__dart__dart_formatinstead offlutter formatmcp__dart__run_testsinstead offlutter testmcp__dart__list_devicesto see available devicesmcp__dart__launch_appto run the app with DTD integration
Architecture
State Management (BLoC Pattern)
The app uses flutter_bloc for state management with two main BLoCs:
1. BookBloc (lib/bloc/book_bloc.dart)
Manages the book collection state and operations:
- State:
BookStatecontainingList<Book> - Events:
AddBook(book)- Add new book to libraryUpdateBook(book)- Update existing bookDeleteBook(id)- Remove book from libraryToggleFavorite(id)- Toggle favorite status
- Initial State: Loads from
initialBooksinconstants.dart - Note: Currently uses in-memory storage; no persistence layer
2. NavigationBloc (lib/bloc/navigation_bloc.dart)
Manages app navigation and screen state:
- State:
NavigationStatewith:screen(AppScreen enum) - Current screenselectedBook- Book being viewed/editedprefilledData- Data for pre-populating forms
- Event:
NavigateTo(screen, {selectedBook, prefilledData}) - Pattern: Declarative navigation where UI rebuilds based on state
- Important:
prefilledDatais used when scanning covers to prefill book form, whileselectedBookis used for editing existing books
Data Models (lib/models/models.dart)
Uses Dart 3 record types for immutability:
typedef Book = ({
String id,
String title,
String author,
String genre,
String annotation,
String? coverUrl,
int? pages,
String? language,
int? publishedYear,
double? rating,
String status, // 'reading', 'done', 'want_to_read'
double? progress, // 0-100 for reading progress
bool isFavorite,
});
Important: Records are immutable. To update a book, create a new record with updated fields using record syntax:
final updatedBook = (
id: book.id,
title: newTitle,
// ... copy all other fields
);
Navigation Flow
The app uses a custom navigation system via NavigationBloc:
- Library Screen (default) → Shows all books in grid/category view
- Categories Screen → Browse books by predefined categories
- Book Details → View/edit single book (triggered by tapping book card)
- Add/Edit Book → Form for adding new books or editing existing
- Scanner Screen → Camera interface for scanning book covers
- Wishlist/Settings → Placeholder screens
Navigation Pattern:
context.read<NavigationBloc>().add(
NavigateTo(AppScreen.details, selectedBook: book)
);
The main shell (_AppShell in main.dart) rebuilds based on NavigationState.screen.
Theme System (Material 3)
Critical: This app uses a centralized theme system. Never hardcode colors, text styles, or spacing.
Theme Files:
lib/theme/app_colors.dart- Semantic color constants (cyan-based palette)lib/theme/app_spacing.dart- Spacing scale (8px base) and border radiuslib/theme/app_theme.dart- Material 3 ThemeData with component themes
Usage Pattern:
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
// Use semantic colors
Container(color: colorScheme.primary)
// Use text styles
Text('Title', style: textTheme.displayMedium)
// Use spacing constants
Padding(padding: EdgeInsets.all(AppSpacing.md))
// Use shadows
BoxDecoration(boxShadow: AppTheme.shadowMd)
Color Scheme:
- Primary: #0891B2 (Cyan-600)
- Success/CTA: #22C55E (Green-500)
- Background: #ECFEFF (Cyan-50)
- Surface: #FFFFFF
Typography: Inter font family loaded via Google Fonts with weights 300-700.
Screen Structure
All main screens follow this pattern:
- Wrap in
SafeAreafor notch/status bar handling - Use
BlocBuilderto listen to relevant BLoC state - Access theme via
Theme.of(context) - Use
AppSpacing.*constants for all padding/margins - Use theme colors and text styles exclusively
Example:
class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return SafeArea(
child: BlocBuilder<BookBloc, BookState>(
builder: (context, state) {
return Padding(
padding: EdgeInsets.all(AppSpacing.lg),
child: Text(
'Hello',
style: textTheme.headlineMedium,
),
);
},
),
);
}
}
Widget Conventions
BookCard (lib/widgets/book_card.dart):
- Displays book cover with metadata overlay
- Includes Hero animation with tag
'book-cover-${book.id}' - Shows shimmer loading while image loads
- Has hover effect on desktop/web (1.02 scale)
- Displays favorite badge, status badge, or progress bar based on book state
Hero Animations: When navigating from library to book details, book covers animate smoothly:
Hero(
tag: 'book-cover-${book.id}',
child: Image.network(book.coverUrl),
)
Both BookCard and BookDetailsScreen must use matching tags.
Gemini AI Integration
The GeminiService (lib/services/gemini_service.dart) is a placeholder for future AI-powered book cover scanning:
- Takes base64-encoded image from camera
- Will analyze cover and extract metadata (title, author, etc.)
- Returns
Book?record with prefilled data - Currently returns
null- implementation pending
Intended Flow:
- User opens Scanner Screen
- Takes photo of book cover
- Image sent to
GeminiService.analyzeBookCover() - Extracted data passed to Add Book screen via
NavigationBlocwithprefilledData
Important Patterns
When Adding New Features
- New Book Fields: Update the
Booktypedef inmodels.dartand all places that construct book records - New Screens: Add to
AppScreenenum, handle in_AppShellswitch statement - Theme Changes: Only modify theme files, never inline styles
- Navigation: Always use
NavigationBloc, neverNavigator.push()
Code Style Requirements
- Immutability: Use records for data models, never mutable classes
- Theme Compliance: Zero hardcoded colors/styles/spacing
- BLoC Pattern: UI is always a pure function of state
- Const Constructors: Use
constfor all stateless widgets and values - Reduced Motion: Check
MediaQuery.of(context).disableAnimationsfor animations
Testing Gotchas
- Books are stored in-memory only; restarting app resets to
initialBooks - Camera requires physical device or simulator with camera support
- Gemini API requires valid API key (not implemented yet)
- Hero animations require matching tags between screens
Project-Specific Notes
Why Records Instead of Classes?
This codebase uses Dart 3 record types for immutability and simplicity. When updating books, create new records rather than mutating fields. This makes BLoC state updates predictable and prevents accidental mutations.
Navigation Without Navigator
The app doesn't use Flutter's built-in Navigator. Instead, NavigationBloc tracks the current screen, and _AppShell rebuilds the entire UI tree based on state. This gives centralized control over navigation state but means:
- No native back button handling (would need to emit
NavigateToevents) - No deep linking support (yet)
- All screens must be handled in the main switch statement
Initial Data
Books are initialized from initialBooks constant in lib/constants/constants.dart. Categories are defined in the same file. To add sample data, modify these constants.
Future Enhancements
Based on the codebase structure, likely next steps:
- Implement persistence (SharedPreferences, SQLite, or Firebase)
- Complete Gemini AI integration for cover scanning
- Add native back button handling
- Implement book search/filtering
- Add reading statistics/charts
- Support for book series and collections