Files
bookshelf/books_flutter/CLAUDE.md
2026-02-08 12:05:05 +06:00

301 lines
9.2 KiB
Markdown

# 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
```bash
# 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
```bash
# Build APK for Android
flutter build apk
# Build iOS
flutter build ios
# Build for web (not currently configured)
flutter build web
```
### Code Quality
```bash
# 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_files` instead of `flutter analyze`
- `mcp__dart__dart_format` instead of `flutter format`
- `mcp__dart__run_tests` instead of `flutter test`
- `mcp__dart__list_devices` to see available devices
- `mcp__dart__launch_app` to 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**: `BookState` containing `List<Book>`
- **Events**:
- `AddBook(book)` - Add new book to library
- `UpdateBook(book)` - Update existing book
- `DeleteBook(id)` - Remove book from library
- `ToggleFavorite(id)` - Toggle favorite status
- **Initial State**: Loads from `initialBooks` in `constants.dart`
- **Note**: Currently uses in-memory storage; no persistence layer
#### 2. NavigationBloc (`lib/bloc/navigation_bloc.dart`)
Manages app navigation and screen state:
- **State**: `NavigationState` with:
- `screen` (AppScreen enum) - Current screen
- `selectedBook` - Book being viewed/edited
- `prefilledData` - Data for pre-populating forms
- **Event**: `NavigateTo(screen, {selectedBook, prefilledData})`
- **Pattern**: Declarative navigation where UI rebuilds based on state
- **Important**: `prefilledData` is used when scanning covers to prefill book form, while `selectedBook` is used for editing existing books
### Data Models (`lib/models/models.dart`)
Uses Dart 3 **record types** for immutability:
```dart
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:
```dart
final updatedBook = (
id: book.id,
title: newTitle,
// ... copy all other fields
);
```
### Navigation Flow
The app uses a custom navigation system via `NavigationBloc`:
1. **Library Screen** (default) → Shows all books in grid/category view
2. **Categories Screen** → Browse books by predefined categories
3. **Book Details** → View/edit single book (triggered by tapping book card)
4. **Add/Edit Book** → Form for adding new books or editing existing
5. **Scanner Screen** → Camera interface for scanning book covers
6. **Wishlist/Settings** → Placeholder screens
**Navigation Pattern:**
```dart
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 radius
- `lib/theme/app_theme.dart` - Material 3 ThemeData with component themes
**Usage Pattern:**
```dart
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:
1. Wrap in `SafeArea` for notch/status bar handling
2. Use `BlocBuilder` to listen to relevant BLoC state
3. Access theme via `Theme.of(context)`
4. Use `AppSpacing.*` constants for all padding/margins
5. Use theme colors and text styles exclusively
**Example:**
```dart
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:
```dart
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:**
1. User opens Scanner Screen
2. Takes photo of book cover
3. Image sent to `GeminiService.analyzeBookCover()`
4. Extracted data passed to Add Book screen via `NavigationBloc` with `prefilledData`
## Important Patterns
### When Adding New Features
1. **New Book Fields**: Update the `Book` typedef in `models.dart` and all places that construct book records
2. **New Screens**: Add to `AppScreen` enum, handle in `_AppShell` switch statement
3. **Theme Changes**: Only modify theme files, never inline styles
4. **Navigation**: Always use `NavigationBloc`, never `Navigator.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 `const` for all stateless widgets and values
- **Reduced Motion**: Check `MediaQuery.of(context).disableAnimations` for 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 `NavigateTo` events)
- 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