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

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_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:

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:

  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:

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:

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:

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:

  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